Hibernate Search with Spring Boot 3

124 views Asked by At

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;
    ...

Update

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 !

3

There are 3 answers

0
Smaillns On BEST ANSWER

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)

Note

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]
0
mark_o On

I suspect that the data hasn't been indexed yet !

Hibernate Search would populate the index only with those entities that you've been updating after adding the Search annotations. So, if you are just starting with adding those to your datamodel -- your indexes are empty, and you need to prepopulate them. An easy way to do that is to use a massindexer:

SearchSession searchSession = /* ... */ 
searchSession.massIndexer() 
        .startAndWait(); 

see here for more details: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#search-batchindex-massindexer

2
yrodiere On

I'm struggling bcz manything have been changed in the latest versions of Hibernate Search

If you mean "compared to Hibernate Search 5", that's true, and that's why there is an extensive migration guide: https://docs.jboss.org/hibernate/search/6.0/migration/html_single/

I suspect that the data hasn't been indexed yet !

Data that was already present in the database before you deployed your application must indeed be indexed explicitly: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#indexing-massindexer

Hibernate Search will then keep the index up to date as you change data through Hibernate ORM: https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#listener-triggered-indexing

any insights on what I might have overlooked ?

I'm afraid without a concrete example, including the analysis configuration, the data of the document you expect to match, and the terms of your query, there's little we can do to help.

Ideally you'd just provide a reproducer based on the Hibernate Search test case templates: https://github.com/hibernate/hibernate-test-case-templates/tree/main/search/

Related Questions in HIBERNATE-SEARCH-7