in solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java [1212:1348]
public ProcessedFilter getProcessedFilter(List<Query> queries) throws IOException {
ProcessedFilter pf = new ProcessedFilter();
if (queries == null || queries.size() == 0) {
return pf;
}
// We combine all the filter queries that come from the filter cache into "answer".
// This might become pf.answer but not if there are any non-cached filters
DocSet answer = null;
boolean[] neg = new boolean[queries.size()];
DocSet[] sets = new DocSet[queries.size()];
List<ExtendedQuery> notCached = null;
List<PostFilter> postFilters = null;
int end = 0; // size of "sets" and "neg"; parallel arrays
for (Query q : queries) {
if (q instanceof ExtendedQuery eq) {
if (!eq.getCache()) {
if (eq.getCost() >= 100 && eq instanceof PostFilter) {
if (postFilters == null) postFilters = new ArrayList<>(sets.length - end);
postFilters.add((PostFilter) q);
} else {
if (notCached == null) notCached = new ArrayList<>(sets.length - end);
notCached.add((ExtendedQuery) q);
}
continue;
}
}
if (filterCache == null) {
// there is no cache: don't pull bitsets
if (notCached == null) notCached = new ArrayList<>(sets.length - end);
WrappedQuery uncached = new WrappedQuery(q);
uncached.setCache(false);
notCached.add(uncached);
continue;
}
Query posQuery = QueryUtils.getAbs(q);
DocSet docSet = getPositiveDocSet(posQuery);
// Negative query if absolute value different from original
if (Objects.equals(q, posQuery)) {
// keep track of the smallest positive set; use "answer" for this.
if (answer == null) {
answer = docSet;
continue;
}
// note: assume that size() is cached. It generally comes from the cache, so should be.
if (docSet.size() < answer.size()) {
// swap answer & docSet so that answer is smallest
DocSet tmp = answer;
answer = docSet;
docSet = tmp;
}
neg[end] = false;
} else {
neg[end] = true;
}
sets[end++] = docSet;
} // end of queries
if (end > 0) {
// Are all of our normal cached filters negative?
if (answer == null) {
answer = getLiveDocSet();
}
// This optimizes for the case where we have more than 2 filters and instead
// of copying the bitsets we make one mutable bitset. We should only do this
// for BitDocSet since it clones the backing bitset for andNot and intersection.
if (end > 1 && answer instanceof BitDocSet) {
answer = MutableBitDocSet.fromBitDocSet((BitDocSet) answer);
}
// do negative queries first to shrink set size
for (int i = 0; i < end; i++) {
if (neg[i]) answer = answer.andNot(sets[i]);
}
for (int i = 0; i < end; i++) {
if (!neg[i]) answer = answer.intersection(sets[i]);
}
// Make sure to keep answer as an immutable DocSet if we made it mutable
answer = MutableBitDocSet.unwrapIfMutable(answer);
}
// ignore "answer" if it simply matches all docs
if (answer != null && answer.size() == numDocs()) {
answer = null;
}
// answer is done.
// If no notCached nor postFilters, we can return now.
if (notCached == null && postFilters == null) {
// "answer" is the only part of the filter, so set it.
if (answer != null) {
pf.answer = answer;
pf.filter = answer.makeQuery();
}
return pf;
}
// pf.answer will remain null ... (our local "answer" var is not the complete answer)
// Set pf.filter based on combining "answer" and "notCached"
if (notCached == null) {
if (answer != null) {
pf.filter = answer.makeQuery();
}
} else {
notCached.sort(sortByCost); // pointless?
final BooleanQuery.Builder builder = new BooleanQuery.Builder();
if (answer != null) {
builder.add(answer.makeQuery(), Occur.FILTER);
}
for (ExtendedQuery eq : notCached) {
Query q = eq.getCostAppliedQuery();
builder.add(q, Occur.FILTER);
}
pf.filter = builder.build();
}
// Set pf.postFilter
if (postFilters != null) {
postFilters.sort(sortByCost);
for (int i = postFilters.size() - 1; i >= 0; i--) {
DelegatingCollector prev = pf.postFilter;
pf.postFilter = postFilters.get(i).getFilterCollector(this);
if (prev != null) pf.postFilter.setDelegate(prev);
}
}
return pf;
}