/* * 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.DocIdSet; import org.apache.lucene.search.Query; 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.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.QParser; import org.apache.solr.search.QueryParsing; import org.apache.solr.search.SolrCache; import org.apache.solr.search.SolrConstantScoreQuery; import org.apache.solr.search.SyntaxError; public class BlockJoinParentQParser extends QParser { /** implementation detail subject to change */ public static final String CACHE_NAME="perSegFilter"; protected String getParentFilterLocalParamName() { return "which"; } BlockJoinParentQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { super(qstr, localParams, params, req); } @Override public Query parse() throws SyntaxError { String filter = localParams.get(getParentFilterLocalParamName()); String scoreMode = localParams.get("score", ScoreMode.None.name()); QParser parentParser = subQuery(filter, null); Query parentQ = parentParser.getQuery(); String queryText = localParams.get(QueryParsing.V); // there is no child query, return parent filter from cache if (queryText == null || queryText.length()==0) { SolrConstantScoreQuery wrapped = new SolrConstantScoreQuery(getFilter(parentQ)); wrapped.setCache(false); return wrapped; } QParser childrenParser = subQuery(queryText, null); Query childrenQuery = childrenParser.getQuery(); return createQuery(parentQ, childrenQuery, scoreMode); } protected Query createQuery(final Query parentList, Query query, String scoreMode) throws SyntaxError { return new AllParentsAware(query, getFilter(parentList).filter, ScoreModeParser.parse(scoreMode), parentList); } BitDocIdSetFilterWrapper getFilter(Query parentList) { return getCachedFilter(req, parentList); } static BitDocIdSetFilterWrapper getCachedFilter(final SolrQueryRequest request, Query parentList) { SolrCache parentCache = request.getSearcher().getCache(CACHE_NAME); // lazily retrieve from solr cache Filter filter = null; if (parentCache != null) { filter = (Filter) parentCache.get(parentList); } BitDocIdSetFilterWrapper result; if (filter instanceof BitDocIdSetFilterWrapper) { result = (BitDocIdSetFilterWrapper) filter; } else { result = new BitDocIdSetFilterWrapper(createParentFilter(parentList)); if (parentCache != null) { parentCache.put(parentList, result); } } return result; } private static BitSetProducer createParentFilter(Query parentQ) { return new QueryBitSetProducer(parentQ); } 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; } } // We need this wrapper since BitDocIdSetFilter does not extend Filter static class BitDocIdSetFilterWrapper extends Filter { final BitSetProducer filter; 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); } @Override public String toString(String field) { return getClass().getSimpleName() + "(" + filter + ")"; } @Override public boolean equals(Object other) { return sameClassAs(other) && Objects.equals(filter, getClass().cast(other).filter); } @Override public int hashCode() { return classHash() + filter.hashCode(); } } }