elasticsearchquerydslhibernate-search

Elastic Search / Hibernate Search : how to code xor operator with query DSL API in java?


I'am using hibernate search java API to generate an elastic search query. I have problem to code a xor operator. Typically I wrote :

private void applyXorOperator(BooleanPredicateClausesStep<?> boolClausesAppender,
                                  BooleanPredicateClausesStep<?> firstBool,
                                  BooleanPredicateClausesStep<?> secondBool) {

        boolClausesAppender.should(
        boolClausesAppender.must(firstBool).mustNot(secondBool));
        boolClausesAppender.should(
                boolClausesAppender.mustNot(firstBool).must(secondBool));
        boolClausesAppender.minimumShouldMatchNumber(1);
    }

How do I say "not both", I was expecting :

boolClausesAppender.maximumShouldMatchNumber(1);

but this method doesn't exist in the API. Any solution ?

Thx.


Solution

  • By definition:

    a XOR b <=> (a AND NOT b) OR (NOT a AND b)
    

    So just implement it that way:

    private void applyXorOperator(BooleanPredicateClausesStep<?> boolClausesAppender,
                                      BooleanPredicateClausesStep<?> firstBool,
                                      BooleanPredicateClausesStep<?> secondBool) {
        SearchPredicate a = firstBool.toPredicate();
        SearchPredicate b = secondBool.toPredicate();
    
        boolClausesAppender.should(f -> f.bool()
             // a AND NOT b
             .should(f.bool().must(a).mustNot(b))
             // OR (NOT a AND b)
             .should(f.bool().mustNot(a).must(b)));
    }
    

    EDIT: By the way, you don't need first/second to be boolean predicates. This would work too, and seems simpler:

    private void applyXorOperator(BooleanPredicateClausesStep<?> boolClausesAppender,
                                      PredicateFinalStep first,
                                      PredicateFinalStep second) {
        SearchPredicate a = first.toPredicate();
        SearchPredicate b = second.toPredicate();
    
        boolClausesAppender.should(f -> f.bool()
             // a AND NOT b
             .should(f.bool().must(a).mustNot(b))
             // OR (NOT a AND b)
             .should(f.bool().mustNot(a).must(b)));
    }
    

    EDIT: Also, starting with Hibernate Search 6.2 you can simply use .or(...)/.not(...), since in this case you don't need advanced bool features such as minShouldMatch or completely optional clauses that only affect the score.

    private void applyXorOperator(BooleanPredicateClausesStep<?> boolClausesAppender,
                                      PredicateFinalStep a,
                                      PredicateFinalStep b) {
        boolClausesAppender.should(f -> f.or(
             f.and(a, f.not(b)),
             f.and(f.not(a), b)));
    }