pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / SortField.java
1 package org.apache.lucene.search;
2
3 /**
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 import java.io.IOException;
21 import java.io.Serializable;
22 import java.util.Locale;
23
24 import org.apache.lucene.util.StringHelper;
25
26 /**
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.
29  *
30  * <p>Created: Feb 11, 2004 1:25:29 PM
31  *
32  * @since   lucene 1.4
33  * @see Sort
34  */
35 public class SortField
36 implements Serializable {
37
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;
41
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;
45
46   // reserved, in Lucene 2.9, there was a constant: AUTO = 2;
47
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;
51
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;
55
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;
59
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;
63
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;
67
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;
71
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;
75
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;
79   
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;
85   
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.
89
90   /** Represents sorting by document score (relevance). */
91   public static final SortField FIELD_SCORE = new SortField(null, SCORE);
92
93   /** Represents sorting by document number (index order). */
94   public static final SortField FIELD_DOC = new SortField(null, DOC);
95
96   private String field;
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;
101
102   // Used for CUSTOM sort
103   private FieldComparatorSource comparatorSource;
104
105   private Object missingValue;
106
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.
112    */
113   public SortField(String field, int type) {
114     initFieldType(field, type);
115   }
116
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.
123    */
124   public SortField(String field, int type, boolean reverse) {
125     initFieldType(field, type);
126     this.reverse = reverse;
127   }
128
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
138    */
139   public SortField(String field, FieldCache.Parser parser) {
140     this(field, parser, false);
141   }
142
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
153    */
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);
161     else
162       throw new IllegalArgumentException("Parser instance does not subclass existing numeric parser from FieldCache (got " + parser + ")");
163
164     this.reverse = reverse;
165     this.parser = parser;
166   }
167
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.
172    */
173   public SortField (String field, Locale locale) {
174     initFieldType(field, STRING);
175     this.locale = locale;
176   }
177
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.
182    */
183   public SortField (String field, Locale locale, boolean reverse) {
184     initFieldType(field, STRING);
185     this.locale = locale;
186     this.reverse = reverse;
187   }
188
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.
192    */
193   public SortField(String field, FieldComparatorSource comparator) {
194     initFieldType(field, CUSTOM);
195     this.comparatorSource = comparator;
196   }
197
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.
202    */
203   public SortField(String field, FieldComparatorSource comparator, boolean reverse) {
204     initFieldType(field, CUSTOM);
205     this.reverse = reverse;
206     this.comparatorSource = comparator;
207   }
208
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" );
213     }
214     this.missingValue = missingValue;
215     
216     return this;
217   }
218   
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) {
222     this.type = type;
223     if (field == null) {
224       if (type != SCORE && type != DOC)
225         throw new IllegalArgumentException("field can only be null when type is SCORE or DOC");
226     } else {
227       this.field = StringHelper.intern(field);
228     }
229   }
230
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>.
234    */
235   public String getField() {
236     return field;
237   }
238
239   /** Returns the type of contents in the field.
240    * @return One of the constants SCORE, DOC, STRING, INT or FLOAT.
241    */
242   public int getType() {
243     return type;
244   }
245
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>.
249    */
250   public Locale getLocale() {
251     return locale;
252   }
253
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>.
257    */
258   public FieldCache.Parser getParser() {
259     return parser;
260   }
261
262   /** Returns whether the sort should be reversed.
263    * @return  True if natural order should be reversed.
264    */
265   public boolean getReverse() {
266     return reverse;
267   }
268
269   /** Returns the {@link FieldComparatorSource} used for
270    * custom sorting
271    */
272   public FieldComparatorSource getComparatorSource() {
273     return comparatorSource;
274   }
275
276   @Override
277   public String toString() {
278     StringBuilder buffer = new StringBuilder();
279     switch (type) {
280       case SCORE:
281         buffer.append("<score>");
282         break;
283
284       case DOC:
285         buffer.append("<doc>");
286         break;
287
288       case STRING:
289         buffer.append("<string: \"").append(field).append("\">");
290         break;
291
292       case STRING_VAL:
293         buffer.append("<string_val: \"").append(field).append("\">");
294         break;
295
296       case BYTE:
297         buffer.append("<byte: \"").append(field).append("\">");
298         break;
299
300       case SHORT:
301         buffer.append("<short: \"").append(field).append("\">");
302         break;
303
304       case INT:
305         buffer.append("<int: \"").append(field).append("\">");
306         break;
307
308       case LONG:
309         buffer.append("<long: \"").append(field).append("\">");
310         break;
311
312       case FLOAT:
313         buffer.append("<float: \"").append(field).append("\">");
314         break;
315
316       case DOUBLE:
317         buffer.append("<double: \"").append(field).append("\">");
318         break;
319
320       case CUSTOM:
321         buffer.append("<custom:\"").append(field).append("\": ").append(comparatorSource).append('>');
322         break;
323
324       default:
325         buffer.append("<???: \"").append(field).append("\">");
326         break;
327     }
328
329     if (locale != null) buffer.append('(').append(locale).append(')');
330     if (parser != null) buffer.append('(').append(parser).append(')');
331     if (reverse) buffer.append('!');
332
333     return buffer.toString();
334   }
335
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). */
340   @Override
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;
345     return (
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))
352     );
353   }
354
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
359    *  used). */
360   @Override
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;
367     return hash;
368   }
369
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();
373     if (field != null)
374       field = StringHelper.intern(field);
375   }
376
377   /** Returns the {@link FieldComparator} to use for
378    * sorting.
379    *
380    * @lucene.experimental
381    *
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
388    */
389   public FieldComparator getComparator(final int numHits, final int sortPos) throws IOException {
390
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);
396     }
397
398     switch (type) {
399     case SortField.SCORE:
400       return new FieldComparator.RelevanceComparator(numHits);
401
402     case SortField.DOC:
403       return new FieldComparator.DocComparator(numHits);
404
405     case SortField.INT:
406       return new FieldComparator.IntComparator(numHits, field, parser, (Integer) missingValue);
407
408     case SortField.FLOAT:
409       return new FieldComparator.FloatComparator(numHits, field, parser, (Float) missingValue);
410
411     case SortField.LONG:
412       return new FieldComparator.LongComparator(numHits, field, parser, (Long) missingValue);
413
414     case SortField.DOUBLE:
415       return new FieldComparator.DoubleComparator(numHits, field, parser, (Double) missingValue);
416
417     case SortField.BYTE:
418       return new FieldComparator.ByteComparator(numHits, field, parser, (Byte) missingValue);
419
420     case SortField.SHORT:
421       return new FieldComparator.ShortComparator(numHits, field, parser, (Short) missingValue);
422
423     case SortField.CUSTOM:
424       assert comparatorSource != null;
425       return comparatorSource.newComparator(field, numHits, sortPos, reverse);
426
427     case SortField.STRING:
428       return new FieldComparator.StringOrdValComparator(numHits, field, sortPos, reverse);
429
430     case SortField.STRING_VAL:
431       return new FieldComparator.StringValComparator(numHits, field);
432         
433     default:
434       throw new IllegalStateException("Illegal sort type: " + type);
435     }
436   }
437 }