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.search.Query;
import org.apache.lucene.util.BitSet; 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 { public class UniqueBlockQueryAgg extends UniqueBlockAgg {
@ -42,7 +42,7 @@ public class UniqueBlockQueryAgg extends UniqueBlockAgg {
@Override @Override
public void setNextReader(LeafReaderContext readerContext) throws IOException { 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 @Override

View File

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

View File

@ -20,22 +20,25 @@ import java.io.IOException;
import java.util.Objects; import java.util.Objects;
import org.apache.lucene.index.LeafReaderContext; 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.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.BitSetProducer;
import org.apache.lucene.search.join.QueryBitSetProducer; import org.apache.lucene.search.join.QueryBitSetProducer;
import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ScoreMode;
import org.apache.lucene.search.join.ToParentBlockJoinQuery; import org.apache.lucene.search.join.ToParentBlockJoinQuery;
import org.apache.lucene.util.BitDocIdSet;
import org.apache.lucene.util.BitSet; 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.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.search.BitsFilteredDocIdSet; import org.apache.solr.search.ExtendedQueryBase;
import org.apache.solr.search.Filter;
import org.apache.solr.search.QParser; import org.apache.solr.search.QParser;
import org.apache.solr.search.SolrCache; import org.apache.solr.search.SolrCache;
import org.apache.solr.search.SolrConstantScoreQuery;
import org.apache.solr.search.SyntaxError; import org.apache.solr.search.SyntaxError;
public class BlockJoinParentQParser extends FiltersQParser { public class BlockJoinParentQParser extends FiltersQParser {
@ -71,42 +74,26 @@ public class BlockJoinParentQParser extends FiltersQParser {
@Override @Override
protected Query noClausesQuery() throws SyntaxError { protected Query noClausesQuery() throws SyntaxError {
SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(getFilter(parseParentFilter())); return new BitSetProducerQuery(getBitSetProducer(parseParentFilter()));
wrapped.setCache(false);
return wrapped;
} }
protected Query createQuery(final Query parentList, Query query, String scoreMode) throws SyntaxError { 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) { BitSetProducer getBitSetProducer(Query query) {
return getCachedFilter(req, parentList); return getCachedBitSetProducer(req, query);
} }
public static BitDocIdSetFilterWrapper getCachedFilter(final SolrQueryRequest request, Query parentList) { public static BitSetProducer getCachedBitSetProducer(final SolrQueryRequest request, Query query) {
@SuppressWarnings("unchecked") @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 // lazily retrieve from solr cache
BitDocIdSetFilterWrapper result;
if (parentCache != null) { if (parentCache != null) {
Filter filter = parentCache.computeIfAbsent(parentList, return parentCache.computeIfAbsent(query, QueryBitSetProducer::new);
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);
}
} else { } 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 { 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 /** A constant score query based on a {@link BitSetProducer}. */
public static class BitDocIdSetFilterWrapper extends Filter { static class BitSetProducerQuery extends ExtendedQueryBase {
private final BitSetProducer filter; final BitSetProducer bitSetProducer;
BitDocIdSetFilterWrapper(BitSetProducer filter) { public BitSetProducerQuery(BitSetProducer bitSetProducer) {
this.filter = filter; this.bitSetProducer = bitSetProducer;
} setCache(false); // because we assume the bitSetProducer is itself cached
@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;
} }
@Override @Override
public String toString(String field) { public String toString(String field) {
return getClass().getSimpleName() + "(" + filter + ")"; return getClass().getSimpleName() + "(" + bitSetProducer + ")";
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return sameClassAs(other) && return sameClassAs(other) && Objects.equals(bitSetProducer, getClass().cast(other).bitSetProducer);
Objects.equals(filter, getClass().cast(other).getFilter());
} }
@Override @Override
public int hashCode() { 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; bjQ = (AllParentsAware) query;
parentFilter = BlockJoinParentQParser.getCachedFilter(fp.getReq(), bjQ.getParentQuery()).getFilter(); parentFilter = BlockJoinParentQParser.getCachedBitSetProducer(fp.getReq(), bjQ.getParentQuery());
childFilter = BlockJoinParentQParser.getCachedFilter(fp.getReq(), bjQ.getChildQuery()).getFilter(); childFilter = BlockJoinParentQParser.getCachedBitSetProducer(fp.getReq(), bjQ.getChildQuery());
if (sortFieldName==null || sortFieldName.equals("")) { if (sortFieldName==null || sortFieldName.equals("")) {
throw new SyntaxError ("field is omitted in "+fp.getString()); 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")); TermQuery childQuery = new TermQuery(new Term("child_s", "l"));
Query parentQuery = new WildcardQuery(new Term("parent_s", "*")); Query parentQuery = new WildcardQuery(new Term("parent_s", "*"));
ToParentBlockJoinQuery tpbjq = new ToParentBlockJoinQuery(childQuery, 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); Assert.assertEquals(6, req.getSearcher().search(tpbjq,10).totalHits.value);
} }
} }