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.util.PriorityQueue;
020
021 import java.io.IOException;
022 import java.text.Collator;
023 import java.util.Locale;
024
025 /**
026 * Expert: Collects sorted results from Searchable's and collates them.
027 * The elements put into this queue must be of type FieldDoc.
028 *
029 * <p>Created: Feb 11, 2004 2:04:21 PM
030 *
031 * @author Tim Jones (Nacimiento Software)
032 * @since lucene 1.4
033 * @version $Id: FieldDocSortedHitQueue.java 529 2004-10-05 11:55:26Z niraj $
034 */
035 class FieldDocSortedHitQueue
036 extends PriorityQueue {
037
038 // this cannot contain AUTO fields - any AUTO fields should
039 // have been resolved by the time this class is used.
040 volatile SortField[] fields;
041
042 // used in the case where the fields are sorted by locale
043 // based strings
044 volatile Collator[] collators;
045
046
047 /**
048 * Creates a hit queue sorted by the given list of fields.
049 * @param fields Field names, in priority order (highest priority first).
050 * @param size The number of hits to retain. Must be greater than zero.
051 * @throws IOException
052 */
053 FieldDocSortedHitQueue (SortField[] fields, int size)
054 throws IOException {
055 this.fields = fields;
056 this.collators = hasCollators (fields);
057 initialize (size);
058 }
059
060
061 /**
062 * Allows redefinition of sort fields if they are <code>null</code>.
063 * This is to handle the case using ParallelMultiSearcher where the
064 * original list contains AUTO and we don't know the actual sort
065 * type until the values come back. The fields can only be set once.
066 * This method is thread safe.
067 * @param fields
068 */
069 synchronized void setFields (SortField[] fields) {
070 if (this.fields == null) {
071 this.fields = fields;
072 this.collators = hasCollators (fields);
073 }
074 }
075
076
077 /** Returns the fields being used to sort. */
078 SortField[] getFields() {
079 return fields;
080 }
081
082
083 /** Returns an array of collators, possibly <code>null</code>. The collators
084 * correspond to any SortFields which were given a specific locale.
085 * @param fields Array of sort fields.
086 * @return Array, possibly <code>null</code>.
087 */
088 private Collator[] hasCollators (final SortField[] fields) {
089 if (fields == null) return null;
090 Collator[] ret = new Collator[fields.length];
091 for (int i=0; i<fields.length; ++i) {
092 Locale locale = fields[i].getLocale();
093 if (locale != null)
094 ret[i] = Collator.getInstance (locale);
095 }
096 return ret;
097 }
098
099
100 /**
101 * Returns whether <code>a</code> is less relevant than <code>b</code>.
102 * @param a ScoreDoc
103 * @param b ScoreDoc
104 * @return <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
105 */
106 protected final boolean lessThan (final Object a, final Object b) {
107 final FieldDoc docA = (FieldDoc) a;
108 final FieldDoc docB = (FieldDoc) b;
109 final int n = fields.length;
110 int c = 0;
111 for (int i=0; i<n && c==0; ++i) {
112 final int type = fields[i].getType();
113 if (fields[i].getReverse()) {
114 switch (type) {
115 case SortField.SCORE:
116 float r1 = ((Float)docA.fields[i]).floatValue();
117 float r2 = ((Float)docB.fields[i]).floatValue();
118 if (r1 < r2) c = -1;
119 if (r1 > r2) c = 1;
120 break;
121 case SortField.DOC:
122 case SortField.INT:
123 int i1 = ((Integer)docA.fields[i]).intValue();
124 int i2 = ((Integer)docB.fields[i]).intValue();
125 if (i1 > i2) c = -1;
126 if (i1 < i2) c = 1;
127 break;
128 case SortField.STRING:
129 String s1 = (String) docA.fields[i];
130 String s2 = (String) docB.fields[i];
131 if (s2 == null) c = -1; // could be null if there are
132 else if (s1 == null) c = 1; // no terms in the given field
133 else if (fields[i].getLocale() == null) {
134 c = s2.compareTo(s1);
135 } else {
136 c = collators[i].compare (s2, s1);
137 }
138 break;
139 case SortField.FLOAT:
140 float f1 = ((Float)docA.fields[i]).floatValue();
141 float f2 = ((Float)docB.fields[i]).floatValue();
142 if (f1 > f2) c = -1;
143 if (f1 < f2) c = 1;
144 break;
145 case SortField.CUSTOM:
146 c = docB.fields[i].compareTo (docA.fields[i]);
147 break;
148 case SortField.AUTO:
149 // we cannot handle this - even if we determine the type of object (Float or
150 // Integer), we don't necessarily know how to compare them (both SCORE and
151 // FLOAT both contain floats, but are sorted opposite of each other). Before
152 // we get here, each AUTO should have been replaced with its actual value.
153 throw new RuntimeException ("FieldDocSortedHitQueue cannot use an AUTO SortField");
154 default:
155 throw new RuntimeException ("invalid SortField type: "+type);
156 }
157 } else {
158 switch (type) {
159 case SortField.SCORE:
160 float r1 = ((Float)docA.fields[i]).floatValue();
161 float r2 = ((Float)docB.fields[i]).floatValue();
162 if (r1 > r2) c = -1;
163 if (r1 < r2) c = 1;
164 break;
165 case SortField.DOC:
166 case SortField.INT:
167 int i1 = ((Integer)docA.fields[i]).intValue();
168 int i2 = ((Integer)docB.fields[i]).intValue();
169 if (i1 < i2) c = -1;
170 if (i1 > i2) c = 1;
171 break;
172 case SortField.STRING:
173 String s1 = (String) docA.fields[i];
174 String s2 = (String) docB.fields[i];
175 // null values need to be sorted first, because of how FieldCache.getStringIndex()
176 // works - in that routine, any documents without a value in the given field are
177 // put first.
178 if (s1 == null) c = -1; // could be null if there are
179 else if (s2 == null) c = 1; // no terms in the given field
180 else if (fields[i].getLocale() == null) {
181 c = s1.compareTo(s2);
182 } else {
183 c = collators[i].compare (s1, s2);
184 }
185 break;
186 case SortField.FLOAT:
187 float f1 = ((Float)docA.fields[i]).floatValue();
188 float f2 = ((Float)docB.fields[i]).floatValue();
189 if (f1 < f2) c = -1;
190 if (f1 > f2) c = 1;
191 break;
192 case SortField.CUSTOM:
193 c = docA.fields[i].compareTo (docB.fields[i]);
194 break;
195 case SortField.AUTO:
196 // we cannot handle this - even if we determine the type of object (Float or
197 // Integer), we don't necessarily know how to compare them (both SCORE and
198 // FLOAT both contain floats, but are sorted opposite of each other). Before
199 // we get here, each AUTO should have been replaced with its actual value.
200 throw new RuntimeException ("FieldDocSortedHitQueue cannot use an AUTO SortField");
201 default:
202 throw new RuntimeException ("invalid SortField type: "+type);
203 }
204 }
205 }
206 return c > 0;
207 }
208 }
|