Kyne H Kyne H - 5 months ago 20
Java Question

index not updating after external entity changes

I'm currently working on a project to persist data with JPA 2.1 and to search entities using hibernate search 4.5.0.final.

After mapping classes and indexing, the searching works fine.

However, when I changed the value description of classB from "someStr" to "anotherStr". The database was updated accordingly, but when I checked the index using Luke, classA.classB.description in the index wasn't updated, and the data cannot be searchable by keyword "anotherStr", but can be searchable by keyword "someStr".

After I reindex the whole database, it's updated finally.

According to Hibernate search website,


The short answer is that indexing is automatic: Hibernate Search will transparently index every entity persisted, updated or removed through Hibernate ORM. Its mission is to keep the index and your database in sync, allowing you to forget about this problem.


But it's not working in my case. I'm not sure if I missed some details or I need to handle it myself for this kind of issues.

I also tried to add annotation @Indexed on classB as suggested by this one, but it's still not solving my problem.

As far as I know, the solution would be to reindex the database periodically. But reindexing would disable the search functionality and that's not an option in most of the cases.

Could anyone give some suggestions? Thanks.

I have a class which embedded some other classes by using @IndexedEmbedded annotation. Here is a simplified version of my class mapping.

Class A
@Entity(name = "classA")
@Indexed
public class classA extends Model {
private int id;
private String name;
private ClassB place;
...
some constructors
...
@Id
@GeneratedValue
@DocumentId
public int getId() {
return id;
}

@Column(name = "name")
@Field(analyze = Analyze.NO, store = Store.YES) // only used for sorting
public String getName() {
return name;
}

@IndexedEmbedded
@ManyToOne
@JoinColumn(name = "place_id")
public ClassB getPlace() {
return place;
}
...
}





Class B
@Entity(name = "classB")
public class classB extends Model {
private int id;
private String description;
...
some constructors
...
@Id
@GeneratedValue
public int getId() {
return id;
}

@Fields({
@Field,
@Field(name = "description_sort", analyze = Analyze.NO, store = Store.YES)
})
@ContainedIn
@Column(name = "description")
public String getDescription() {
return description;
}
...
}


And the indexing methods is as follows:

fullTextEntityManager.createIndexer()
.purgeAllOnStart(true)
.optimizeAfterPurge(true)
.optimizeOnFinish(true)
.batchSizeToLoadObjects(25)
.threadsToLoadObjects(8)
.startAndWait();

Answer

You placed ContainedIn annotation incorrectly. According the Hibernate Search documentation:

Be careful. Because the data is denormalized in the Lucene index when using the @IndexedEmbedded technique, Hibernate Search needs to be aware of any change in the Place object and any change in the Address object to keep the index up to date. To make sure the Place Lucene document is updated when it's Address changes, you need to mark the other side of the bidirectional relationship with @ContainedIn.

In your example, you need to:

  1. Make the relationship between classes bidirectional
  2. Mark the relationship in ClassB as ContainedIn

In your case:

ClassB {

    private Set<ClassA> linkedObjects;

    .... 

    @OneToMany(mappedBy="place")
    @ContainedIn
    public Set<ClassA> getLinkedObjects() {
        return linkedObjects;
    }

    ....
}
Comments