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[i] = new 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 = (ScoreDoc) a;
084 final ScoreDoc docB = (ScoreDoc) b;
085
086 // keep track of maximum score
087 if (docA.score > maxscore) maxscore = docA.score;
088 if (docB.score > maxscore) maxscore = 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.0f) doc.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 != null) ? new FieldCacheImpl.Entry (reader, field, factory)
134 : new FieldCacheImpl.Entry (reader, field, type);
135 synchronized (Comparators) {
136 return (ScoreDocComparator) Comparators.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 != null) ? new 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.DOC) return ScoreDocComparator.INDEXORDER;
152 if (type == SortField.SCORE) return 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 != null) comparator = 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 < fj) return -1;
198 if (fi > fj) return 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 < fj) return -1;
230 if (fi > fj) return 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 < fj) return -1;
261 if (fi > fj) return 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 }
|