I have a Spring boot project with 3 entities, UserEntity which is a standalone and Person which is inherited by lawyer.
I set the automatic indexing strategy to sync, When I insert a new user into the database, the new user is picked immediately, but a new lawyer or person are indexed but the don't appear in the result until I restart the mass indexer.
UserEntity:
@Entity
@Accessors(chain = true)
@Getter
@Setter
@Indexed
@SyncAnnotation(convertor = UserConvertor.class, repoClass = UserDetailServiceImplementation.class)
public class UserEntity implements UserDetails {
@Id
@Basic
@Column(name = "id", columnDefinition = "uniqueidentifier")
@Type(type = "uuid-char")
private UUID id;
@Column(name = "user_name", length = 20)
@Basic
private String username;
@Basic
@Column(name = "email")
@Email
private String email;
@Basic
@Column(name = "full_name", length = 50, nullable = false, columnDefinition = "nvarchar(50)")
@NotNull
@FullTextField(termVector = TermVector.WITH_POSITIONS_OFFSETS)
private String fullName;
PersonEntity:
@Entity
@Accessors(chain = true)
@Getter
@Setter
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "person_type", discriminatorType = DiscriminatorType.INTEGER)
@DiscriminatorValue("1")
@Indexed
@SyncAnnotation(convertor = ClientConvertor.class, repoClass = PersonRepository.class)
public class PersonEntity implements Serializable {
public PersonEntity(){
this.personType=1;
}
@Id
@Basic
@Column(name = "id", columnDefinition = "uniqueidentifier")
@Type(type = "uuid-char")
private UUID id;
@Basic
@Column(name = "first_name", nullable = false, length = 50, columnDefinition = "nvarchar(50)")
private String firstName;
@Basic
@Column(name = "last_name", nullable = false, length = 50, columnDefinition = "nvarchar(50)")
private String lastName;
@Basic
@Column(name = "father_name", length = 50, columnDefinition = "nvarchar(50)")
private String fatherName;
@Basic
@FullTextField(termVector = TermVector.YES)
@Column(name = "full_name", columnDefinition = "as concat(first_name,' ',isnull(father_name,''),' ',last_name)", insertable = false, updatable = false)
private String fullName;
@Basic
@Column(name = "person_type", insertable = false, updatable = false)
@GenericField
private Integer personType;
And a LawyerEntity that inherits PersonEntity:
@Entity
@Accessors(chain = true)
@Getter
@Setter
@DiscriminatorValue("2")
@Indexed
@SyncAnnotation(convertor = ClientConvertor.class, repoClass = LawyerRepository.class)
public class LawyerEntity extends PersonEntity {
public LawyerEntity(){
this.setPersonType(2);
}
@Basic
@Column(name = "bar_id")
@GenericField
private Integer barId;
@Basic
@Column(name = "bar_card_number")
private Long barCardNumber;
@Basic
@Column(name = "bar_regisration_date")
private LocalDate barRegistrationDate;
@ManyToOne(targetEntity = BarEntity.class)
@JoinColumn(foreignKey = @ForeignKey(name = "fk_lawyer_bar"),
name = "bar_id", referencedColumnName = "id", insertable = false, updatable = false)
@JsonIgnore
private BarEntity bar;
}
When using Sync
hibernate search automatic indexing strategy, the UserEntity index updates and includes the newly inserted entities in the index , the TRACE output:
2022-12-22 10:16:06.112 TRACE 68869 --- [nio-8080-exec-4] i.AfterCommitIndexingPlanSynchronization : Processing Transaction's beforeCompletion() phase for org.hibernate.engine.transaction.internal.TransactionImpl@5193eb5f.
2022-12-22 10:16:06.119 TRACE 68869 --- [nio-8080-exec-4] i.AfterCommitIndexingPlanSynchronization : Processing Transaction's afterCompletion() phase for org.hibernate.engine.transaction.internal.TransactionImpl@5193eb5f. Executing indexing plan.
2022-12-22 10:16:06.119 TRACE 68869 --- [nio-8080-exec-4] o.h.s.e.b.o.spi.SingletonTask : Scheduling task 'Lucene indexing orchestrator for index 'User' - 9'.
2022-12-22 10:16:06.120 TRACE 68869 --- [rker thread - 2] o.h.s.e.b.o.spi.SingletonTask : Running task 'Lucene indexing orchestrator for index 'User' - 9'
2022-12-22 10:16:06.120 TRACE 68869 --- [rker thread - 2] o.h.s.e.b.o.spi.BatchingExecutor : Processing 1 works in executor 'Lucene indexing orchestrator for index 'User' - 9'
2022-12-22 10:16:06.132 TRACE 68869 --- [rker thread - 2] o.h.s.e.b.o.spi.BatchingExecutor : Processed 1 works in executor 'Lucene indexing orchestrator for index 'User' - 9'
2022-12-22 10:16:06.132 TRACE 68869 --- [rker thread - 2] o.h.s.e.b.o.spi.SingletonTask : Completed task 'Lucene indexing orchestrator for index 'User' - 9'
However, when entering a new person or a lawyer, the index doesn't reflect the changes, not even after awhile, I need to restart the massindexer for it work, it has a similar output to the previous log, but it doesn't reflect the changes on the index until I restart the mass indexer
2022-12-22 10:14:38.086 TRACE 68869 --- [nio-8080-exec-6] i.AfterCommitIndexingPlanSynchronization : Processing Transaction's beforeCompletion() phase for org.hibernate.engine.transaction.internal.TransactionImpl@6b9d9f5e.
2022-12-22 10:14:38.089 TRACE 68869 --- [nio-8080-exec-6] i.AfterCommitIndexingPlanSynchronization : Processing Transaction's afterCompletion() phase for org.hibernate.engine.transaction.internal.TransactionImpl@6b9d9f5e. Executing indexing plan.
2022-12-22 10:14:38.091 TRACE 68869 --- [nio-8080-exec-6] o.h.s.e.b.o.spi.SingletonTask : Scheduling task 'Lucene indexing orchestrator for index 'Person' - 8'.
2022-12-22 10:14:38.091 TRACE 68869 --- [rker thread - 3] o.h.s.e.b.o.spi.SingletonTask : Running task 'Lucene indexing orchestrator for index 'Person' - 8'
2022-12-22 10:14:38.092 TRACE 68869 --- [rker thread - 3] o.h.s.e.b.o.spi.BatchingExecutor : Processing 1 works in executor 'Lucene indexing orchestrator for index 'Person' - 8'
2022-12-22 10:14:38.137 TRACE 68869 --- [rker thread - 3] o.h.s.e.b.o.spi.BatchingExecutor : Processed 1 works in executor 'Lucene indexing orchestrator for index 'Person' - 8'
2022-12-22 10:14:38.138 TRACE 68869 --- [rker thread - 3] o.h.s.e.b.o.spi.SingletonTask : Completed task 'Lucene indexing orchestrator for index 'Person' - 8'
How can I make it detect show the change in the index without restart mass index ? I also tried calling hibernate search indexing plan but to no success
I am using Hibernate search 6.1.6.Final with lucene backend and spring boot 2.7.5
As per request: The code used to search for UserEntity (user can belong to bar):
public List<UserEntity> findInAnyRole(String name, Integer barId, UUID[] role) {
var session = sessionProvider.getSearchSession();
var search = session.search(UserEntity.class);
var res = search.where(f -> f.bool(
b -> {
b.must(f.match().field("fullName").matching(name).fuzzy(2));
if (role != null && role.length > 0) {
b.must(f.bool(b2 -> {
for (var roleValue : role)
b2.should(f.match().field("roles.id").matching(roleValue));
}));
}
if (barId != null)
b.must(f.match().field("barId").matching(barId));
}
));
return res.fetchHits(10);
}
As for PersonEntity:
public List<PersonEntity> findSimilar(@NotNull String name, String[] ids) {
var session = sessionProvider.getSearchSession();
var search = session.search(PersonEntity.class);
var q=search.where(f -> f.bool().must(f.match().field("fullName").matching(name).fuzzy(2))
.must(f.match().field("personType").matching(1))).sort(searchSortFactory -> searchSortFactory.score().desc());
log.info(q.toQuery().queryString());
return q.fetchHits(10);
}
and LawyerEntity:
public List<LawyerEntity> findSimilar(@NotNull String name, Integer barId) {
var session = sessionProvider.getSearchSession();
var search = session.search(LawyerEntity.class);
var res = search.where(f -> f.match().field("fullName").matching(name).fuzzy(2));
if (barId != null)
res = search.where(f -> f.bool().must(f.match().field("fullName").matching(name).fuzzy(2))
.must(f.match().field("barId").matching(barId)));
var list = res.fetchHits(10);
return list;
}
CodePudding user response:
I suspect your problem is here:
@Column(name = "full_name", columnDefinition = "as concat(first_name,' ',isnull(father_name,''),' ',last_name)", insertable = false, updatable = false)
private String fullName;
As you're defining the full name on the database side, it will only be populated correctly when it's loaded from the database. The first time you create your entity, or anytime you change the first name or last name on your Java object, the fullName
property in your Java object will not have the correct value, until it's loaded back from the database.
I think that when you create your entity, fullName
is null
, so Hibernate Search is indexing your entities with a fullName
index field set to null
, which explains that your queries (with predicates on the fullName
field) do not match anything.
When you use the mass indexer, on the other hand, entities are loaded from the database and fullName
is populated correctly from the database.
As to solutions, either:
- Always update
fullName
manually whenever you updatefirstName
orlastName
. That might be inconvenient. - OR, if you don't need to use
fullName
in SQL queries, do not persistfullName
in the database, do not add afullName
property to your entity, and just declare agetFullName()
getter annotated with@javax.persistence.Transient
that does the concatenation in Java:
See this section of the documentation for@Transient @FullTextField(termVector = TermVector.YES) @IndexingDependency(derivedFrom = { @ObjectPath(@PropertyValue(propertyName = "firstName")), @ObjectPath(@PropertyValue(propertyName = "fatherName")), @ObjectPath(@PropertyValue(propertyName = "lastName")) }) public String getFullName() { return firstName ( fatherName == null ? "" : " " fatherName ) " " lastName; }
@IndexingDependency
.