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;
21 import java.io.Serializable;
22 import java.util.Locale;
24 import org.apache.lucene.util.StringHelper;
27 * Stores information about how to sort documents by terms in an individual
28 * field. Fields must be indexed in order to sort by them.
30 * <p>Created: Feb 11, 2004 1:25:29 PM
35 public class SortField
36 implements Serializable {
38 /** Sort by document score (relevance). Sort values are Float and higher
39 * values are at the front. */
40 public static final int SCORE = 0;
42 /** Sort by document number (index order). Sort values are Integer and lower
43 * values are at the front. */
44 public static final int DOC = 1;
46 // reserved, in Lucene 2.9, there was a constant: AUTO = 2;
48 /** Sort using term values as Strings. Sort values are String and lower
49 * values are at the front. */
50 public static final int STRING = 3;
52 /** Sort using term values as encoded Integers. Sort values are Integer and
53 * lower values are at the front. */
54 public static final int INT = 4;
56 /** Sort using term values as encoded Floats. Sort values are Float and
57 * lower values are at the front. */
58 public static final int FLOAT = 5;
60 /** Sort using term values as encoded Longs. Sort values are Long and
61 * lower values are at the front. */
62 public static final int LONG = 6;
64 /** Sort using term values as encoded Doubles. Sort values are Double and
65 * lower values are at the front. */
66 public static final int DOUBLE = 7;
68 /** Sort using term values as encoded Shorts. Sort values are Short and
69 * lower values are at the front. */
70 public static final int SHORT = 8;
72 /** Sort using a custom Comparator. Sort values are any Comparable and
73 * sorting is done according to natural order. */
74 public static final int CUSTOM = 9;
76 /** Sort using term values as encoded Bytes. Sort values are Byte and
77 * lower values are at the front. */
78 public static final int BYTE = 10;
80 /** Sort using term values as Strings, but comparing by
81 * value (using String.compareTo) for all comparisons.
82 * This is typically slower than {@link #STRING}, which
83 * uses ordinals to do the sorting. */
84 public static final int STRING_VAL = 11;
86 // IMPLEMENTATION NOTE: the FieldCache.STRING_INDEX is in the same "namespace"
87 // as the above static int values. Any new values must not have the same value
88 // as FieldCache.STRING_INDEX.
90 /** Represents sorting by document score (relevance). */
91 public static final SortField FIELD_SCORE = new SortField(null, SCORE);
93 /** Represents sorting by document number (index order). */
94 public static final SortField FIELD_DOC = new SortField(null, DOC);
97 private int type; // defaults to determining type dynamically
98 private Locale locale; // defaults to "natural order" (no Locale)
99 boolean reverse = false; // defaults to natural order
100 private FieldCache.Parser parser;
102 // Used for CUSTOM sort
103 private FieldComparatorSource comparatorSource;
105 private Object missingValue;
107 /** Creates a sort by terms in the given field with the type of term
108 * values explicitly given.
109 * @param field Name of field to sort by. Can be <code>null</code> if
110 * <code>type</code> is SCORE or DOC.
111 * @param type Type of values in the terms.
113 public SortField(String field, int type) {
114 initFieldType(field, type);
117 /** Creates a sort, possibly in reverse, by terms in the given field with the
118 * type of term values explicitly given.
119 * @param field Name of field to sort by. Can be <code>null</code> if
120 * <code>type</code> is SCORE or DOC.
121 * @param type Type of values in the terms.
122 * @param reverse True if natural order should be reversed.
124 public SortField(String field, int type, boolean reverse) {
125 initFieldType(field, type);
126 this.reverse = reverse;
129 /** Creates a sort by terms in the given field, parsed
130 * to numeric values using a custom {@link FieldCache.Parser}.
131 * @param field Name of field to sort by. Must not be null.
132 * @param parser Instance of a {@link FieldCache.Parser},
133 * which must subclass one of the existing numeric
134 * parsers from {@link FieldCache}. Sort type is inferred
135 * by testing which numeric parser the parser subclasses.
136 * @throws IllegalArgumentException if the parser fails to
137 * subclass an existing numeric parser, or field is null
139 public SortField(String field, FieldCache.Parser parser) {
140 this(field, parser, false);
143 /** Creates a sort, possibly in reverse, by terms in the given field, parsed
144 * to numeric values using a custom {@link FieldCache.Parser}.
145 * @param field Name of field to sort by. Must not be null.
146 * @param parser Instance of a {@link FieldCache.Parser},
147 * which must subclass one of the existing numeric
148 * parsers from {@link FieldCache}. Sort type is inferred
149 * by testing which numeric parser the parser subclasses.
150 * @param reverse True if natural order should be reversed.
151 * @throws IllegalArgumentException if the parser fails to
152 * subclass an existing numeric parser, or field is null
154 public SortField(String field, FieldCache.Parser parser, boolean reverse) {
155 if (parser instanceof FieldCache.IntParser) initFieldType(field, INT);
156 else if (parser instanceof FieldCache.FloatParser) initFieldType(field, FLOAT);
157 else if (parser instanceof FieldCache.ShortParser) initFieldType(field, SHORT);
158 else if (parser instanceof FieldCache.ByteParser) initFieldType(field, BYTE);
159 else if (parser instanceof FieldCache.LongParser) initFieldType(field, LONG);
160 else if (parser instanceof FieldCache.DoubleParser) initFieldType(field, DOUBLE);
162 throw new IllegalArgumentException("Parser instance does not subclass existing numeric parser from FieldCache (got " + parser + ")");
164 this.reverse = reverse;
165 this.parser = parser;
168 /** Creates a sort by terms in the given field sorted
169 * according to the given locale.
170 * @param field Name of field to sort by, cannot be <code>null</code>.
171 * @param locale Locale of values in the field.
173 public SortField (String field, Locale locale) {
174 initFieldType(field, STRING);
175 this.locale = locale;
178 /** Creates a sort, possibly in reverse, by terms in the given field sorted
179 * according to the given locale.
180 * @param field Name of field to sort by, cannot be <code>null</code>.
181 * @param locale Locale of values in the field.
183 public SortField (String field, Locale locale, boolean reverse) {
184 initFieldType(field, STRING);
185 this.locale = locale;
186 this.reverse = reverse;
189 /** Creates a sort with a custom comparison function.
190 * @param field Name of field to sort by; cannot be <code>null</code>.
191 * @param comparator Returns a comparator for sorting hits.
193 public SortField(String field, FieldComparatorSource comparator) {
194 initFieldType(field, CUSTOM);
195 this.comparatorSource = comparator;
198 /** Creates a sort, possibly in reverse, with a custom comparison function.
199 * @param field Name of field to sort by; cannot be <code>null</code>.
200 * @param comparator Returns a comparator for sorting hits.
201 * @param reverse True if natural order should be reversed.
203 public SortField(String field, FieldComparatorSource comparator, boolean reverse) {
204 initFieldType(field, CUSTOM);
205 this.reverse = reverse;
206 this.comparatorSource = comparator;
209 /** Set a default sorting value for documents which lacks one */
210 public SortField setMissingValue(Object missingValue) {
211 if (type != BYTE && type != SHORT && type != INT && type != FLOAT && type != LONG && type != DOUBLE) {
212 throw new IllegalArgumentException( "Missing value only works for numeric types" );
214 this.missingValue = missingValue;
219 // Sets field & type, and ensures field is not NULL unless
220 // type is SCORE or DOC
221 private void initFieldType(String field, int type) {
224 if (type != SCORE && type != DOC)
225 throw new IllegalArgumentException("field can only be null when type is SCORE or DOC");
227 this.field = StringHelper.intern(field);
231 /** Returns the name of the field. Could return <code>null</code>
232 * if the sort is by SCORE or DOC.
233 * @return Name of field, possibly <code>null</code>.
235 public String getField() {
239 /** Returns the type of contents in the field.
240 * @return One of the constants SCORE, DOC, STRING, INT or FLOAT.
242 public int getType() {
246 /** Returns the Locale by which term values are interpreted.
247 * May return <code>null</code> if no Locale was specified.
248 * @return Locale, or <code>null</code>.
250 public Locale getLocale() {
254 /** Returns the instance of a {@link FieldCache} parser that fits to the given sort type.
255 * May return <code>null</code> if no parser was specified. Sorting is using the default parser then.
256 * @return An instance of a {@link FieldCache} parser, or <code>null</code>.
258 public FieldCache.Parser getParser() {
262 /** Returns whether the sort should be reversed.
263 * @return True if natural order should be reversed.
265 public boolean getReverse() {
269 /** Returns the {@link FieldComparatorSource} used for
272 public FieldComparatorSource getComparatorSource() {
273 return comparatorSource;
277 public String toString() {
278 StringBuilder buffer = new StringBuilder();
281 buffer.append("<score>");
285 buffer.append("<doc>");
289 buffer.append("<string: \"").append(field).append("\">");
293 buffer.append("<string_val: \"").append(field).append("\">");
297 buffer.append("<byte: \"").append(field).append("\">");
301 buffer.append("<short: \"").append(field).append("\">");
305 buffer.append("<int: \"").append(field).append("\">");
309 buffer.append("<long: \"").append(field).append("\">");
313 buffer.append("<float: \"").append(field).append("\">");
317 buffer.append("<double: \"").append(field).append("\">");
321 buffer.append("<custom:\"").append(field).append("\": ").append(comparatorSource).append('>');
325 buffer.append("<???: \"").append(field).append("\">");
329 if (locale != null) buffer.append('(').append(locale).append(')');
330 if (parser != null) buffer.append('(').append(parser).append(')');
331 if (reverse) buffer.append('!');
333 return buffer.toString();
336 /** Returns true if <code>o</code> is equal to this. If a
337 * {@link FieldComparatorSource} or {@link
338 * FieldCache.Parser} was provided, it must properly
339 * implement equals (unless a singleton is always used). */
341 public boolean equals(Object o) {
342 if (this == o) return true;
343 if (!(o instanceof SortField)) return false;
344 final SortField other = (SortField)o;
346 other.field == this.field // field is always interned
347 && other.type == this.type
348 && other.reverse == this.reverse
349 && (other.locale == null ? this.locale == null : other.locale.equals(this.locale))
350 && (other.comparatorSource == null ? this.comparatorSource == null : other.comparatorSource.equals(this.comparatorSource))
351 && (other.parser == null ? this.parser == null : other.parser.equals(this.parser))
355 /** Returns true if <code>o</code> is equal to this. If a
356 * {@link FieldComparatorSource} or {@link
357 * FieldCache.Parser} was provided, it must properly
358 * implement hashCode (unless a singleton is always
361 public int hashCode() {
362 int hash=type^0x346565dd + Boolean.valueOf(reverse).hashCode()^0xaf5998bb;
363 if (field != null) hash += field.hashCode()^0xff5685dd;
364 if (locale != null) hash += locale.hashCode()^0x08150815;
365 if (comparatorSource != null) hash += comparatorSource.hashCode();
366 if (parser != null) hash += parser.hashCode()^0x3aaf56ff;
370 // field must be interned after reading from stream
371 private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
372 in.defaultReadObject();
374 field = StringHelper.intern(field);
377 /** Returns the {@link FieldComparator} to use for
380 * @lucene.experimental
382 * @param numHits number of top hits the queue will store
383 * @param sortPos position of this SortField within {@link
384 * Sort}. The comparator is primary if sortPos==0,
385 * secondary if sortPos==1, etc. Some comparators can
386 * optimize themselves when they are the primary sort.
387 * @return {@link FieldComparator} to use when sorting
389 public FieldComparator getComparator(final int numHits, final int sortPos) throws IOException {
391 if (locale != null) {
392 // TODO: it'd be nice to allow FieldCache.getStringIndex
393 // to optionally accept a Locale so sorting could then use
394 // the faster StringComparator impls
395 return new FieldComparator.StringComparatorLocale(numHits, field, locale);
399 case SortField.SCORE:
400 return new FieldComparator.RelevanceComparator(numHits);
403 return new FieldComparator.DocComparator(numHits);
406 return new FieldComparator.IntComparator(numHits, field, parser).setMissingValue((Integer) missingValue);
408 case SortField.FLOAT:
409 return new FieldComparator.FloatComparator(numHits, field, parser).setMissingValue((Float) missingValue);
412 return new FieldComparator.LongComparator(numHits, field, parser).setMissingValue((Long) missingValue);
414 case SortField.DOUBLE:
415 return new FieldComparator.DoubleComparator(numHits, field, parser).setMissingValue((Double) missingValue);
418 return new FieldComparator.ByteComparator(numHits, field, parser).setMissingValue((Byte) missingValue);
420 case SortField.SHORT:
421 return new FieldComparator.ShortComparator(numHits, field, parser).setMissingValue((Short) missingValue);
423 case SortField.CUSTOM:
424 assert comparatorSource != null;
425 return comparatorSource.newComparator(field, numHits, sortPos, reverse);
427 case SortField.STRING:
428 return new FieldComparator.StringOrdValComparator(numHits, field, sortPos, reverse);
430 case SortField.STRING_VAL:
431 return new FieldComparator.StringValComparator(numHits, field);
434 throw new IllegalStateException("Illegal sort type: " + type);