Compatibility
Java | 8, 11, 17 or 19 |
Hibernate ORM | 5.6 |
Hibernate ORM 6 (through *-orm6 artifacts) | 6.2 |
Elasticsearch server | 5.6 - 8.6 |
OpenSearch server | 1.0 - 2.5 |
Apache Lucene | 8.11 |
Not compatible with your requirements? Have a look at the other series.
See also the Compatibility policy.
Documentation
Documentation for Hibernate Search 6.2 can be accessed through the links below:
You can find more documentation for all series on the documentation page.
How to get it
More information about specific releases (announcements, download links) can be found here.
Getting started
If you want to start using Hibernate Search 6.2, please refer to the getting started guide:
Migrating
If you need to upgrade from a previous series, please refer to the migration guide:
What's new
Hibernate Search 6.2 is still in development:
We encourage you to give it a try and to let us know of any bugs or problems you encounter. |
Latest release announcement (2023-02-13): 6.2.0.Alpha2.
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 8.6 and 7.17 as well as other versions that were already compatible.
- OpenSearch
-
The Elasticsearch backend now works with OpenSearch 1.3 and 2.5 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 );
See this section of the reference documentation for more information.
Mapping improvements
- 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 newand
/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 usef.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 usef.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 );
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 whendropAndCreateSchemaOnStart
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.
Releases in this series
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