Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement full text search in Spring Boot with JPA + MySQL

public class Product {

    private Long id;    

    private String name;

    private String description;

}

Is there any way to implement a full text search with JpaRepository for the description of the class Product ?

like image 826
Konstantinos Kyriakos Avatar asked Aug 27 '19 22:08

Konstantinos Kyriakos


People also ask

What is full text search in MySQL?

A full-text index in MySQL is an index of type FULLTEXT . Full-text indexes can be used only with InnoDB or MyISAM tables, and can be created only for CHAR , VARCHAR , or TEXT columns.

What is @query annotation in spring boot?

The @Query annotation takes precedence over named queries, which are annotated with @NamedQuery or defined in an orm.xml file. It's a good approach to place a query definition just above the method inside the repository rather than inside our domain model as named queries.


1 Answers

You can use Hibernate Search, a library that plugs into Hibernate ORM to index your entities into a Lucene index on the filesystem, on the fly, when you send them to your database. See the Getting started guide.

(EDIT: Nowadays Hibernate Search also supports using Elasticsearch as a backend; it will automatically duplicate part of your database in Elasticsearch OR in a local Lucene index, whichever you chose.)

Querying the Lucene/Elasticsearch index is a bit different than querying the database, so you cannot use HQL or Criteria like you usually would. Hibernate Search offers its own query DSL.

If you want to use auto-magically generated method implementations in your repository, you can rely on Snowdrop, which is a Hibernate Search / Spring Data integration, but it hasn't been updated in a while.

Your best bet is probably to define query methods in your repository interface, then implement them yourself using Hibernate Search APIs. It's really not that complicated, and it's generally recommended for all but the most obvious queries. See the Spring Data JPA documentation.

Essentially you'll have something like the snippet below. Remember that you will need to reindex your database before this works! See the getting started guide for more information.

With Hibernate Search 6+:

@Indexed // Add this
public class Product {

    private Long id;    

    @FullTextField // And this
    private String name;

    @FullTextField // And this
    private String description;

}

public interface ProductRepository extends CrudRepository<Product, Long>, CustomizedProductRepository {
  // Declare automatically generated methods here
}


public interface CustomizedProductRepository {
  List<Product> search(String terms, int limit, int offset);
}



public class CustomizedProductRepositoryImpl implements CustomizedProductRepository {

  @PersistenceContext
  private EntityManager em;

  @Override
  public List<Product> search(String terms, int limit, int offset) {
    return Search.session(em).search(Product.class)
            .where(f -> f.match()
                    .fields("name", "description")
                    .matching(terms))
            .fetchHits(offset, limit);
  }
}

OR with Hibernate Search 5:

@Indexed // Add this
public class Product {

    private Long id;    

    @Field // And this
    private String name;

    @Field // And this
    private String description;

}

public interface ProductRepository extends CrudRepository<Product, Long>, CustomizedProductRepository {
  // Declare automatically generated methods here
}


public interface CustomizedProductRepository {
  List<Product> search(String terms, int limit, int offset);
}



public class CustomizedProductRepositoryImpl implements CustomizedProductRepository {

  @PersistenceContext
  private EntityManager em;

  @Override
  public List<Product> search(String terms, int limit, int offset) {
    FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);

    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory()
        .buildQueryBuilder().forEntity(Product.class).get();
    org.apache.lucene.search.Query luceneQuery = queryBuilder
        .keyword()
        .onFields("name", "description")
        .matching(terms)
        .createQuery();

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

    jpaQuery.setMaxResults(limit);
    jpaQuery.setFirstResult(offset);

    // execute search
    return jpaQuery.getResultList();
  }
}
like image 120
yrodiere Avatar answered Oct 04 '22 22:10

yrodiere