Automatic indexing of Hibernate ORM entities into Apache Lucene or Elasticsearch. Advanced search API: full-text, geospatial, aggregations and more.

Full-text search for entities

Hibernate Search automatically extracts data from Hibernate ORM entities to push it to local Apache Lucene indexes or remote Elasticsearch/OpenSearch indexes.

It features:

  • Declarative mapping of entity properties to index fields, either through annotations or a programmatic API.

  • On-demand mass indexing of all entities in the database, to initialize the indexes with pre-existing data.

  • On-the-fly listener-triggered indexing of entities modified through a Hibernate ORM session, to always keep the indexes up-to-date.

  • A Search DSL to easily build full-text search queries and retrieve the hits as Hibernate ORM entities.

  • And much more! (see below)

For example, map your entities like this:

@Entity
// This entity is mapped to an index
@Indexed
public class Book {

    // The entity ID is the document ID
    @Id
    @GeneratedValue
    private Integer id;

    // This property is mapped to a document field
    @FullTextField
    private String title;

    @ManyToMany
    // Authors will be embedded in Book documents
    @IndexedEmbedded
    private Set<Author> authors = new HashSet<>();

    // Getters and setters
    // ...
}

@Entity
public class Author {

    @Id
    @GeneratedValue
    private Integer id;

    // This property is mapped to a document field
    @FullTextField
    private String name;

    @ManyToMany(mappedBy = "authors")
    private Set<Book> books = new HashSet<>();

    // Getters and setters
    // ...
}

Index pre-existing data like this:

SearchSession searchSession = Search.session( entityManager );
MassIndexer indexer = searchSession.massIndexer( Book.class );
indexer.startAndWait();

Listener-triggered indexing does not require any change to code based on JPA or Hibernate ORM:

Author author = new Author();
author.setName( "Isaac Asimov" );

Book book = new Book();
book.setTitle( "The Caves Of Steel" );
book.getAuthors().add( author );
author.getBooks().add( book );

entityManager.persist( author );
entityManager.persist( book );

And search like this:

SearchResult<Book> result = Search.session( entityManager )
        .search( Book.class )
        .where( f -> f.match()
                .fields( "title", "authors.name" )
                .matching( "Isaac" ) )
        .fetch( 20 );

List<Book> hits = result.hits();
long totalHitCount = result.total().hitCount();

Full control

Unlike with web search, this is your data, your domain, your application, stored wherever you decide.

You can choose what to index (and how to index it) very precisely with per-property mapping and go even further with custom bridges.

You also have far better control on how your data is processed with configurable analysis, so you can:

  • Tune text processing for specific languages.

  • Tune text processing for domain specific terminology (e.g. medical terms, custom acronyms expansion, …​).

  • Control the ranking process: which results are more important.

Easy yet powerful

Designed to be easy to use from the ground up. Handles schema initialization, indexing and query syntax transparently while you focus on the business side of your search.

The Search DSL exposes many different predicates and sorts through a succinct, easy-to-use API.

And if you need to go beyond, it won’t stop you: native Lucene Queries or Elasticsearch JSON can be inserted right in the middle of Search DSL queries.

Local or distributed

While you’ll find that performance of a "single box" based on Apache Lucene is exceptional, you can also scale out by distributing your application and relying on an Elasticsearch cluster for indexing, with only a few configuration changes.

For even better scaling and coordination with the Elasticsearch backend, Hibernate Search can also send entity change events to a transactional outbox table in the database, spreading indexing across multiple asynchronous event processors.

Spatial queries

Indexing geo-localized entities is as easy as adding the @GenericField annotation.

Filter results around a certain location like the user position, and use a distance sort to pull to the top the matching pizzerias which are closest to the user.

Aggregations

Get search results aggregated by groups and categories.

For example webshops will want to show all hits, but also display a count of hits by price range and brand.

Back to top