X-Git-Url: https://git.mdrn.pl/pylucene.git/blobdiff_plain/a2e61f0c04805cfcb8706176758d1283c7e3a55c..aaeed5504b982cf3545252ab528713250aa33eed:/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/TopFieldCollector.java diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/TopFieldCollector.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/TopFieldCollector.java new file mode 100644 index 0000000..76f9b71 --- /dev/null +++ b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/TopFieldCollector.java @@ -0,0 +1,999 @@ +package org.apache.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 org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.FieldValueHitQueue.Entry; +import org.apache.lucene.util.PriorityQueue; + +/** + * A {@link Collector} that sorts by {@link SortField} using + * {@link FieldComparator}s. + *

+ * See the {@link #create(org.apache.lucene.search.Sort, int, boolean, boolean, boolean, boolean)} method + * for instantiating a TopFieldCollector. + * + * @lucene.experimental + */ +public abstract class TopFieldCollector extends TopDocsCollector { + + // TODO: one optimization we could do is to pre-fill + // the queue with sentinel value that guaranteed to + // always compare lower than a real hit; this would + // save having to check queueFull on each insert + + /* + * Implements a TopFieldCollector over one SortField criteria, without + * tracking document scores and maxScore. + */ + private static class OneComparatorNonScoringCollector extends + TopFieldCollector { + + final FieldComparator comparator; + final int reverseMul; + + public OneComparatorNonScoringCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + comparator = queue.getComparators()[0]; + reverseMul = queue.getReverseMul()[0]; + } + + final void updateBottom(int doc) { + // bottom.score is already set to Float.NaN in add(). + bottom.doc = docBase + doc; + bottom = pq.updateTop(); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + if ((reverseMul * comparator.compareBottom(doc)) <= 0) { + // since docs are visited in doc Id order, if compare is 0, it means + // this document is largest than anything else in the queue, and + // therefore not competitive. + return; + } + + // This hit is competitive - replace bottom element in queue & adjustTop + comparator.copy(bottom.slot, doc); + updateBottom(doc); + comparator.setBottom(bottom.slot); + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + comparator.copy(slot, doc); + add(slot, doc, Float.NaN); + if (queueFull) { + comparator.setBottom(bottom.slot); + } + } + } + + @Override + public void setNextReader(IndexReader reader, int docBase) throws IOException { + this.docBase = docBase; + comparator.setNextReader(reader, docBase); + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + comparator.setScorer(scorer); + } + + } + + /* + * Implements a TopFieldCollector over one SortField criteria, without + * tracking document scores and maxScore, and assumes out of orderness in doc + * Ids collection. + */ + private static class OutOfOrderOneComparatorNonScoringCollector extends + OneComparatorNonScoringCollector { + + public OutOfOrderOneComparatorNonScoringCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + final int cmp = reverseMul * comparator.compareBottom(doc); + if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.doc)) { + return; + } + + // This hit is competitive - replace bottom element in queue & adjustTop + comparator.copy(bottom.slot, doc); + updateBottom(doc); + comparator.setBottom(bottom.slot); + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + comparator.copy(slot, doc); + add(slot, doc, Float.NaN); + if (queueFull) { + comparator.setBottom(bottom.slot); + } + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return true; + } + + } + + /* + * Implements a TopFieldCollector over one SortField criteria, while tracking + * document scores but no maxScore. + */ + private static class OneComparatorScoringNoMaxScoreCollector extends + OneComparatorNonScoringCollector { + + Scorer scorer; + + public OneComparatorScoringNoMaxScoreCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + } + + final void updateBottom(int doc, float score) { + bottom.doc = docBase + doc; + bottom.score = score; + bottom = pq.updateTop(); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + if ((reverseMul * comparator.compareBottom(doc)) <= 0) { + // since docs are visited in doc Id order, if compare is 0, it means + // this document is largest than anything else in the queue, and + // therefore not competitive. + return; + } + + // Compute the score only if the hit is competitive. + final float score = scorer.score(); + + // This hit is competitive - replace bottom element in queue & adjustTop + comparator.copy(bottom.slot, doc); + updateBottom(doc, score); + comparator.setBottom(bottom.slot); + } else { + // Compute the score only if the hit is competitive. + final float score = scorer.score(); + + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + comparator.copy(slot, doc); + add(slot, doc, score); + if (queueFull) { + comparator.setBottom(bottom.slot); + } + } + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + this.scorer = scorer; + comparator.setScorer(scorer); + } + + } + + /* + * Implements a TopFieldCollector over one SortField criteria, while tracking + * document scores but no maxScore, and assumes out of orderness in doc Ids + * collection. + */ + private static class OutOfOrderOneComparatorScoringNoMaxScoreCollector extends + OneComparatorScoringNoMaxScoreCollector { + + public OutOfOrderOneComparatorScoringNoMaxScoreCollector( + FieldValueHitQueue queue, int numHits, boolean fillFields) + throws IOException { + super(queue, numHits, fillFields); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + final int cmp = reverseMul * comparator.compareBottom(doc); + if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.doc)) { + return; + } + + // Compute the score only if the hit is competitive. + final float score = scorer.score(); + + // This hit is competitive - replace bottom element in queue & adjustTop + comparator.copy(bottom.slot, doc); + updateBottom(doc, score); + comparator.setBottom(bottom.slot); + } else { + // Compute the score only if the hit is competitive. + final float score = scorer.score(); + + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + comparator.copy(slot, doc); + add(slot, doc, score); + if (queueFull) { + comparator.setBottom(bottom.slot); + } + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return true; + } + + } + + /* + * Implements a TopFieldCollector over one SortField criteria, with tracking + * document scores and maxScore. + */ + private static class OneComparatorScoringMaxScoreCollector extends + OneComparatorNonScoringCollector { + + Scorer scorer; + + public OneComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN. + maxScore = Float.NEGATIVE_INFINITY; + } + + final void updateBottom(int doc, float score) { + bottom.doc = docBase + doc; + bottom.score = score; + bottom = pq.updateTop(); + } + + @Override + public void collect(int doc) throws IOException { + final float score = scorer.score(); + if (score > maxScore) { + maxScore = score; + } + ++totalHits; + if (queueFull) { + if ((reverseMul * comparator.compareBottom(doc)) <= 0) { + // since docs are visited in doc Id order, if compare is 0, it means + // this document is largest than anything else in the queue, and + // therefore not competitive. + return; + } + + // This hit is competitive - replace bottom element in queue & adjustTop + comparator.copy(bottom.slot, doc); + updateBottom(doc, score); + comparator.setBottom(bottom.slot); + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + comparator.copy(slot, doc); + add(slot, doc, score); + if (queueFull) { + comparator.setBottom(bottom.slot); + } + } + + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + this.scorer = scorer; + super.setScorer(scorer); + } + } + + /* + * Implements a TopFieldCollector over one SortField criteria, with tracking + * document scores and maxScore, and assumes out of orderness in doc Ids + * collection. + */ + private static class OutOfOrderOneComparatorScoringMaxScoreCollector extends + OneComparatorScoringMaxScoreCollector { + + public OutOfOrderOneComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + } + + @Override + public void collect(int doc) throws IOException { + final float score = scorer.score(); + if (score > maxScore) { + maxScore = score; + } + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + final int cmp = reverseMul * comparator.compareBottom(doc); + if (cmp < 0 || (cmp == 0 && doc + docBase > bottom.doc)) { + return; + } + + // This hit is competitive - replace bottom element in queue & adjustTop + comparator.copy(bottom.slot, doc); + updateBottom(doc, score); + comparator.setBottom(bottom.slot); + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + comparator.copy(slot, doc); + add(slot, doc, score); + if (queueFull) { + comparator.setBottom(bottom.slot); + } + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return true; + } + + } + + /* + * Implements a TopFieldCollector over multiple SortField criteria, without + * tracking document scores and maxScore. + */ + private static class MultiComparatorNonScoringCollector extends TopFieldCollector { + + final FieldComparator[] comparators; + final int[] reverseMul; + + public MultiComparatorNonScoringCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + comparators = queue.getComparators(); + reverseMul = queue.getReverseMul(); + } + + final void updateBottom(int doc) { + // bottom.score is already set to Float.NaN in add(). + bottom.doc = docBase + doc; + bottom = pq.updateTop(); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + for (int i = 0;; i++) { + final int c = reverseMul[i] * comparators[i].compareBottom(doc); + if (c < 0) { + // Definitely not competitive. + return; + } else if (c > 0) { + // Definitely competitive. + break; + } else if (i == comparators.length - 1) { + // Here c=0. If we're at the last comparator, this doc is not + // competitive, since docs are visited in doc Id order, which means + // this doc cannot compete with any other document in the queue. + return; + } + } + + // This hit is competitive - replace bottom element in queue & adjustTop + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(bottom.slot, doc); + } + + updateBottom(doc); + + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(slot, doc); + } + add(slot, doc, Float.NaN); + if (queueFull) { + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } + } + } + + @Override + public void setNextReader(IndexReader reader, int docBase) throws IOException { + this.docBase = docBase; + for (int i = 0; i < comparators.length; i++) { + comparators[i].setNextReader(reader, docBase); + } + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + // set the scorer on all comparators + for (int i = 0; i < comparators.length; i++) { + comparators[i].setScorer(scorer); + } + } + } + + /* + * Implements a TopFieldCollector over multiple SortField criteria, without + * tracking document scores and maxScore, and assumes out of orderness in doc + * Ids collection. + */ + private static class OutOfOrderMultiComparatorNonScoringCollector extends + MultiComparatorNonScoringCollector { + + public OutOfOrderMultiComparatorNonScoringCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + for (int i = 0;; i++) { + final int c = reverseMul[i] * comparators[i].compareBottom(doc); + if (c < 0) { + // Definitely not competitive. + return; + } else if (c > 0) { + // Definitely competitive. + break; + } else if (i == comparators.length - 1) { + // This is the equals case. + if (doc + docBase > bottom.doc) { + // Definitely not competitive + return; + } + break; + } + } + + // This hit is competitive - replace bottom element in queue & adjustTop + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(bottom.slot, doc); + } + + updateBottom(doc); + + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(slot, doc); + } + add(slot, doc, Float.NaN); + if (queueFull) { + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return true; + } + + } + + /* + * Implements a TopFieldCollector over multiple SortField criteria, with + * tracking document scores and maxScore. + */ + private static class MultiComparatorScoringMaxScoreCollector extends MultiComparatorNonScoringCollector { + + Scorer scorer; + + public MultiComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN. + maxScore = Float.NEGATIVE_INFINITY; + } + + final void updateBottom(int doc, float score) { + bottom.doc = docBase + doc; + bottom.score = score; + bottom = pq.updateTop(); + } + + @Override + public void collect(int doc) throws IOException { + final float score = scorer.score(); + if (score > maxScore) { + maxScore = score; + } + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + for (int i = 0;; i++) { + final int c = reverseMul[i] * comparators[i].compareBottom(doc); + if (c < 0) { + // Definitely not competitive. + return; + } else if (c > 0) { + // Definitely competitive. + break; + } else if (i == comparators.length - 1) { + // Here c=0. If we're at the last comparator, this doc is not + // competitive, since docs are visited in doc Id order, which means + // this doc cannot compete with any other document in the queue. + return; + } + } + + // This hit is competitive - replace bottom element in queue & adjustTop + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(bottom.slot, doc); + } + + updateBottom(doc, score); + + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(slot, doc); + } + add(slot, doc, score); + if (queueFull) { + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } + } + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + this.scorer = scorer; + super.setScorer(scorer); + } + } + + /* + * Implements a TopFieldCollector over multiple SortField criteria, with + * tracking document scores and maxScore, and assumes out of orderness in doc + * Ids collection. + */ + private final static class OutOfOrderMultiComparatorScoringMaxScoreCollector + extends MultiComparatorScoringMaxScoreCollector { + + public OutOfOrderMultiComparatorScoringMaxScoreCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + } + + @Override + public void collect(int doc) throws IOException { + final float score = scorer.score(); + if (score > maxScore) { + maxScore = score; + } + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + for (int i = 0;; i++) { + final int c = reverseMul[i] * comparators[i].compareBottom(doc); + if (c < 0) { + // Definitely not competitive. + return; + } else if (c > 0) { + // Definitely competitive. + break; + } else if (i == comparators.length - 1) { + // This is the equals case. + if (doc + docBase > bottom.doc) { + // Definitely not competitive + return; + } + break; + } + } + + // This hit is competitive - replace bottom element in queue & adjustTop + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(bottom.slot, doc); + } + + updateBottom(doc, score); + + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(slot, doc); + } + add(slot, doc, score); + if (queueFull) { + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return true; + } + + } + + /* + * Implements a TopFieldCollector over multiple SortField criteria, with + * tracking document scores and maxScore. + */ + private static class MultiComparatorScoringNoMaxScoreCollector extends MultiComparatorNonScoringCollector { + + Scorer scorer; + + public MultiComparatorScoringNoMaxScoreCollector(FieldValueHitQueue queue, + int numHits, boolean fillFields) throws IOException { + super(queue, numHits, fillFields); + } + + final void updateBottom(int doc, float score) { + bottom.doc = docBase + doc; + bottom.score = score; + bottom = pq.updateTop(); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + for (int i = 0;; i++) { + final int c = reverseMul[i] * comparators[i].compareBottom(doc); + if (c < 0) { + // Definitely not competitive. + return; + } else if (c > 0) { + // Definitely competitive. + break; + } else if (i == comparators.length - 1) { + // Here c=0. If we're at the last comparator, this doc is not + // competitive, since docs are visited in doc Id order, which means + // this doc cannot compete with any other document in the queue. + return; + } + } + + // This hit is competitive - replace bottom element in queue & adjustTop + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(bottom.slot, doc); + } + + // Compute score only if it is competitive. + final float score = scorer.score(); + updateBottom(doc, score); + + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(slot, doc); + } + + // Compute score only if it is competitive. + final float score = scorer.score(); + add(slot, doc, score); + if (queueFull) { + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } + } + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + this.scorer = scorer; + super.setScorer(scorer); + } + } + + /* + * Implements a TopFieldCollector over multiple SortField criteria, with + * tracking document scores and maxScore, and assumes out of orderness in doc + * Ids collection. + */ + private final static class OutOfOrderMultiComparatorScoringNoMaxScoreCollector + extends MultiComparatorScoringNoMaxScoreCollector { + + public OutOfOrderMultiComparatorScoringNoMaxScoreCollector( + FieldValueHitQueue queue, int numHits, boolean fillFields) + throws IOException { + super(queue, numHits, fillFields); + } + + @Override + public void collect(int doc) throws IOException { + ++totalHits; + if (queueFull) { + // Fastmatch: return if this hit is not competitive + for (int i = 0;; i++) { + final int c = reverseMul[i] * comparators[i].compareBottom(doc); + if (c < 0) { + // Definitely not competitive. + return; + } else if (c > 0) { + // Definitely competitive. + break; + } else if (i == comparators.length - 1) { + // This is the equals case. + if (doc + docBase > bottom.doc) { + // Definitely not competitive + return; + } + break; + } + } + + // This hit is competitive - replace bottom element in queue & adjustTop + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(bottom.slot, doc); + } + + // Compute score only if it is competitive. + final float score = scorer.score(); + updateBottom(doc, score); + + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } else { + // Startup transient: queue hasn't gathered numHits yet + final int slot = totalHits - 1; + // Copy hit into queue + for (int i = 0; i < comparators.length; i++) { + comparators[i].copy(slot, doc); + } + + // Compute score only if it is competitive. + final float score = scorer.score(); + add(slot, doc, score); + if (queueFull) { + for (int i = 0; i < comparators.length; i++) { + comparators[i].setBottom(bottom.slot); + } + } + } + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + this.scorer = scorer; + super.setScorer(scorer); + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return true; + } + + } + + private static final ScoreDoc[] EMPTY_SCOREDOCS = new ScoreDoc[0]; + + private final boolean fillFields; + + /* + * Stores the maximum score value encountered, needed for normalizing. If + * document scores are not tracked, this value is initialized to NaN. + */ + float maxScore = Float.NaN; + + final int numHits; + FieldValueHitQueue.Entry bottom = null; + boolean queueFull; + int docBase; + + // Declaring the constructor private prevents extending this class by anyone + // else. Note that the class cannot be final since it's extended by the + // internal versions. If someone will define a constructor with any other + // visibility, then anyone will be able to extend the class, which is not what + // we want. + private TopFieldCollector(PriorityQueue pq, int numHits, boolean fillFields) { + super(pq); + this.numHits = numHits; + this.fillFields = fillFields; + } + + /** + * Creates a new {@link TopFieldCollector} from the given + * arguments. + * + *

NOTE: The instances returned by this method + * pre-allocate a full array of length + * numHits. + * + * @param sort + * the sort criteria (SortFields). + * @param numHits + * the number of results to collect. + * @param fillFields + * specifies whether the actual field values should be returned on + * the results (FieldDoc). + * @param trackDocScores + * specifies whether document scores should be tracked and set on the + * results. Note that if set to false, then the results' scores will + * be set to Float.NaN. Setting this to true affects performance, as + * it incurs the score computation on each competitive result. + * Therefore if document scores are not required by the application, + * it is recommended to set it to false. + * @param trackMaxScore + * specifies whether the query's maxScore should be tracked and set + * on the resulting {@link TopDocs}. Note that if set to false, + * {@link TopDocs#getMaxScore()} returns Float.NaN. Setting this to + * true affects performance as it incurs the score computation on + * each result. Also, setting this true automatically sets + * trackDocScores to true as well. + * @param docsScoredInOrder + * specifies whether documents are scored in doc Id order or not by + * the given {@link Scorer} in {@link #setScorer(Scorer)}. + * @return a {@link TopFieldCollector} instance which will sort the results by + * the sort criteria. + * @throws IOException + */ + public static TopFieldCollector create(Sort sort, int numHits, + boolean fillFields, boolean trackDocScores, boolean trackMaxScore, + boolean docsScoredInOrder) + throws IOException { + if (sort.fields.length == 0) { + throw new IllegalArgumentException("Sort must contain at least one field"); + } + + if (numHits <= 0) { + throw new IllegalArgumentException("numHits must be > 0; please use TotalHitCountCollector if you just need the total hit count"); + } + + FieldValueHitQueue queue = FieldValueHitQueue.create(sort.fields, numHits); + if (queue.getComparators().length == 1) { + if (docsScoredInOrder) { + if (trackMaxScore) { + return new OneComparatorScoringMaxScoreCollector(queue, numHits, fillFields); + } else if (trackDocScores) { + return new OneComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields); + } else { + return new OneComparatorNonScoringCollector(queue, numHits, fillFields); + } + } else { + if (trackMaxScore) { + return new OutOfOrderOneComparatorScoringMaxScoreCollector(queue, numHits, fillFields); + } else if (trackDocScores) { + return new OutOfOrderOneComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields); + } else { + return new OutOfOrderOneComparatorNonScoringCollector(queue, numHits, fillFields); + } + } + } + + // multiple comparators. + if (docsScoredInOrder) { + if (trackMaxScore) { + return new MultiComparatorScoringMaxScoreCollector(queue, numHits, fillFields); + } else if (trackDocScores) { + return new MultiComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields); + } else { + return new MultiComparatorNonScoringCollector(queue, numHits, fillFields); + } + } else { + if (trackMaxScore) { + return new OutOfOrderMultiComparatorScoringMaxScoreCollector(queue, numHits, fillFields); + } else if (trackDocScores) { + return new OutOfOrderMultiComparatorScoringNoMaxScoreCollector(queue, numHits, fillFields); + } else { + return new OutOfOrderMultiComparatorNonScoringCollector(queue, numHits, fillFields); + } + } + } + + final void add(int slot, int doc, float score) { + bottom = pq.add(new Entry(slot, docBase + doc, score)); + queueFull = totalHits == numHits; + } + + /* + * Only the following callback methods need to be overridden since + * topDocs(int, int) calls them to return the results. + */ + + @Override + protected void populateResults(ScoreDoc[] results, int howMany) { + if (fillFields) { + // avoid casting if unnecessary. + FieldValueHitQueue queue = (FieldValueHitQueue) pq; + for (int i = howMany - 1; i >= 0; i--) { + results[i] = queue.fillFields(queue.pop()); + } + } else { + for (int i = howMany - 1; i >= 0; i--) { + Entry entry = pq.pop(); + results[i] = new FieldDoc(entry.doc, entry.score); + } + } + } + + @Override + protected TopDocs newTopDocs(ScoreDoc[] results, int start) { + if (results == null) { + results = EMPTY_SCOREDOCS; + // Set maxScore to NaN, in case this is a maxScore tracking collector. + maxScore = Float.NaN; + } + + // If this is a maxScoring tracking collector and there were no results, + return new TopFieldDocs(totalHits, results, ((FieldValueHitQueue) pq).getFields(), maxScore); + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return false; + } +}