1 package org.apache.lucene.search;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
22 import org.apache.lucene.util.PriorityQueue;
25 * Expert: A hit queue for sorting by hits by terms in more than one field.
26 * Uses <code>FieldCache.DEFAULT</code> for maintaining
27 * internal term lookup tables.
29 * @lucene.experimental
31 * @see Searcher#search(Query,Filter,int,Sort)
34 public abstract class FieldValueHitQueue<T extends FieldValueHitQueue.Entry> extends PriorityQueue<T> {
36 public static class Entry extends ScoreDoc {
39 public Entry(int slot, int doc, float score) {
45 public String toString() {
46 return "slot:" + slot + " " + super.toString();
51 * An implementation of {@link FieldValueHitQueue} which is optimized in case
52 * there is just one comparator.
54 private static final class OneComparatorFieldValueHitQueue<T extends FieldValueHitQueue.Entry> extends FieldValueHitQueue<T> {
56 private final FieldComparator comparator;
57 private final int oneReverseMul;
59 public OneComparatorFieldValueHitQueue(SortField[] fields, int size)
63 SortField field = fields[0];
64 comparator = field.getComparator(size, 0);
65 oneReverseMul = field.reverse ? -1 : 1;
67 comparators[0] = comparator;
68 reverseMul[0] = oneReverseMul;
74 * Returns whether <code>a</code> is less relevant than <code>b</code>.
77 * @return <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
80 protected boolean lessThan(final Entry hitA, final Entry hitB) {
83 assert hitA.slot != hitB.slot;
85 final int c = oneReverseMul * comparator.compare(hitA.slot, hitB.slot);
90 // avoid random sort order that could lead to duplicates (bug #31241):
91 return hitA.doc > hitB.doc;
97 * An implementation of {@link FieldValueHitQueue} which is optimized in case
98 * there is more than one comparator.
100 private static final class MultiComparatorsFieldValueHitQueue<T extends FieldValueHitQueue.Entry> extends FieldValueHitQueue<T> {
102 public MultiComparatorsFieldValueHitQueue(SortField[] fields, int size)
106 int numComparators = comparators.length;
107 for (int i = 0; i < numComparators; ++i) {
108 SortField field = fields[i];
110 reverseMul[i] = field.reverse ? -1 : 1;
111 comparators[i] = field.getComparator(size, i);
118 protected boolean lessThan(final Entry hitA, final Entry hitB) {
121 assert hitA.slot != hitB.slot;
123 int numComparators = comparators.length;
124 for (int i = 0; i < numComparators; ++i) {
125 final int c = reverseMul[i] * comparators[i].compare(hitA.slot, hitB.slot);
132 // avoid random sort order that could lead to duplicates (bug #31241):
133 return hitA.doc > hitB.doc;
138 // prevent instantiation and extension.
139 private FieldValueHitQueue(SortField[] fields) {
140 // When we get here, fields.length is guaranteed to be > 0, therefore no
141 // need to check it again.
143 // All these are required by this class's API - need to return arrays.
144 // Therefore even in the case of a single comparator, create an array
146 this.fields = fields;
147 int numComparators = fields.length;
148 comparators = new FieldComparator[numComparators];
149 reverseMul = new int[numComparators];
153 * Creates a hit queue sorted by the given list of fields.
155 * <p><b>NOTE</b>: The instances returned by this method
156 * pre-allocate a full array of length <code>numHits</code>.
159 * SortField array we are sorting by in priority order (highest
160 * priority first); cannot be <code>null</code> or empty
162 * The number of hits to retain. Must be greater than zero.
163 * @throws IOException
165 public static <T extends FieldValueHitQueue.Entry> FieldValueHitQueue<T> create(SortField[] fields, int size) throws IOException {
167 if (fields.length == 0) {
168 throw new IllegalArgumentException("Sort must contain at least one field");
171 if (fields.length == 1) {
172 return new OneComparatorFieldValueHitQueue<T>(fields, size);
174 return new MultiComparatorsFieldValueHitQueue<T>(fields, size);
178 public FieldComparator[] getComparators() {
182 public int[] getReverseMul() {
186 /** Stores the sort criteria being used. */
187 protected final SortField[] fields;
188 protected final FieldComparator[] comparators;
189 protected final int[] reverseMul;
192 protected abstract boolean lessThan (final Entry a, final Entry b);
195 * Given a queue Entry, creates a corresponding FieldDoc
196 * that contains the values used to sort the given document.
197 * These values are not the raw values out of the index, but the internal
198 * representation of them. This is so the given search hit can be collated by
199 * a MultiSearcher with other search hits.
201 * @param entry The Entry used to create a FieldDoc
202 * @return The newly created FieldDoc
203 * @see Searchable#search(Weight,Filter,int,Sort)
205 FieldDoc fillFields(final Entry entry) {
206 final int n = comparators.length;
207 final Object[] fields = new Object[n];
208 for (int i = 0; i < n; ++i) {
209 fields[i] = comparators[i].value(entry.slot);
211 //if (maxscore > 1.0f) doc.score /= maxscore; // normalize scores
212 return new FieldDoc(entry.doc, entry.score, fields);
215 /** Returns the SortFields being used by this hit queue. */
216 SortField[] getFields() {