if (clauses.size() == 1) {
FilterClause clause = clauses.get(0);
DocIdSet set = clause.getFilter().getDocIdSet(context, acceptDocs);
if (clause.getOccur() == Occur.MUST_NOT) {
if (DocIdSets.isEmpty(set)) {
return new AllDocIdSet(maxDoc);
} else {
return new NotDocIdSet(set, maxDoc);
}
}
// SHOULD or MUST, just return the set...
if (DocIdSets.isEmpty(set)) {
return null;
}
return set;
}
// We have several clauses, try to organize things to make it easier to process
List<DocIdSetIterator> shouldIterators = new ArrayList<>();
List<Bits> shouldBits = new ArrayList<>();
boolean hasShouldClauses = false;
List<DocIdSetIterator> requiredIterators = new ArrayList<>();
List<DocIdSetIterator> excludedIterators = new ArrayList<>();
List<Bits> requiredBits = new ArrayList<>();
List<Bits> excludedBits = new ArrayList<>();
for (FilterClause clause : clauses) {
DocIdSet set = clause.getFilter().getDocIdSet(context, null);
DocIdSetIterator it = null;
Bits bits = null;
if (DocIdSets.isEmpty(set) == false) {
it = set.iterator();
if (it != null) {
bits = set.bits();
}
}
switch (clause.getOccur()) {
case SHOULD:
hasShouldClauses = true;
if (it == null) {
// continue, but we recorded that there is at least one should clause
// so that if all iterators are null we know that nothing matches this
// filter since at least one SHOULD clause needs to match
} else if (bits != null && DocIdSets.isBroken(it)) {
shouldBits.add(bits);
} else {
shouldIterators.add(it);
}
break;
case MUST:
if (it == null) {
// no documents matched a clause that is compulsory, then nothing matches at all
return null;
} else if (bits != null && DocIdSets.isBroken(it)) {
requiredBits.add(bits);
} else {
requiredIterators.add(it);
}
break;
case MUST_NOT:
if (it == null) {
// ignore
} else if (bits != null && DocIdSets.isBroken(it)) {
excludedBits.add(bits);
} else {
excludedIterators.add(it);
}
break;
default:
throw new AssertionError();
}
}
// Since BooleanFilter requires that at least one SHOULD clause matches,
// transform the SHOULD clauses into a MUST clause
if (hasShouldClauses) {
if (shouldIterators.isEmpty() && shouldBits.isEmpty()) {
// we had should clauses, but they all produced empty sets
// yet BooleanFilter requires that at least one clause matches
// so it means we do not match anything
return null;
} else if (shouldIterators.size() == 1 && shouldBits.isEmpty()) {
requiredIterators.add(shouldIterators.get(0));
} else {
// apply high-cardinality should clauses first
CollectionUtil.timSort(shouldIterators, COST_DESCENDING);
BitDocIdSet.Builder shouldBuilder = null;
for (DocIdSetIterator it : shouldIterators) {
if (shouldBuilder == null) {
shouldBuilder = new BitDocIdSet.Builder(maxDoc);
}
shouldBuilder.or(it);
}
if (shouldBuilder != null && shouldBits.isEmpty() == false) {
// we have both iterators and bits, there is no way to compute
// the union efficiently, so we just transform the iterators into
// bits
// add first since these are fast bits
shouldBits.add(0, shouldBuilder.build().bits());
shouldBuilder = null;
}
if (shouldBuilder == null) {
// only bits
assert shouldBits.size() >= 1;
if (shouldBits.size() == 1) {
requiredBits.add(shouldBits.get(0));
} else {
requiredBits.add(new OrBits(shouldBits.toArray(new Bits[shouldBits.size()])));
}
} else {
assert shouldBits.isEmpty();
// only iterators, we can add the merged iterator to the list of required iterators
requiredIterators.add(shouldBuilder.build().iterator());
}
}
} else {
assert shouldIterators.isEmpty();
assert shouldBits.isEmpty();
}
// From now on, we don't have to care about SHOULD clauses anymore since we upgraded
// them to required clauses (if necessary)
// cheap iterators first to make intersection faster
CollectionUtil.timSort(requiredIterators, COST_ASCENDING);
CollectionUtil.timSort(excludedIterators, COST_ASCENDING);
// Intersect iterators
BitDocIdSet.Builder res = null;
for (DocIdSetIterator iterator : requiredIterators) {
if (res == null) {
res = new BitDocIdSet.Builder(maxDoc);
res.or(iterator);
} else {
res.and(iterator);
}
}
for (DocIdSetIterator iterator : excludedIterators) {
if (res == null) {
res = new BitDocIdSet.Builder(maxDoc, true);
}
res.andNot(iterator);
}
// Transform the excluded bits into required bits
if (excludedBits.isEmpty() == false) {
Bits excluded;
if (excludedBits.size() == 1) {
excluded = excludedBits.get(0);
} else {
excluded = new OrBits(excludedBits.toArray(new Bits[excludedBits.size()]));
}
requiredBits.add(new NotDocIdSet.NotBits(excluded));
}
// The only thing left to do is to intersect 'res' with 'requiredBits'
// the main doc id set that will drive iteration
DocIdSet main;
if (res == null) {
main = new AllDocIdSet(maxDoc);
} else {
main = res.build();
}
// apply accepted docs and compute the bits to filter with