SOLR-14494: Refactor BlockJoin to not use Filter (#1523)

Note: henceforth the perSegFilter cache will internally have values of type BitSetProducer instead of Filter.
This commit is contained in:
David Smiley 2020-05-29 21:44:44 -04:00 committed by GitHub
parent 22cb4d4d95
commit a6a02ac0e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 68 deletions

View File

@ -25,7 +25,7 @@ import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BitSet;
import static org.apache.solr.search.join.BlockJoinParentQParser.getCachedFilter;
import static org.apache.solr.search.join.BlockJoinParentQParser.getCachedBitSetProducer;
public class UniqueBlockQueryAgg extends UniqueBlockAgg {
@ -42,7 +42,7 @@ public class UniqueBlockQueryAgg extends UniqueBlockAgg {
@Override
public void setNextReader(LeafReaderContext readerContext) throws IOException {
this.parentBitSet = getCachedFilter(fcontext.req, query).getFilter().getBitSet(readerContext);
this.parentBitSet = getCachedBitSetProducer(fcontext.req, query).getBitSet(readerContext);
}
@Override

View File

@ -23,7 +23,6 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ToChildBlockJoinQuery;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.search.SolrConstantScoreQuery;
import org.apache.solr.search.SyntaxError;
public class BlockJoinChildQParser extends BlockJoinParentQParser {
@ -34,7 +33,7 @@ public class BlockJoinChildQParser extends BlockJoinParentQParser {
@Override
protected Query createQuery(Query parentListQuery, Query query, String scoreMode) {
return new ToChildBlockJoinQuery(query, getFilter(parentListQuery).getFilter());
return new ToChildBlockJoinQuery(query, getBitSetProducer(parentListQuery));
}
@Override
@ -49,8 +48,6 @@ public class BlockJoinChildQParser extends BlockJoinParentQParser {
.add(new MatchAllDocsQuery(), Occur.MUST)
.add(parents, Occur.MUST_NOT)
.build();
SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(getFilter(notParents));
wrapped.setCache(false);
return wrapped;
return new BitSetProducerQuery(getBitSetProducer(notParents));
}
}

View File

@ -20,22 +20,25 @@ import java.io.IOException;
import java.util.Objects;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.ConstantScoreScorer;
import org.apache.lucene.search.ConstantScoreWeight;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryVisitor;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.search.join.BitSetProducer;
import org.apache.lucene.search.join.QueryBitSetProducer;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
import org.apache.lucene.util.BitDocIdSet;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BitSetIterator;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.search.BitsFilteredDocIdSet;
import org.apache.solr.search.Filter;
import org.apache.solr.search.ExtendedQueryBase;
import org.apache.solr.search.QParser;
import org.apache.solr.search.SolrCache;
import org.apache.solr.search.SolrConstantScoreQuery;
import org.apache.solr.search.SyntaxError;
public class BlockJoinParentQParser extends FiltersQParser {
@ -71,42 +74,26 @@ public class BlockJoinParentQParser extends FiltersQParser {
@Override
protected Query noClausesQuery() throws SyntaxError {
SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(getFilter(parseParentFilter()));
wrapped.setCache(false);
return wrapped;
return new BitSetProducerQuery(getBitSetProducer(parseParentFilter()));
}
protected Query createQuery(final Query parentList, Query query, String scoreMode) throws SyntaxError {
return new AllParentsAware(query, getFilter(parentList).filter, ScoreModeParser.parse(scoreMode), parentList);
return new AllParentsAware(query, getBitSetProducer(parentList), ScoreModeParser.parse(scoreMode), parentList);
}
BitDocIdSetFilterWrapper getFilter(Query parentList) {
return getCachedFilter(req, parentList);
BitSetProducer getBitSetProducer(Query query) {
return getCachedBitSetProducer(req, query);
}
public static BitDocIdSetFilterWrapper getCachedFilter(final SolrQueryRequest request, Query parentList) {
public static BitSetProducer getCachedBitSetProducer(final SolrQueryRequest request, Query query) {
@SuppressWarnings("unchecked")
SolrCache<Query, Filter> parentCache = request.getSearcher().getCache(CACHE_NAME);
SolrCache<Query, BitSetProducer> parentCache = request.getSearcher().getCache(CACHE_NAME);
// lazily retrieve from solr cache
BitDocIdSetFilterWrapper result;
if (parentCache != null) {
Filter filter = parentCache.computeIfAbsent(parentList,
query -> new BitDocIdSetFilterWrapper(createParentFilter(query)));
if (filter instanceof BitDocIdSetFilterWrapper) {
result = (BitDocIdSetFilterWrapper) filter;
} else {
result = new BitDocIdSetFilterWrapper(createParentFilter(parentList));
// non-atomic update of existing entry to ensure strong-typing
parentCache.put(parentList, result);
}
return parentCache.computeIfAbsent(query, QueryBitSetProducer::new);
} else {
result = new BitDocIdSetFilterWrapper(createParentFilter(parentList));
return new QueryBitSetProducer(query);
}
return result;
}
private static BitSetProducer createParentFilter(Query parentQ) {
return new QueryBitSetProducer(parentQ);
}
static final class AllParentsAware extends ToParentBlockJoinQuery {
@ -123,49 +110,55 @@ public class BlockJoinParentQParser extends FiltersQParser {
}
}
// We need this wrapper since BitDocIdSetFilter does not extend Filter
public static class BitDocIdSetFilterWrapper extends Filter {
/** A constant score query based on a {@link BitSetProducer}. */
static class BitSetProducerQuery extends ExtendedQueryBase {
private final BitSetProducer filter;
final BitSetProducer bitSetProducer;
BitDocIdSetFilterWrapper(BitSetProducer filter) {
this.filter = filter;
}
@Override
public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
BitSet set = filter.getBitSet(context);
if (set == null) {
return null;
}
return BitsFilteredDocIdSet.wrap(new BitDocIdSet(set), acceptDocs);
}
public BitSetProducer getFilter() {
return filter;
public BitSetProducerQuery(BitSetProducer bitSetProducer) {
this.bitSetProducer = bitSetProducer;
setCache(false); // because we assume the bitSetProducer is itself cached
}
@Override
public String toString(String field) {
return getClass().getSimpleName() + "(" + filter + ")";
return getClass().getSimpleName() + "(" + bitSetProducer + ")";
}
@Override
public boolean equals(Object other) {
return sameClassAs(other) &&
Objects.equals(filter, getClass().cast(other).getFilter());
return sameClassAs(other) && Objects.equals(bitSetProducer, getClass().cast(other).bitSetProducer);
}
@Override
public int hashCode() {
return classHash() + filter.hashCode();
return classHash() + bitSetProducer.hashCode();
}
@Override
public void visit(QueryVisitor visitor) {
visitor.visitLeaf(this);
}
@Override
public Weight createWeight(IndexSearcher searcher, org.apache.lucene.search.ScoreMode scoreMode, float boost) throws IOException {
return new ConstantScoreWeight(BitSetProducerQuery.this, boost) {
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
BitSet bitSet = bitSetProducer.getBitSet(context);
if (bitSet == null) {
return null;
}
DocIdSetIterator disi = new BitSetIterator(bitSet, bitSet.approximateCardinality());
return new ConstantScoreScorer(this, boost, scoreMode, disi);
}
@Override
public boolean isCacheable(LeafReaderContext ctx) {
return getCache();
}
};
}
}
}

View File

@ -180,8 +180,8 @@ public class ChildFieldValueSourceParser extends ValueSourceParser {
}
bjQ = (AllParentsAware) query;
parentFilter = BlockJoinParentQParser.getCachedFilter(fp.getReq(), bjQ.getParentQuery()).getFilter();
childFilter = BlockJoinParentQParser.getCachedFilter(fp.getReq(), bjQ.getChildQuery()).getFilter();
parentFilter = BlockJoinParentQParser.getCachedBitSetProducer(fp.getReq(), bjQ.getParentQuery());
childFilter = BlockJoinParentQParser.getCachedBitSetProducer(fp.getReq(), bjQ.getChildQuery());
if (sortFieldName==null || sortFieldName.equals("")) {
throw new SyntaxError ("field is omitted in "+fp.getString());

View File

@ -46,7 +46,7 @@ public class BJQFilterAccessibleTest extends SolrTestCaseJ4 {
TermQuery childQuery = new TermQuery(new Term("child_s", "l"));
Query parentQuery = new WildcardQuery(new Term("parent_s", "*"));
ToParentBlockJoinQuery tpbjq = new ToParentBlockJoinQuery(childQuery,
BlockJoinParentQParser.getCachedFilter(req,parentQuery).getFilter(), ScoreMode.Max);
BlockJoinParentQParser.getCachedBitSetProducer(req,parentQuery), ScoreMode.Max);
Assert.assertEquals(6, req.getSearcher().search(tpbjq,10).totalHits.value);
}
}