I am trying to implement a query for the following logic:
Match results where endDate is above now OR endDate is not present (i.e. null)
So far, I am able to do the first part as following:
dateQuery = queryBuilder.range()
.onField("endDate")
.above(LocalDateTime.now())
.createQuery();
Unfortunately, I cannot figure out the second part. I have tried this (and failed):
queryBuilder.bool()
.must(dateQuery)
.should(queryBuilder.keyword().onField("endDate")
.matching("").createQuery())
.createQuery();
Is there any elegant way of checking if a non-string field is Null in Hibernate Search?
By default empty/null values are not indexed, thus looking for an empty/null value will not return any result.
With Hibernate Search 6 and above, consider the exists()
predicate predicate, which you can negate to find documents where a given field does not exist (has no value).
Hibernate Search 6.0 / 6.1:
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.bool().mustNot( f.exists().field( "endDate" ) ) )
.fetchHits( 20 );
Or a simpler syntax with the upcoming release of Hibernate Search 6.2:
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.not( f.exists().field( "endDate" ) ) )
.fetchHits( 20 );
Note however that, as a purely negative query, this won't perform well.
For larger indexes, if you want decent performance, consider indexing null as a non-null value. See @GenericField(indexNullAs = ...)
(also available on other @*Field
annotations).
Basically you'll annotate your property with something like @GenericField(indexNullAs = "9999-12-31T23:59:59.999")
, then you'll create a query looking for the value LocalDateTime.parse("9999-12-31T23:59:59.999")
, and Hibernate Search will return all documents that had a null
value when indexed.
Old answer for Hibernate Search 5:
You should use @Field(indexNullAs = "...")
in your mapping. In your case you should set indexNullAs
to something like 9999-12-31T23:59:59.999
(last milisecond of the last day of year 9999).
See the documentation about Field.indexNullAs for more information.