I'm using spring boot v3.1.2
. I'm trying to implement the search feature using Hibernate search with Lucene. For the configuration, I followed the following documentation
I'm struggling bcz manything have been changed in the latest versions of Hibernate Search ! the searchByQuery returns an empty list even though I expect data to match the query
@RequiredArgsConstructor
public class ProductRepositoryImpl implements ProductRepository {
private final EntityManager entityManager;
@Override
public List<Product> searchByQuery(String query, Pageable pageable) throws RuntimeException {
SearchSession searchSession = Search.session( entityManager );
SearchResult<Product> result = searchSession.search( Product.class )
.where( f -> f.match()
.fields( "title", "description" )
.matching( query ) )
.fetch( 20 );
long totalHitCount = result.total().hitCount();
List<Product> hits = result.hits();
return hits;
}
}
I suspect that the data hasn't been indexed yet ! any insights on what I might have overlooked ?
// Product.java
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
@Table(name = "products")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@Indexed
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@FullTextField
private String title;
private String code;
@FullTextField
private String description;
...
Still doesn't able to index correctly the data, I've created a basic example here https://github.com/smaillns/demo-hibernate-search
Get /book/search?query=prod
should return items !
To index the pre-existing data on application bootstrap, we can add this bean
import com.example.demo.model.Book;
import jakarta.persistence.EntityManager;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.massindexing.MassIndexer;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class EntityIndexer implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private final EntityManager entityManager;
public EntityIndexer(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
@Transactional
@Async
public void onApplicationEvent(ContextRefreshedEvent event) {
SearchSession searchSession = Search.session(entityManager);
MassIndexer indexer = searchSession.massIndexer(Book.class).threadsToLoadObjects(7);
try {
indexer.startAndWait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
we should then get something like that in the logs
2024-03-03T12:50:09.583+01:00 INFO 79783 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
Hibernate: select count(b1_0.id) from book b1_0
2024-03-03T12:50:09.781+01:00 INFO 79783 --- [ ID loading - 0] s.m.p.m.i.PojoMassIndexingLoggingMonitor : HSEARCH000027: Mass indexing is going to index 2 entities.
Hibernate: select b1_0.id from book b1_0
Hibernate: select b1_0.id,b1_0.author,b1_0.title from book b1_0 where b1_0.id in (?,?)
2024-03-03T12:50:10.027+01:00 INFO 79783 --- [ main] s.m.p.m.i.PojoMassIndexingLoggingMonitor : HSEARCH000028: Mass indexing complete. Indexed 2 entities.
2024-03-03T12:50:10.029+01:00 INFO 79783 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 3.985 seconds (process running for 4.404)
In another project, I had to upgrade to spring boot
v3.2.1
to make the solution work, otherwise you may encounter the following error
2024-03-03T12:54:27.374+01:00 ERROR 80057 --- [ task-1] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected exception occurred invoking async method: public void com.mypackage.backend.config.hibernate.EntityIndexer.onApplicationEvent(org.springframework.context.event.ContextRefreshedEvent)
java.lang.NoSuchMethodError: 'org.hibernate.SessionBuilder org.hibernate.engine.spi.SessionBuilderImplementor.tenantIdentifier(java.lang.Object)'
at org.hibernate.search.mapper.orm.massindexing.impl.HibernateOrmMassIndexingContext$HibernateOrmMassIndexingLoadingStrategy.createEntityLoader(HibernateOrmMassIndexingContext.java:201) ~[hibernate-search-mapper-orm-7.0.0.Final.jar:7.0.0.Final]
at org.hibernate.search.mapper.pojo.massindexing.impl.PojoMassIndexingEntityLoadingRunnable.runWithFailureHandler(PojoMassIndexingEntityLoadingRunnable.java:61) ~[hibernate-search-mapper-pojo-base-7.0.0.Final.jar:7.0.0.Final]
at org.hibernate.search.mapper.pojo.massindexing.impl.PojoMassIndexingFailureHandledRunnable.run(PojoMassIndexingFailureHandledRunnable.java:38) ~[hibernate-search-mapper-pojo-base-7.0.0.Final.jar:7.0.0.Final]