Hibernate Search is a library that integrates Hibernate ORM with Apache Lucene or Elasticsearch by automatically indexing entities, enabling advanced search functionality: full-text, geospatial, aggregations and more. For more information, see Hibernate Search on hibernate.org.

We just published Hibernate Search 6.2.0.Alpha1, an alpha release of the next minor version of Hibernate Search.

The main feature of this new version is the new Standalone POJO mapper, allowing to map arbitrary objects to an index, even if those objects are not Hibernate ORM entities. This opens the way to custom integrations to your favorite NoSQL datastores!

Beyond that, 6.2.0.Alpha1 also includes 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, and more.

Thanks

Thanks to:

What’s new

Hibernate Search 6.2 is still in development: some features are still incomplete or may change in a backward-incompatible way.

Dependency upgrades

Hibernate ORM (HSEARCH-4628/HSEARCH-4627)

Hibernate Search now depends on Hibernate ORM 5.6.10.Final, or Hibernate ORM 6.1.1.Final for -orm6 artifacts.

Lucene (HSEARCH-4609)

The Lucene backend now uses Lucene 8.11.2.

Elasticsearch (HSEARCH-4473/HSEARCH-4622)

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

OpenSearch (HSEARCH-4561/HSEARCH-4562)

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

Others

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)

As of HSEARCH-3927, 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 );

Mapping improvements

As of HSEARCH-4575, all fields are 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

Shorter syntax for complex, root boolean predicates (HSEARCH-4520)

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, b) -> {
            b.must( f.matchAll() );
            if ( searchParameters.getGenreFilter() != null ) {
                b.must( f.match().field( "genre" )
                        .matching( searchParameters.getGenreFilter() ) );
            }
            if ( searchParameters.getFullTextFilter() != null ) {
                b.must( f.match().fields( "title", "description" )
                        .matching( searchParameters.getFullTextFilter() ) );
            }
            if ( searchParameters.getPageCountMaxFilter() != null ) {
                b.must( 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 (HSEARCH-4520)

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 (HSEARCH-4499)

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

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

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

New matchNone predicate (HSEARCH-4489)

The matchNone predicate matches no documents.

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

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 (HSEARCH-3943)

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 (HSEARCH-4489)

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

outbox-polling coordination improvements

As of HSEARCH-4533, you can now customize table names, schema and catalog involved in Hibernate Search’s outbox-polling coordination strategy through simple, straightforward configuration properties.

Other improvements and bug fixes

  • HSEARCH-4565: Cyclic dependency detection for IndexingDependency(derivedFrom = …​) now detects "buried" cycles.

  • HSEARCH-4580: "_routing" in custom Elasticsearch schema no longer leads to "JsonIOException: JSON document was not fully consumed."

  • HSEARCH-4584: Projection and sort on the same nested field no longer fails with the Lucene Backend.

  • HSEARCH-4619: Boolean predicate now consistently match no documents when not adding any clause, regardless of the backend.

  • HSEARCH-4604: AWS Request signing no longer ignores the target port of the service endpoint.

  • HSEARCH-4483: The default mass indexing monitor (which uses logging) now uses a different format and displays an "instant speed" on top of the overall speed.

  • HSEARCH-4594: Bean references to configurers defined in configuration properties can now be multi-valued, allowing you to apply multiple configurers at once.

  • HSEARCH-4611: A few error messages have been improved, and multi-line error messages are now better indented in Hibernate Search’s failure reports.

And more. For a full list of changes since the previous releases, please see the release notes.

How to get this release

All details are available and up to date on the dedicated page on hibernate.org.

Getting started, migrating

For new applications, refer to the getting started guide:

For existing applications, Hibernate Search 6.2 is a drop-in replacement for 6.1, assuming you also upgrade the dependencies. Information about deprecated configuration and API is included in the migration guide.

Feedback, issues, ideas?

To get in touch, use the following channels:


Back to top