FieldSortedHitQueue.java
001 package gate.creole.annic.apache.lucene.search;
002 
003 /**
004  * Copyright 2004 The Apache Software Foundation
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 
019 import gate.creole.annic.apache.lucene.index.IndexReader;
020 import gate.creole.annic.apache.lucene.util.PriorityQueue;
021 
022 import java.io.IOException;
023 import java.util.WeakHashMap;
024 import java.util.Map;
025 import java.util.Locale;
026 import java.text.Collator;
027 
028 /**
029  * Expert: A hit queue for sorting by hits by terms in more than one field.
030  * Uses <code>FieldCache.DEFAULT</code> for maintaining internal term lookup tables.
031  *
032  <p>Created: Dec 8, 2003 12:56:03 PM
033  *
034  @author  Tim Jones (Nacimiento Software)
035  @since   lucene 1.4
036  @version $Id: FieldSortedHitQueue.java 529 2004-10-05 11:55:26Z niraj $
037  @see Searchable#search(Query,Filter,int,Sort)
038  @see FieldCache
039  */
040 class FieldSortedHitQueue
041 extends PriorityQueue {
042 
043   /**
044    * Creates a hit queue sorted by the given list of fields.
045    @param reader  Index to use.
046    @param fields Field names, in priority order (highest priority first).  Cannot be <code>null</code> or empty.
047    @param size  The number of hits to retain.  Must be greater than zero.
048    @throws IOException
049    */
050   FieldSortedHitQueue (IndexReader reader, SortField[] fields, int size)
051   throws IOException {
052     final int n = fields.length;
053     comparators = new ScoreDocComparator[n];
054     this.fields = new SortField[n];
055     for (int i=0; i<n; ++i) {
056       String fieldname = fields[i].getField();
057       comparators[i= getCachedComparator (reader, fieldname, fields[i].getType(), fields[i].getLocale(), fields[i].getFactory());
058       this.fields[inew SortField (fieldname, comparators[i].sortType(), fields[i].getReverse());
059     }
060     initialize (size);
061   }
062 
063 
064   /** Stores a comparator corresponding to each field being sorted by */
065   protected ScoreDocComparator[] comparators;
066 
067   /** Stores the sort criteria being used. */
068   protected SortField[] fields;
069 
070   /** Stores the maximum score value encountered, for normalizing.
071    *  we only care about scores greater than 1.0 - if all the scores
072    *  are less than 1.0, we don't have to normalize. */
073   protected float maxscore = 1.0f;
074 
075 
076   /**
077    * Returns whether <code>a</code> is less relevant than <code>b</code>.
078    @param a ScoreDoc
079    @param b ScoreDoc
080    @return <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
081    */
082   protected final boolean lessThan (final Object a, final Object b) {
083     final ScoreDoc docA = (ScoreDoca;
084     final ScoreDoc docB = (ScoreDocb;
085 
086     // keep track of maximum score
087     if (docA.score > maxscoremaxscore = docA.score;
088     if (docB.score > maxscoremaxscore = docB.score;
089 
090     // run comparators
091     final int n = comparators.length;
092     int c = 0;
093     for (int i=0; i<n && c==0; ++i) {
094       c = (fields[i].reverse? comparators[i].compare (docB, docA)
095                               : comparators[i].compare (docA, docB);
096     }
097     return c > 0;
098   }
099 
100 
101   /**
102    * Given a FieldDoc object, stores the values used
103    * to sort the given document.  These values are not the raw
104    * values out of the index, but the internal representation
105    * of them.  This is so the given search hit can be collated
106    * by a MultiSearcher with other search hits.
107    @param  doc  The FieldDoc to store sort values into.
108    @return  The same FieldDoc passed in.
109    @see Searchable#search(Query,Filter,int,Sort)
110    */
111   FieldDoc fillFields (final FieldDoc doc) {
112     final int n = comparators.length;
113     final Comparable[] fields = new Comparable[n];
114     for (int i=0; i<n; ++i)
115       fields[i= comparators[i].sortValue(doc);
116     doc.fields = fields;
117     if (maxscore > 1.0fdoc.score /= maxscore;   // normalize scores
118     return doc;
119   }
120 
121 
122   /** Returns the SortFields being used by this hit queue. */
123   SortField[] getFields() {
124     return fields;
125   }
126 
127   /** Internal cache of comparators. Similar to FieldCache, only
128    *  caches comparators instead of term values. */
129   static final Map Comparators = new WeakHashMap();
130 
131   /** Returns a comparator if it is in the cache. */
132   static ScoreDocComparator lookup (IndexReader reader, String field, int type, Object factory) {
133     FieldCacheImpl.Entry entry = (factory != nullnew FieldCacheImpl.Entry (reader, field, factory)
134                                                    new FieldCacheImpl.Entry (reader, field, type);
135     synchronized (Comparators) {
136       return (ScoreDocComparatorComparators.get (entry);
137     }
138   }
139 
140   /** Stores a comparator into the cache. */
141   static Object store (IndexReader reader, String field, int type, Object factory, Object value) {
142     FieldCacheImpl.Entry entry = (factory != nullnew FieldCacheImpl.Entry (reader, field, factory)
143                                                    new FieldCacheImpl.Entry (reader, field, type);
144     synchronized (Comparators) {
145       return Comparators.put (entry, value);
146     }
147   }
148 
149   static ScoreDocComparator getCachedComparator (IndexReader reader, String fieldname, int type, Locale locale, SortComparatorSource factory)
150   throws IOException {
151     if (type == SortField.DOCreturn ScoreDocComparator.INDEXORDER;
152     if (type == SortField.SCOREreturn ScoreDocComparator.RELEVANCE;
153     ScoreDocComparator comparator = lookup (reader, fieldname, type, factory);
154     if (comparator == null) {
155       switch (type) {
156         case SortField.AUTO:
157           comparator = comparatorAuto (reader, fieldname);
158           break;
159         case SortField.INT:
160           comparator = comparatorInt (reader, fieldname);
161           break;
162         case SortField.FLOAT:
163           comparator = comparatorFloat (reader, fieldname);
164           break;
165         case SortField.STRING:
166           if (locale != nullcomparator = comparatorStringLocale (reader, fieldname, locale);
167           else comparator = comparatorString (reader, fieldname);
168           break;
169         case SortField.CUSTOM:
170           comparator = factory.newComparator (reader, fieldname);
171           break;
172         default:
173           throw new RuntimeException ("unknown field type: "+type);
174       }
175       store (reader, fieldname, type, factory, comparator);
176     }
177     return comparator;
178   }
179 
180   /**
181    * Returns a comparator for sorting hits according to a field containing integers.
182    @param reader  Index to use.
183    @param fieldname  Field containg integer values.
184    @return  Comparator for sorting hits.
185    @throws IOException If an error occurs reading the index.
186    */
187   static ScoreDocComparator comparatorInt (final IndexReader reader, final String fieldname)
188   throws IOException {
189     final String field = fieldname.intern();
190     return new ScoreDocComparator() {
191 
192       final int[] fieldOrder = FieldCache.DEFAULT.getInts (reader, field);
193 
194       public final int compare (final ScoreDoc i, final ScoreDoc j) {
195         final int fi = fieldOrder[i.doc];
196         final int fj = fieldOrder[j.doc];
197         if (fi < fjreturn -1;
198         if (fi > fjreturn 1;
199         return 0;
200       }
201 
202       public Comparable sortValue (final ScoreDoc i) {
203         return new Integer (fieldOrder[i.doc]);
204       }
205 
206       public int sortType() {
207         return SortField.INT;
208       }
209     };
210   }
211 
212   /**
213    * Returns a comparator for sorting hits according to a field containing floats.
214    @param reader  Index to use.
215    @param fieldname  Field containg float values.
216    @return  Comparator for sorting hits.
217    @throws IOException If an error occurs reading the index.
218    */
219   static ScoreDocComparator comparatorFloat (final IndexReader reader, final String fieldname)
220   throws IOException {
221     final String field = fieldname.intern();
222     return new ScoreDocComparator () {
223 
224       protected final float[] fieldOrder = FieldCache.DEFAULT.getFloats (reader, field);
225 
226       public final int compare (final ScoreDoc i, final ScoreDoc j) {
227         final float fi = fieldOrder[i.doc];
228         final float fj = fieldOrder[j.doc];
229         if (fi < fjreturn -1;
230         if (fi > fjreturn 1;
231         return 0;
232       }
233 
234       public Comparable sortValue (final ScoreDoc i) {
235         return new Float (fieldOrder[i.doc]);
236       }
237 
238       public int sortType() {
239         return SortField.FLOAT;
240       }
241     };
242   }
243 
244   /**
245    * Returns a comparator for sorting hits according to a field containing strings.
246    @param reader  Index to use.
247    @param fieldname  Field containg string values.
248    @return  Comparator for sorting hits.
249    @throws IOException If an error occurs reading the index.
250    */
251   static ScoreDocComparator comparatorString (final IndexReader reader, final String fieldname)
252   throws IOException {
253     final String field = fieldname.intern();
254     return new ScoreDocComparator () {
255       final FieldCache.StringIndex index = FieldCache.DEFAULT.getStringIndex (reader, field);
256 
257       public final int compare (final ScoreDoc i, final ScoreDoc j) {
258         final int fi = index.order[i.doc];
259         final int fj = index.order[j.doc];
260         if (fi < fjreturn -1;
261         if (fi > fjreturn 1;
262         return 0;
263       }
264 
265       public Comparable sortValue (final ScoreDoc i) {
266         return index.lookup[index.order[i.doc]];
267       }
268 
269       public int sortType() {
270         return SortField.STRING;
271       }
272     };
273   }
274 
275   /**
276    * Returns a comparator for sorting hits according to a field containing strings.
277    @param reader  Index to use.
278    @param fieldname  Field containg string values.
279    @return  Comparator for sorting hits.
280    @throws IOException If an error occurs reading the index.
281    */
282   static ScoreDocComparator comparatorStringLocale (final IndexReader reader, final String fieldname, final Locale locale)
283   throws IOException {
284     final Collator collator = Collator.getInstance (locale);
285     final String field = fieldname.intern();
286     return new ScoreDocComparator() {
287       final String[] index = FieldCache.DEFAULT.getStrings (reader, field);
288 
289       public final int compare (final ScoreDoc i, final ScoreDoc j) {
290         return collator.compare (index[i.doc], index[j.doc]);
291       }
292 
293       public Comparable sortValue (final ScoreDoc i) {
294         return index[i.doc];
295       }
296 
297       public int sortType() {
298         return SortField.STRING;
299       }
300     };
301   }
302 
303   /**
304    * Returns a comparator for sorting hits according to values in the given field.
305    * The terms in the field are looked at to determine whether they contain integers,
306    * floats or strings.  Once the type is determined, one of the other static methods
307    * in this class is called to get the comparator.
308    @param reader  Index to use.
309    @param fieldname  Field containg values.
310    @return  Comparator for sorting hits.
311    @throws IOException If an error occurs reading the index.
312    */
313   static ScoreDocComparator comparatorAuto (final IndexReader reader, final String fieldname)
314   throws IOException {
315     final String field = fieldname.intern();
316     Object lookupArray = FieldCache.DEFAULT.getAuto (reader, field);
317     if (lookupArray instanceof FieldCache.StringIndex) {
318       return comparatorString (reader, field);
319     else if (lookupArray instanceof int[]) {
320       return comparatorInt (reader, field);
321     else if (lookupArray instanceof float[]) {
322       return comparatorFloat (reader, field);
323     else if (lookupArray instanceof String[]) {
324       return comparatorString (reader, field);
325     else {
326       throw new RuntimeException ("unknown data type in field '"+field+"'");
327     }
328   }
329 }