Search Returns No Results

I’m trying to implement Hibernate Seach in my Spring Boot Application. I have Product class which holds
“name” and “author” as strings. When I put the full name of a product, it returns the products with the same name with no problem but I want to implement it such way that when I write “19”, it should return the products which have “19” in their name, for example, “1984”, “19 Ways”, etc.

Can you show me the way of doing it? Thank you in advance.

In my product model, there are 2 Analyzers, one for indexing, one for the query.

@Indexed
@Entity
@Table(name = "product")
@AnalyzerDefs({
        @AnalyzerDef(name = "edgeNgram",
                tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
                filters = {
                        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class), 
                        @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                        @TokenFilterDef(
                                factory = EdgeNGramFilterFactory.class, 
                                params = {
                                        @Parameter(name = "minGramSize", value = "1"),
                                        @Parameter(name = "maxGramSize", value = "10")
                                }
                        )
                }),
        @AnalyzerDef(name = "edgeNGram_query",
                tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
                filters = {
                        @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
                        @TokenFilterDef(factory = LowerCaseFilterFactory.class)
                })
})

// Some code in here
@Field(analyzer = @Analyzer(definition = "edgeNgram"))
    @Column(length = 45)
    private String name;

@Field(analyzer = @Analyzer(definition = "edgeNgram"))
    @Column(length = 45)
    private String author;

My Repository Interface is:

  • I have 2 different repositories, my hibernate implementation is at “ProductSearchRepository”
public interface ProductRepository extends PagingAndSortingRepository<Product, Integer>, ProductSearchRepository {
}

My repository implementation is:

public class ProductRepositoryImpl implements ProductSearchRepository {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<Product> findProductByName(String name) {


        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);

        QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Product.class)
                .overridesForField( "name", "edgeNGram_query" )
                .get();

        Query query = queryBuilder
                .keyword()
                .onField("name")
                .matching(name)
                .createQuery();

        javax.persistence.Query jpaQuery =
                fullTextEntityManager.createFullTextQuery(query, Product.class);

        return jpaQuery.getResultList();
    }

}

Your code looks good. Did you think of reindexing your data after you changed the analyzers?

I actually didn’t do anything about indexing. I thought hibernate handles it automatically. How can I index my data?

It’s handled automatically when you persist/update/delete data through Hibernate ORM, but not when you change your mapping.

You should probably have a look at the getting started guide, in particular this section.

I’ve read that part before but I couldn’t understand where to put that code snippet. Now I put it into my “findProductByName” method in “ProductRepositoryImpl” but it still returns an empty list. I tried to put a debug point and print statement but it neither it prints something nor it stops at the debug point.

My updated ProductRepositoryImpl method:

public class ProductRepositoryImpl implements ProductSearchRepository {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<Product> findProductByName(String name) {
        
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        try {
            fullTextEntityManager.createIndexer().startAndWait();
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
            System.out.println(e.fillInStackTrace());
        }

        QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Product.class)
                .overridesForField( "name", "edgeNGram_query" )
                .get();

        Query query = queryBuilder
                .keyword()
                .onFields("name", "author")
                .matching(name)
                .createQuery();

        javax.persistence.Query jpaQuery =
                fullTextEntityManager.createFullTextQuery(query, Product.class);

        return jpaQuery.getResultList();
    }

And I searched about some implementations and I ran into an additional Config class that has

  • LocalContainerEntityManagerFactoryBean
  • DataSource
  • PlatformTransactionManager
  • PersistenceExceptionTranslationPostProcessor

Typed methods. I tried to implement it to my project but now it gives me “bean not registered” error on a totally different class.

The mass indexer should be executed whenever you need to reindex your data. This means when you first start your application, and whenever you change your mapping.

Some people add a button to their administration console to run it whenever they want.

Some people just run it whenever the application starts, because they don’t have much data and reindexing just takes a few seconds for them.

Where did you put the break point? Do you mean the method findProductByName is not called? If so, it could just be a problem of Spring Data not detecting your implementation and using auto-generated database queries instead.

When you run the mass indexer, it should print some logs saying “Reindexed X entities”. Can you please keep your current code (the one you showed in your latest post), start your application and run a search, then copy/paste your logs here, from when your application starts to after that line?

I just found out that code doesn’t go inside my findProductByName method. Now, I deleted my ProductSearch Repository, Added @Repository annotation to my ProductRepositoryImpl like this:

@Repository
public class ProductRepositoryImpl {

    @PersistenceContext
    private EntityManager entityManager;

    public List<Product> findProductByName(String name) {
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        try {
            fullTextEntityManager.createIndexer().startAndWait();
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
            System.out.println(e.fillInStackTrace());
        }

        QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Product.class)
                .overridesForField( "name", "edgeNGram_query" )
                .get();

        Query query = queryBuilder
                .keyword()
                .onFields("name", "author")
                .matching(name)
                .createQuery();

        // wrap Lucene query in a javax.persistence.Query
        javax.persistence.Query jpaQuery =
                fullTextEntityManager.createFullTextQuery(query, Product.class);

        System.out.println(jpaQuery.getResultList().size());

        // execute search
        return jpaQuery.getResultList();
    }

but when I run it I get this error:

java.lang.IllegalArgumentException: java.lang.Object is not an indexed entity or a subclass of an indexed entity

I think this occurred because I didn’t index my data as you said. But I couldn’t figure out how to implement these.

I tried to put these snippets below into the main method of my spring app. but I couldn’t create the “session” and “entityManager” so it gave me an error.

FullTextSession fullTextSession = ...
MassIndexer massIndexer = fullTextSession.createIndexer();
massIndexer.startAndWait();
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.createIndexer().startAndWait();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
fullTextEntityManager.createIndexer().startAndWait();

How can I index my data?

This actually means that the mass indexer failed to start because it couldn’t find any indexed entity (extending Object). There’s a configuration issue. First you should check that Product is actually an entity in Hibernate ORM. Then check that it’s annotated with the correct @Indexed annotation (the one from Hibernate Search). Also check Product is not an abstract class: you need to put @Indexed on concrete classes.

Your problem seems mostly related to Spring. I’m definitely not an expert. Maybe you should ask these questions to the Spring community instead?

That being said, from what I remember, the easiest way to reindex on startup is to add a method annotated with @PostConstruct. See this question.

1 Like

Thank you so much for your answers and your quick response. The problem was on my @Indexed Annotation and my dependency versions. I solved them and now it works like a charm :slight_smile:

1 Like