@Override
public SenseiDocIdSet getSenseiDocIdSet(IndexReader reader) throws IOException {
if (reader instanceof BoboIndexReader) {
BoboIndexReader boboReader = (BoboIndexReader)reader;
FacetHandler facetHandler = (FacetHandler)boboReader.getFacetHandler(_name);
Object obj = null;
String[] vals = _vals;
String[] nots = _not;
List<String> optimizedVals = new ArrayList<String>(vals.length);
List<String> optimizedNots = new ArrayList<String>(nots.length);
int maxDoc = reader.maxDoc();
if ( (vals == null || vals.length == 0) && (nots == null || nots.length == 0) ) {
// Bobo madness part 2: no vals and no nots will match nothing, regardless of isAnd.
return SenseiDocIdSet.buildMatchNone(planString("TRIVIAL", vals, nots, optimizedVals, optimizedNots));
}
// No facetHandler == no cardinality info.
DocIdSetCardinality totalDocIdSetCardinality = null;
String planType = "FACETED NOFACETDATA";
if(facetHandler == null) {
if (logger.isDebugEnabled()) {
logger.debug("not facet support, default to term filter: "+_name);
}
DocIdSet docIdSet = buildLuceneDefaultDocIdSet(boboReader, _name, vals, nots, _isAnd);
// No cardinality since we don't have the facet data and because Lucene's TermDocs is
// too expensive to justify calling
return new SenseiDocIdSet(docIdSet, DocIdSetCardinality.random(), planString("NOFACET LUCENE", vals, nots, optimizedVals, optimizedNots));
} else if (facetHandler instanceof UIDFacetHandler) {
planType = "FACET UID";
if (vals.length != 0) {
// We *could* look up all the ranges right now and see if there's any one even there. This would greatly
// speed up empty _uid queries, but I've never seen one of those.
totalDocIdSetCardinality = DocIdSetCardinality.exactRange(0, 1, maxDoc + 1);
} else {
totalDocIdSetCardinality = DocIdSetCardinality.zero();
}
if (nots.length != 0) {
totalDocIdSetCardinality.andWith(DocIdSetCardinality.exactRange(maxDoc + 1 - nots.length, maxDoc + 1, maxDoc + 1));
}
} else {
obj = facetHandler.getFacetData(boboReader);
if (obj != null && obj instanceof FacetDataCache) {
planType = "FACETED";
FacetDataCache facetData = (FacetDataCache)obj;
TermValueList valArray = facetData.valArray;
int[] freqs = facetData.freqs;
// Total cardinality = AND/OR(val1, val2, ...) AND NOT (OR(not1, not2))
totalDocIdSetCardinality = _isAnd ? DocIdSetCardinality.one() : DocIdSetCardinality.zero();
vals = getValsByFrequency(vals, freqs, maxDoc, totalDocIdSetCardinality, valArray, optimizedVals, _isAnd);
DocIdSetCardinality notDocIdSetCardinality = DocIdSetCardinality.zero();
nots = getValsByFrequency(nots, freqs, maxDoc, notDocIdSetCardinality, valArray, optimizedNots, false);
notDocIdSetCardinality.invert();
totalDocIdSetCardinality.andWith(notDocIdSetCardinality);
// If we optimized it out completely, return trivial sets. This is mostly there to deal with weird
// semantics for empty-match filters in Bobo.
if (totalDocIdSetCardinality.isOne()) {
return SenseiDocIdSet.buildMatchAll(reader, planString("FACET TRIVIAL", vals, nots, optimizedVals, optimizedNots));
} else if (totalDocIdSetCardinality.isZero()) {
return SenseiDocIdSet.buildMatchNone(planString("FACET TRIVIAL", vals, nots, optimizedVals, optimizedNots));
}
if(_noAutoOptimize) {
DocIdSet docIdSet = buildLuceneDefaultDocIdSet(boboReader,
_name,
vals,
nots,
_isAnd);
return new SenseiDocIdSet(docIdSet, totalDocIdSetCardinality, planString("DE-OPTIMIZED LUCENE", vals, nots, optimizedVals, optimizedNots));
}
}
}
// we get to optimize using facets
BrowseSelection sel = new BrowseSelection(_name);
sel.setValues(vals);
if (nots != null)
sel.setNotValues(nots);
if (_isAnd) {
sel.setSelectionOperation(ValueOperation.ValueOperationAnd);
} else {
sel.setSelectionOperation(ValueOperation.ValueOperationOr);
}
RandomAccessFilter filter = facetHandler.buildFilter(sel);
if (filter == null)
filter = EmptyFilter.getInstance();
// If we don't have an cardinality estimate, ask Bobo.
if (totalDocIdSetCardinality == null) {