Hibernate Search

6.2 series limited-support

Standalone POJO Mapper, mapping of classes/records to projections using @ProjectionConstructor, highlighting, indexing plan filters to suspend automatic indexing, excludePaths for @IndexedEmbedded, search DSL improvements (including projections on object fields), Elasticsearch schema export, compatibility with Elasticsearch 8.9 and OpenSearch 2.9, upgrade of -orm6 artifacts to Hibernate ORM 6.2.

Hibernate Search 6.2 is in limited support mode:

  • releases may be delayed until a significant need arises;

  • bugs will only be fixed if they are considered significant enough or in the context of paid support;

  • pull requests against this version might be rejected for bugs that are not considered critical enough;

  • bug reproducers using this version will be given lower priority.

We recommend that you upgrade to a newer series if possible or look for paid support.

See also the Maintenance policy.

Compatibility

Java 8, 11, 17, 20 or 21
Hibernate ORM 5.6 in the main artifacts, 6.2 - 6.3 through *-orm6 artifacts
Elasticsearch server 5.6 - 8.10
OpenSearch server 1.0 - 2.11
Apache Lucene 8.11

Not compatible with your requirements? Have a look at the other series.

See also the Compatibility policy and Maintenance policy.

Documentation

Documentation for Hibernate Search 6.2 can be accessed through the links below:

HTML PDF API (JavaDoc)

You can find more documentation for all series on the documentation page.

How to get it

Hibernate Search 6.2 is in limited maintenance mode: we recommend that you upgrade to a newer series if possible or look for paid support.

See also the Maintenance policy.

Maven, Gradle...

Maven artifacts of Hibernate Search are published to Maven Central. Most build tools fetch artifacts from Maven Central by default, but if that's not the case for you, see this page to configure your build tool.

You can find the Maven coordinates of all artifacts through the link below:

Maven artifacts

Below are the Maven coordinates of the main artifacts.

org.hibernate.search:hibernate-search-mapper-orm-orm6:6.2.4.Final
Hibernate ORM mapper (Hibernate ORM 6)
org.hibernate.search:hibernate-search-mapper-orm-coordination-outbox-polling-orm6:6.2.4.Final
"outbox-polling" coordination strategy for the Hibernate ORM mapper (Hibernate ORM 6)
org.hibernate.search:hibernate-search-mapper-pojo-standalone:6.2.4.Final
Standalone POJO mapper
org.hibernate.search:hibernate-search-backend-lucene:6.2.4.Final
Lucene backend
org.hibernate.search:hibernate-search-backend-elasticsearch:6.2.4.Final
Elasticsearch/OpenSearch backend
org.hibernate.search:hibernate-search-backend-elasticsearch-aws:6.2.4.Final
Amazon IAM authentication for Elasticsearch/OpenSearch
org.hibernate.search:hibernate-search-mapper-orm-batch-jsr352-core-orm6:6.2.4.Final
Batch for Java (JSR-352) mass indexing job for the Hibernate ORM mapper - Core (Hibernate ORM 6)
org.hibernate.search:hibernate-search-mapper-orm-batch-jsr352-jberet-orm6:6.2.4.Final
Batch for Java (JSR-352) mass indexing job for the Hibernate ORM mapper - JBeret specifics (Hibernate ORM 6)
org.hibernate.search:hibernate-search-v5migrationhelper-orm-orm6:6.2.4.Final
Helper for migrating from Hibernate Search 5 to Hibernate Search 6 (Hibernate ORM mapper + Lucene backend) (Hibernate ORM 6)
org.hibernate.search:hibernate-search-mapper-orm:6.2.4.Final
Hibernate ORM mapper (Hibernate ORM 5)
org.hibernate.search:hibernate-search-mapper-orm-coordination-outbox-polling:6.2.4.Final
"outbox-polling" coordination strategy for the Hibernate ORM mapper (Hibernate ORM 5)
org.hibernate.search:hibernate-search-mapper-orm-batch-jsr352-core:6.2.4.Final
Batch for Java (JSR-352) mass indexing job for the Hibernate ORM mapper - Core (Hibernate ORM 5)
org.hibernate.search:hibernate-search-mapper-orm-batch-jsr352-jberet:6.2.4.Final
Batch for Java (JSR-352) mass indexing job for the Hibernate ORM mapper - JBeret specifics (Hibernate ORM 5)
org.hibernate.search:hibernate-search-v5migrationhelper-orm:6.2.4.Final
Helper for migrating from Hibernate Search 5 to Hibernate Search 6 (Hibernate ORM mapper + Lucene backend) (Hibernate ORM 5)

All Maven artifacts of this project released after 2022-01-26 are signed.

To verify signed Maven artifacts, head to this page.

Direct download

A ZIP archive containing all JAR files, documentation and source is available from SourceForge:

Download ZIP archive

Individual Maven artifacts may be downloaded directly from the Maven repository:

Maven Central subdirectory

See here for how to download all dependencies of your Maven project to a local directory on your filesystem.

See here for how to download an explicitly listed set of artifacts to a local directory on your filesystem.

More information about specific releases (announcements, download links) can be found here.

Getting started

Hibernate Search 6.2 is in limited maintenance mode: we recommend that you upgrade to a newer series if possible or look for paid support.

See also the Maintenance policy.

If you want to start using Hibernate Search 6.2, please refer to the getting started guide:

HTML PDF

Migrating

If you need to upgrade from a previous series, please refer to the migration guide:

HTML PDF

What's new

Latest release announcement (2024-04-10): 6.2.4.Final.

A detailed list of new features, improvements and fixes in this series can be found on our issue tracker.

Dependency upgrades

Hibernate ORM

Hibernate Search still depends on Hibernate ORM 5.6 by default. -orm6 artifacts depend on Hibernate ORM 6.2.

Lucene

The Lucene backend still uses Lucene 8.11.

Elasticsearch

The Elasticsearch backend now works with Elasticsearch 7.17, 8.9 and 8.10 as well as other versions that were already compatible.

OpenSearch

The Elasticsearch backend now works with OpenSearch 1.3, 2.10 and 2.11 as well as other versions that were already compatible.

Standalone POJO Mapper

The Standalone POJO Mapper enables mapping arbitrary POJOs to indexes.

Its key feature compared to the Hibernate ORM integration is its ability to run without Hibernate ORM or a relational database. It can be used to index entities coming from an arbitrary datastore or even (though that’s not recommended in general) to use Lucene or Elasticsearch as a primary datastore.

For more information about the Standalone POJO Mapper, see this section of the reference documentation.

To get started with the Standalone POJO Mapper, see this getting started guide.

Mapping index content to custom types (projection constructors)

Hibernate Search now offers the ability to define projections through the mapping of custom types (typically records), by applying the @ProjectionConstructor annotation to those types or their constructor:

@ProjectionConstructor
public record MyBookProjection(String title, List<Author> authors) {
    @ProjectionConstructor
    public record Author(String firstName, String lastName) {
    }
}

Executing such a projection then becomes as easy as referencing the custom type:

List<MyBookProjection> hits = searchSession.search( Book.class )
        .select( MyBookProjection.class )
        .where( f -> f.matchAll() )
        .fetchHits( 20 );

More advanced projections can be configured through explicit annotations, for example a projection to the entity identifier:

@ProjectionConstructor
public record MyBookIdAndTitleProjection(
        @IdProjection (1)
        Integer id,
        String title (2)
) {
}
1 Projects to the entity identifier (explicit projection through an explicit annotation)
2 Projects to field title (implicit projection)

Explicit annotations also allow configuring field projections more precisely, for example setting the path of a field projection to something else than just the name of the constructor parameter:

@ProjectionConstructor
public record MyBookTitleAndAuthorNamesProjection(
        @FieldProjection (1)
        String title,
        @FieldProjection(path = "authors.lastName") (2)
        List<String> authorLastNames
) {
}
1 Projects to field title (path derived from the name of the constructor parameter)
2 Projects to field authors.lastName (explicit path)

Highlighting

Hibernate Search now offers a new feature in the Search DSL: highlighting.

Highlighting is a projection that returns fragments from full-text fields of matched documents that caused a query match. Specific terms that caused the match are "highlighted" with a pair of opening and closing tags. It can help a user to quickly identify the information they were searching for on a results page.

For example, one can enable highlighting on a full-text field like this:

@Entity
@Indexed
public class Book {
    @Id
    private Integer id;

    @FullTextField(analyzer = "english")
    private String title;

    @FullTextField(analyzer = "english", projectable = Projectable.YES)
    private String description;
}

And then retrieve highlights like this:

SearchSession searchSession = /* ... */

List<List<String>> result = searchSession.search( Book.class )
        .select( f -> f.highlight( "description" ) )
        .where( f -> f.match().field( "description" ).matching( "robot" ) )
        .fetchHits( 20 );

Which can for example return the following highlights:

# Hit #0
["A <em>robot</em> becomes self-aware."]
# Hit #1
["A <em>robot</em> helps investigate a murder on an extrasolar colony.",
 "On this planet, <em>robots</em> are used extensively."]
# etc.

This is a simple example, but highligting can be configured extensively. See this section of the reference documentation for more information.

Indexing plan filters

Hibernate Search now allows enabling/disabling indexing in indexing plans (which includes in particular listener-triggered indexing), both fully (for all types) or on a per-type basis.

For example, the following will disable automatic indexing for future changes on all instances of the class Company, except for instances of its subclass Customer:

SearchMapping searchMapping = /* ... */
searchMapping.indexingPlanFilter(
        ctx -> ctx.exclude( Company.class )
                .include( Customer.class )
);

Indexing plan filters can also be configured per-session (though limitations apply when using the outbox-polling coordination strategy):

SearchSession searchSession = /* ... */
searchSession.indexingPlanFilter(
        ctx -> ctx.exclude( Company.class )
                .include( Customer.class )
);

Mapping improvements

@IndexedEmbedded(excludePaths = …​)

The @IndexedEmbedded annotation now exposes an excludePaths attribute, allowing the inclusion of all paths with only a few selectively excluded, as opposed to the pre-existing approach of selectively including paths with includePaths.

Projectable fields

All fields are now projectable by default with the Elasticsearch backend.

This change was made because making a field projectable doesn’t incur any performance penalty with the Elasticsearch backend.

Since making a field projectable does have an impact on performance with the Lucene backend, the defaults with the Lucene backend didn’t change: Lucene fields still need to be made projectable explicitly.

Search DSL improvements

Simpler boolean operators with the and/or/not predicates

For simpler use cases, you can now avoid the rather complex bool predicate and use the new and/or/not predicates instead:

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.and(
                        f.match().field( "title" )
                                .matching( "robot" ),
                        f.match().field( "description" )
                                .matching( "crime" )
        ) )
        .fetchHits( 20 );
List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.or(
                        f.match().field( "title" )
                                .matching( "robot" ),
                        f.match().field( "description" )
                                .matching( "investigation" )
        ) )
        .fetchHits( 20 );
List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.not(
                f.match()
                        .field( "genre" )
                        .matching( Genre.SCIENCE_FICTION )
        ) )
        .fetchHits( 20 );
Shorter syntax for complex, root boolean predicates

Instead of .where( f → f.bool( b → …​ ) ), you can now use .where( (f, b) → …​ ):

MySearchParameters searchParameters = getSearchParameters();
List<Book> hits = searchSession.search( Book.class )
        .where( (f, root) -> {
            root.add( f.matchAll() );
            if ( searchParameters.getGenreFilter() != null ) {
                root.add( f.match().field( "genre" )
                        .matching( searchParameters.getGenreFilter() ) );
            }
            if ( searchParameters.getFullTextFilter() != null ) {
                root.add( f.match().fields( "title", "description" )
                        .matching( searchParameters.getFullTextFilter() ) );
            }
            if ( searchParameters.getPageCountMaxFilter() != null ) {
                root.add( f.range().field( "pageCount" )
                        .atMost( searchParameters.getPageCountMaxFilter() ) );
            }
        } )
        .fetchHits( 20 );

The older syntax has been deprecated in favor of the new one.

Clearer syntax for complex, non-root boolean predicates

Instead of f.bool( b → …​ ), you can now use f.bool().with( b → …​ ):

MySearchParameters searchParameters = getSearchParameters();
List<Book> hits = searchSession.search( Book.class )
        .where( (f, b) -> {
            b.must( f.matchAll() );
            if ( searchParameters.getGenreFilter() != null ) {
                b.must( f.match().field( "genre" )
                        .matching( searchParameters.getGenreFilter() ) );
            }
            if ( !searchParameters.getAuthorFilters().isEmpty() ) {
                b.must( f.bool().with( b2 -> {
                    for ( String authorFilter : searchParameters.getAuthorFilters() ) {
                        b2.should( f.match().fields( "authors.firstName", "authors.lastName" )
                                .matching( authorFilter ) );
                    }
                } ) );
            }
        } )
        .fetchHits( 20 );

The older syntax has been deprecated in favor of the new one.

Clearer syntax for the nested predicate

Instead of f.nested().objectField( …​ ).nest( f.bool().must( …​ ) ), you can now use f.nested( …​ ).add( …​ ):

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.nested( "authors" )
                .add( f.match().field( "authors.firstName" )
                        .matching( "isaac" ) )
                .add( f.match().field( "authors.lastName" )
                        .matching( "asimov" ) ) )
        .fetchHits( 20 );

The older syntax has been deprecated in favor of the new one.

New matchNone predicate

The matchNone predicate matches no documents.

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.matchNone() )
        .fetchHits( 20 );
New syntax for composite projections

The definition of composite projections is now possible with a fluent syntax:

List<MyPair<String, Genre>> hits = searchSession.search( Book.class )
        .select( f -> f.composite()
                .from( f.field( "title", String.class ),
                        f.field( "genre", Genre.class ) )
                .as( MyPair::new ) )
        .where( f -> f.matchAll() )
        .fetchHits( 20 );

Most older syntaxes have been deprecated in favor of the new one.

New object projection

The object projection yields one projected value for each object in a given object field.

List<List<MyAuthorName>> hits = searchSession.search( Book.class )
        .select( f -> f.object( "authors" )
                .from( f.field( "authors.firstName", String.class ),
                        f.field( "authors.lastName", String.class ) )
                .as( MyAuthorName::new )
                .multi() )
        .where( f -> f.matchAll() )
        .fetchHits( 20 );
New constant projection

The constant projection returns the same value for every single document, the value being provided when defining the projection.

Instant searchRequestTimestamp = Instant.now();
List<MyPair<Integer, Instant>> hits = searchSession.search( Book.class )
        .select( f -> f.composite()
                .from( f.id( Integer.class ), f.constant( searchRequestTimestamp ) )
                .as( MyPair::new ) )
        .where( f -> f.matchAll() )
        .fetchHits( 20 );
.missing().lowest()/.missing().highest() options in sorts

When sorting on a field that may not have a value for some documents, it was already possible to use .missing().first()/.missing().last() to tell Hibernate Search to put such documents in first/last position (respectively), regardless of sorting order (ascending/descending).

It is now possible, as an alternative, to use .missing().lowest()/.missing().highest() to tell Hibernate Search to consider such documents as having the lowest/highest value (respectively), taking into account sorting order (ascending/descending):

  • .missing().lowest() puts documents with no value in the first position when using ascending order or in the last position when using descending order.

  • .missing().highest() puts documents with no value in the last position when using ascending order or in the first position when using descending order.

This is mostly useful when the position of missing values is hardcoded, but the sort order is given by the user:

SortOrder orderFromUser = /* ... */;
List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.matchAll() )
        .sort( f -> f.field( "pageCount" ).missing().lowest().order( orderFromuser ) )
        .fetchHits( 20 );

Mass indexing improvements

Mass indexing multiple tenants

In multi-tenant applications, mass indexing can now handle multiple tenants at once, provided you don’t pass any tenant identifier when creating the mass indexer, and you provided a list of tenants in the Hibernate Search configuration. See this section of the reference documentation for more information.

Setting up thread locals during mass indexing

The mass indexer now has a concept of "mass indexing environment", allowing for instance to set up custom thread locals in mass indexing threads. See the environment parameter in this section of the reference documentation for more information.

Better exception handling

Exceptions thrown by Hibernate ORM during mass indexing are now passed to the failure handler as every other exception, instead of aborting the whole mass indexing.

Smarter defaults for parameters

purgeAllOnStart is now disabled by default in the mass indexer when dropAndCreateSchemaOnStart is enabled.

outbox-polling coordination improvements

Outbox events and agents now use UUIDs for their identifiers

The primary key of the relevant tables are now using UUIDs instead of longs, to avoid reliance on sequences that were slowing down event processing on some databases. The migration guide includes migration scripts for the necessary database schema changes.

Customizable database schema

Simple, straightforward configuration properties now allow customizing the database schema involved in Hibernate Search’s outbox-polling coordination strategy: table names, schema and catalog, type of UUID columns as well as UUID generation strategy (random vs. time). See this section of the reference documentation for more information.

Elasticsearch schema export

It is now possible to export the Elasticsearch schema that Hibernate Search expects to JSON files on the filesystem:

SearchSchemaManager schemaManager = searchSession.schemaManager();
schemaManager.exportExpectedSchema( Path.of( "mydirectory" ) );

The code above will result in a directory tree similar to this:

# For the default backend: backend/indexes/<index-name>/<file>
mydirectory/backend/indexes/customer/create-index.json
mydirectory/backend/indexes/customer/create-index-query-params.json
mydirectory/backend/indexes/order/create-index.json
mydirectory/backend/indexes/order/create-index-query-params.json
# For additional named backends: backend/<backend-name>/indexes/<index-name>/<file>
mydirectory/backends/auth/indexes/user/create-index.json
mydirectory/backends/auth/indexes/user/create-index-query-params.json
mydirectory/backends/auth/indexes/usergroup/create-index.json
mydirectory/backends/auth/indexes/usergroup/create-index-query-params.json

Development versions (SNAPSHOTS)

The latest development versions of Maven artifacts for Hibernate Search are published to the OSSRH snapshots repository.

OSSRH snapshots subdirectory

You should only need those (unstable) versions for testing recently merged patches, and should never use them in production.

To consume these artifacts, you may need to configure your build tool to fetch artifacts from and to enable snapshots:

Maven Gradle

Releases in this series

Hibernate Search 6.2 is in limited maintenance mode: we recommend that you upgrade to a newer series if possible or look for paid support.

See also the Maintenance policy.

6.2.4.Final

2024-04-10

Upgrade of -orm6 artifacts to Hibernate ORM 6.2.24.Final, documentation updates.

How to get it Getting started

Maven artifacts Download Resolved issues Release announcement

6.2.3.Final

2023-12-21

Compatibility with OpenSearch 2.11, upgrade of -orm6 artifacts to Hibernate ORM 6.2.17.Final, upgrade to Elasticsearch client 8.10.4, bugfixes.

Maven artifacts Download Resolved issues Release announcement

6.2.2.Final

2023-10-03

Compatibility with Elasticsearch 8.10 and OpenSearch 2.10, upgrade of -orm6 artifacts to Hibernate ORM 6.2.9.Final, upgrade to Elasticsearch client 8.10.2, Jackson 2.15.2 and Avro 1.11.3, deprecation of ~ operator in regexp predicates, bugfixes.

Maven artifacts Download Resolved issues Release announcement

6.2.1.Final

2023-08-17

Compatibility with Elasticsearch 8.9 and OpenSearch 2.9, upgrade of -orm6 artifacts to Hibernate ORM 6.2.7.Final, upgrade to Elasticsearch client 8.9.0, bugfixes.

Maven artifacts Download Resolved issues Release announcement

6.2.0.Final

2023-07-07

Upgrade of -orm6 artifacts to Hibernate ORM 6.2.6.Final, upgrade to Elasticsearch client 8.8.2, upgrade to Avro 1.11.2.

Maven artifacts Download Resolved issues Release announcement

6.2.0.CR1

2023-06-26

New excludePaths filter for @IndexedEmbedded, includePaths/includeDepth/excludePaths filters for @ObjectProjection, compatibility with OpenSearch 2.8, upgrade to Hibernate ORM 5.6.15.Final, upgrade of -orm6 artifacts to Hibernate ORM 6.2.5.Final, dropped compatibility of -orm6 artifacts with Hibernate ORM 6.0/6.1, other bugfixes and improvements.

Maven artifacts Download Resolved issues Release announcement

6.2.0.Beta1

2023-06-02

Highlighting, indexing plan filters to suspend automatic indexing, mapping annotations for constructor parameters when using @ProjectionConstructor, Elasticsearch schema export tool, .missing().lowest()/.missing().highest() options in sorts, compatibility with Elasticsearch 8.8 and OpenSearch 2.7, upgrade of -orm6 artifacts to Hibernate ORM 6.2.4.Final, other bugfixes and improvements.

Maven artifacts Download Resolved issues Release announcement

6.2.0.Alpha2

2023-02-13

Simpler 'and'/'or'/'not' predicates, mass indexing for multiple tenants, switch to UUIDs for identifiers in the outbox-polling coordination strategy, compatibility with Elasticsearch 8.6 and OpenSearch 2.5, upgrade of -orm6 artifacts to Hibernate ORM 6.2.0.CR2, other bugfixes and improvements.

Maven artifacts Download Resolved issues Release announcement

6.2.0.Alpha1

2022-07-12

Standalone POJO Mapper, mapping of classes/records to projections using @ProjectionConstructor, search DSL improvements (including projections on object fields), compatibility with Elasticsearch 8.3 and OpenSearch 2.0, other bugfixes and improvements.

Maven artifacts Download Resolved issues Release announcement

Back to top