| package org.apache.blur.lucene.search; |
| |
| /** |
| * 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. |
| */ |
| import java.io.IOException; |
| import java.util.Arrays; |
| |
| import org.apache.blur.log.Log; |
| import org.apache.blur.log.LogFactory; |
| import org.apache.lucene.index.AtomicReaderContext; |
| import org.apache.lucene.index.IndexReader; |
| import org.apache.lucene.search.Explanation; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.Scorer; |
| import org.apache.lucene.search.Weight; |
| import org.apache.lucene.util.Bits; |
| import org.apache.lucene.util.OpenBitSet; |
| |
| public class FacetQuery extends AbstractWrapperQuery { |
| |
| private static final Log LOG = LogFactory.getLog(FacetQuery.class); |
| |
| private final Query[] _facets; |
| private final FacetExecutor _executor; |
| |
| public FacetQuery(Query query, Query[] facets, FacetExecutor executor) { |
| super(query, false); |
| _facets = facets; |
| _executor = executor; |
| } |
| |
| public FacetQuery(Query query, Query[] facets, FacetExecutor executor, boolean rewritten) { |
| super(query, rewritten); |
| _facets = facets; |
| _executor = executor; |
| } |
| |
| public String toString() { |
| return "facet:{" + _query.toString() + "}"; |
| } |
| |
| public String toString(String field) { |
| return "facet:{" + _query.toString(field) + "}"; |
| } |
| |
| public Query[] getFacets() { |
| return _facets; |
| } |
| |
| @Override |
| public Query clone() { |
| return new FacetQuery((Query) _query.clone(), _facets, _executor, _rewritten); |
| } |
| |
| @Override |
| public Query rewrite(IndexReader reader) throws IOException { |
| if (_rewritten) { |
| return this; |
| } |
| Query[] facets = new Query[_facets.length]; |
| for (int i = 0; i < _facets.length; i++) { |
| facets[i] = _facets[i].rewrite(reader); |
| } |
| return new FacetQuery(_query.rewrite(reader), facets, _executor, true); |
| } |
| |
| @Override |
| public Weight createWeight(IndexSearcher searcher) throws IOException { |
| Weight weight = _query.createWeight(searcher); |
| return new FacetWeight(weight, getWeights(searcher), _executor); |
| } |
| |
| private Weight[] getWeights(IndexSearcher searcher) throws IOException { |
| Weight[] weights = new Weight[_facets.length]; |
| for (int i = 0; i < weights.length; i++) { |
| weights[i] = _facets[i].createWeight(searcher); |
| } |
| return weights; |
| } |
| |
| public static class FacetWeight extends Weight { |
| |
| private final Weight _weight; |
| private final Weight[] _facets; |
| private FacetExecutor _executor; |
| |
| public FacetWeight(Weight weight, Weight[] facets, FacetExecutor executor) { |
| _weight = weight; |
| _facets = facets; |
| _executor = executor; |
| } |
| |
| @Override |
| public Explanation explain(AtomicReaderContext reader, int doc) throws IOException { |
| return _weight.explain(reader, doc); |
| } |
| |
| @Override |
| public Query getQuery() { |
| return _weight.getQuery(); |
| } |
| |
| @Override |
| public void normalize(float norm, float topLevelBoost) { |
| _weight.normalize(norm, topLevelBoost); |
| } |
| |
| @Override |
| public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, Bits acceptDocs) |
| throws IOException { |
| Scorer scorer = _weight.scorer(context, true, topScorer, acceptDocs); |
| if (scorer == null) { |
| return null; |
| } |
| if (!_executor.scorersAlreadyAdded(context)) { |
| Scorer[] scorers = getScorers(context, true, topScorer, acceptDocs); |
| LOG.debug(_executor.getPrefix("Adding scorers for context [{0}] scorers [{1}]"), context, |
| Arrays.asList(scorers)); |
| _executor.addScorers(context, scorers); |
| } else { |
| LOG.debug(_executor.getPrefix("Scorers already added for context [{0}]"), context); |
| } |
| return new FacetScorer(scorer, _executor, context); |
| } |
| |
| private Scorer[] getScorers(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, |
| Bits acceptDocs) throws IOException { |
| Scorer[] scorers = new Scorer[_facets.length]; |
| for (int i = 0; i < scorers.length; i++) { |
| scorers[i] = _facets[i].scorer(context, scoreDocsInOrder, topScorer, acceptDocs); |
| } |
| return scorers; |
| } |
| |
| @Override |
| public float getValueForNormalization() throws IOException { |
| return _weight.getValueForNormalization(); |
| } |
| } |
| |
| public static class FacetScorer extends Scorer { |
| |
| private static final Log LOG = LogFactory.getLog(FacetScorer.class); |
| |
| private final Scorer _baseScorer; |
| private final OpenBitSet _hit; |
| |
| public FacetScorer(Scorer scorer, FacetExecutor executor, AtomicReaderContext context) throws IOException { |
| super(scorer.getWeight()); |
| _baseScorer = scorer; |
| _hit = executor.getBitSet(context); |
| } |
| |
| private int processFacets(int doc) throws IOException { |
| if (doc == NO_MORE_DOCS) { |
| return doc; |
| } |
| if (doc < 0) { |
| LOG.error("DocId from base scorer < 0 [{0}]", _baseScorer); |
| return doc; |
| } |
| _hit.fastSet(doc); |
| return doc; |
| } |
| |
| @Override |
| public float score() throws IOException { |
| return _baseScorer.score(); |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| return processFacets(_baseScorer.advance(target)); |
| } |
| |
| @Override |
| public int docID() { |
| return _baseScorer.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| return processFacets(_baseScorer.nextDoc()); |
| } |
| |
| @Override |
| public int freq() throws IOException { |
| return _baseScorer.freq(); |
| } |
| |
| @Override |
| public long cost() { |
| return _baseScorer.cost(); |
| } |
| } |
| } |