Search code examples
hibernate-searchhibernate-search-6

Hibernate Search: Find in list of intervals


I am using Hibernate Search 6.x within my Spring Boot application. I got an indexed entity with a set of date intervals.

@Indexed
public class ProudParent {
   ...
   @IndexedEmbedded(includePaths = {"start", "end"})
   Set<DateInterval> parentalLeaves;
}

And the class DateInterval looks like

@Embeddable
public class DateInterval {
    
    @GenericField
    LocalDate start;

    @GenericField
    LocalDate end;
}

That is my range query to search for all proud parents which were in parental leave on a specific date:

bool.must(factory.range().field("parentalLeaves.start").lessThan(date));
bool.must(factory.range().field("parentalLeaves.end").greaterThan(date));

Now the problem is that it finds proud parents which first parental leave was before date and the last parental leave was after date. So it does not search in the "same" date interval.

Can someone give me a hint of what I'm doing wrong?

Regards, Rokko


Solution

  • @IndexedEmbedded will "flatten" the object structure by default, so all starts and ends are mixed together in the index, with no way to know which end matches which start.

    You can read more about this in this section of the reference documentation. In short, this default is mainly for performance, because preserving the object structure can be (very) costly on large indexes, and is usually not necessary.

    The solution, in your case, would be to use a NESTED structure:

    @Indexed
    public class ProudParent {
       ...
       @IndexedEmbedded(includePaths = {"start", "end"}, structure = ObjectStructure.NESTED)
       Set<DateInterval> parentalLeaves;
    }
    

    Then make sure to tell Hibernate Search you want the matches on start and end to be on the same parentalLeave, using a nested predicate:

    bool.must(factory.nested().objectField("parentalLeaves") 
            .nest(factory.bool()
                    .must(factory.range()
                            .field("parentalLeaves.start")
                            .lessThan(date)) 
                    .must(factory.range()
                            .field("parentalLeaves.end")
                            .greaterThan(date)))
    

    Don't forget to reindex your data before testing.


    Side note: the syntax should be slightly more convenient with the upcoming Hibernate Search 6.2.0.Alpha2 (not yet released):

    bool.must(factory.nested("parentalLeaves") 
            .add(factory.range()
                    .field("parentalLeaves.start")
                    .lessThan(date)) 
            .add(factory.range()
                    .field("parentalLeaves.end")
                    .greaterThan(date)))
    

    Other side note: hopefully one day we'll have date ranges as a primitive type in Hibernate Search (HSEARCH-4199) and this whole discussion will become irrelevant :)