2

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?

Turzo
  • 490
  • 1
  • 5
  • 14

1 Answers1

4

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.

yrodiere
  • 9,280
  • 1
  • 13
  • 35
  • I managed to circumvent the issue by playing with the logic a little bit like this: `queryBuilder.bool().must(queryBuilder.range().onField("endDate").below(LocalDateTime.now()).createQuery()).not().createQuery();` However, your answer correctly solves the issue without any workarounds. Thank you very much! – Turzo Jul 11 '18 at 10:57