mirror of https://github.com/apache/lucene.git
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:
parent
22cb4d4d95
commit
a6a02ac0e5
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue