blob: 151062f2d10b8cc5ba3b91aadfc3ad1d426c7d42 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.search.join;
import java.io.IOException;
import java.util.Objects;
import org.apache.lucene.index.LeafReaderContext;
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.BitSet;
import org.apache.lucene.util.BitSetIterator;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.search.ExtendedQueryBase;
import org.apache.solr.search.QParser;
import org.apache.solr.search.SolrCache;
import org.apache.solr.search.SyntaxError;
public class BlockJoinParentQParser extends FiltersQParser {
/** implementation detail subject to change */
public static final String CACHE_NAME="perSegFilter";
protected String getParentFilterLocalParamName() {
return "which";
}
@Override
protected String getFiltersParamName() {
return "filters";
}
BlockJoinParentQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
super(qstr, localParams, params, req);
}
protected Query parseParentFilter() throws SyntaxError {
String filter = localParams.get(getParentFilterLocalParamName());
QParser parentParser = subQuery(filter, null);
Query parentQ = parentParser.getQuery();
return parentQ;
}
@Override
protected Query wrapSubordinateClause(Query subordinate) throws SyntaxError {
String scoreMode = localParams.get("score", ScoreMode.None.name());
Query parentQ = parseParentFilter();
return createQuery(parentQ, subordinate, scoreMode);
}
@Override
protected Query noClausesQuery() throws SyntaxError {
return new BitSetProducerQuery(getBitSetProducer(parseParentFilter()));
}
protected Query createQuery(final Query parentList, Query query, String scoreMode) throws SyntaxError {
return new AllParentsAware(query, getBitSetProducer(parentList), ScoreModeParser.parse(scoreMode), parentList);
}
BitSetProducer getBitSetProducer(Query query) {
return getCachedBitSetProducer(req, query);
}
public static BitSetProducer getCachedBitSetProducer(final SolrQueryRequest request, Query query) {
@SuppressWarnings("unchecked")
SolrCache<Query, BitSetProducer> parentCache = request.getSearcher().getCache(CACHE_NAME);
// lazily retrieve from solr cache
if (parentCache != null) {
return parentCache.computeIfAbsent(query, QueryBitSetProducer::new);
} else {
return new QueryBitSetProducer(query);
}
}
static final class AllParentsAware extends ToParentBlockJoinQuery {
private final Query parentQuery;
private AllParentsAware(Query childQuery, BitSetProducer parentsFilter, ScoreMode scoreMode,
Query parentList) {
super(childQuery, parentsFilter, scoreMode);
parentQuery = parentList;
}
public Query getParentQuery(){
return parentQuery;
}
}
/** A constant score query based on a {@link BitSetProducer}. */
static class BitSetProducerQuery extends ExtendedQueryBase {
final BitSetProducer bitSetProducer;
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() + "(" + bitSetProducer + ")";
}
@Override
public boolean equals(Object other) {
return sameClassAs(other) && Objects.equals(bitSetProducer, getClass().cast(other).bitSetProducer);
}
@Override
public int 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();
}
};
}
}
}