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.
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)));
}