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 7.2.0.Alpha1, the first alpha release of the next minor version of Hibernate Search.

This version contains many improvements to the Search DSL, including new projection types, predicate improvements, query parameters and more.

It also upgrades Lucene and Hibernate ORM dependencies and includes compatibility with the latest OpenSearch 2.14.

What’s new

Hibernate Search 7.2 is still in its early stages of development: some features are still incomplete or may change in a backward-incompatible way.

Dependency upgrades

Hibernate ORM (HSEARCH-5150)

Hibernate Search now depends on Hibernate ORM 6.5.1.Final.

Lucene (HSEARCH-5086)

The Lucene backend now uses Lucene 9.10.0.

OpenSearch (HSEARCH-5151)

The Elasticsearch backend works with OpenSearch 2.14, as well as other versions that were already compatible.

Others

queryString/simpleQueryString predicates for numeric/date fields

simpleQueryString and queryString can now be applied to numeric and date fields.

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.queryString()
                .field( "numberOfPages" )
                .matching( "[350 TO 800]" )
        )
        .fetchHits( 20 );

See corresponding sections in simpleQueryString queryString documentation for details.

match predicate and minimum number of terms that should match

With the introduction of the minimumShouldMatch option, similar to the ones already available for the bool, queryString, simpleQueryString predicates, it is now possible to require that an arbitrary number of terms from the match string are present in the document in order for the match predicate to match.

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.match()
                .field( "title" )
                .matching( "investigation detective automatic" )
                .minimumShouldMatchNumber( 2 ) ) (1)
        .fetchHits( 20 ); (2)
1 At least two terms must match for this predicate to match.
2 All returned hits will match at least two of the terms: their titles will match either investigation and detective, investigation and automatic, detective and automatic, or all three of these terms.

Basic support for parameters at the query level

A number of withParameters(..) methods were introduced to the Search DSL. Through them, it is now possible to construct aggregations, predicates, projections, and sorts using query parameters. These can be helpful when there is a need to use the same parameter in multiple parts of the query or when the same query has to be executed for various parameter values.

SearchScope<Book> scope = searchSession.scope( Book.class );
SearchPredicateFactory factory = scope.predicate();
SearchPredicate predefinedPredicate = factory.withParameters(
        params -> factory.bool() (1)
                .should( factory.match().field( "title" )
                        .matching( params.get( "title-param", String.class ) ) ) (2)
                .filter( factory.match().field( "genre" )
                        .matching( params.get( "genre-param", Genre.class ) ) ) (3)
).toPredicate();

List<Book> crimeBooks = searchSession.search( Book.class )
        .where( predefinedPredicate ) (4)
        .param( "title-param", "robot" )  (5)
        .param( "genre-param", Genre.CRIME_FICTION )
        .fetchHits( 20 );

List<Book> scienceFictionBooks = searchSession.search( Book.class )
        .where( predefinedPredicate ) (6)
        .param( "title-param", "spaceship" ) (7)
        .param( "genre-param", Genre.SCIENCE_FICTION )
        .fetchHits( 20 );
1 Start creating the .withParameters() predicate.
2 Access the query parameter title-param of String type when constructing the predicate.
3 Access the query parameter genre-param of Genre enum type when constructing the predicate.
4 Use the predefined, parameterized predicate in a query.
5 Set parameters required by the predicate at the query level.
6 Reuse the predefined, parameterized predicate in a query.
7 Set a different pair of parameters required by the predicate at the query level.

@DistanceProjection to map a constructor parameter to a distance projection

With the introduction of the query parameters, it is now possible to define a @DistanceProjection that can be used in the projection constructors.

@ProjectionConstructor
public record MyAuthorPlaceProjection(
        @DistanceProjection( (1)
                fromParam = "point-param", (2)
                path = "placeOfBirth") (3)
        Double distance ) {
}
1 Annotate the parameter that should receive the distance value with @DistanceProjection.
2 Specify the query parameter that will be used to calculate the distance from.
3 Optionally, customize the path, since most likely the GeoPoint property of the entity will have a different name from the distance property in a projection.
List<MyAuthorPlaceProjection> hits = searchSession.search( Author.class )
        .select( MyAuthorPlaceProjection.class )
        .where( f -> f.matchAll() )
        .param( "point-param", GeoPoint.of( latitude, longitude ) ) (1)
        .fetchHits( 20 );
1 Pass a query parameter value, with the same name point-param as in the @DistanceProjection fromParam of a projection constructor.

Document tree projection

With the Lucene backend, requesting a document tree projection is now possible. This new .documentTree() projection returns the matched document as a tree containing native Lucene Document and corresponding nested tree nodes.

List<DocumentTree> hits = searchSession.search( Book.class )
        .extension( LuceneExtension.get() )
        .select( f -> f.documentTree() )
        .where( f -> f.matchAll() )
        .fetchHits( 20 );

DocumentTree documentTree = hits.get( 0 );
Document rootDocument = documentTree.document();
Map<String, Collection<DocumentTree>> nestedDocuments = documentTree.nested();
// ...

Other improvements and bug fixes

  • HSEARCH-4572: Using SearchPredicate/SearchProjection/SearchSort with a broader scope than the search query.

  • HSEARCH-4929: Add an option to drop and create the schema when starting mass indexing using Jakarta Batch integration.

  • HSEARCH-4963: API to run analysis on a given String.

  • HSEARCH-5006: Non-string tenant identifiers

  • HSEARCH-5016: Allow binding a @HighlightProjection to a single-valued String (instead of List<String>) when using numberOfFragments(1)

  • HSEARCH-5039: Fix the tenant filter for knn predicates and use it as a filter for knn predicates

  • HSEARCH-5124: Jakarta Batch Job parameter purgeAllOnStart will now only purge the documents of the types specified in the entityTypes instead of purging all documents.

And more. Please see the release notes for a complete list of changes since the previous releases.

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 7.2 is a drop-in replacement for 7.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