2 * Copyright 2004 The Apache Software Foundation
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 using IndexReader
= Lucene
.Net
.Index
.IndexReader
;
18 using PriorityQueue
= Lucene
.Net
.Util
.PriorityQueue
;
19 namespace Lucene
.Net
.Search
22 /// <summary> Expert: A hit queue for sorting by hits by terms in more than one Field.
23 /// Uses <code>FieldCache.DEFAULT</code> for maintaining internal term lookup tables.
25 /// <p>Created: Dec 8, 2003 12:56:03 PM
28 /// <author> Tim Jones (Nacimiento Software)
30 /// <since> lucene 1.4
32 /// <version> $Id: FieldSortedHitQueue.cs,v 1.1 2005/01/17 19:54:30 joeshaw Exp $
34 /// <seealso cref="Searchable#Search(Query,Filter,int,Sort)">
36 /// <seealso cref="FieldCache">
38 class FieldSortedHitQueue
: PriorityQueue
40 private class AnonymousClassScoreDocComparator
: ScoreDocComparator
42 public AnonymousClassScoreDocComparator(int[] fieldOrder
)
44 InitBlock(fieldOrder
);
46 private void InitBlock(int[] fieldOrder
)
48 this.fieldOrder
= fieldOrder
;
51 private int[] fieldOrder
;
53 public int Compare(ScoreDoc i
, ScoreDoc j
)
55 int fi
= fieldOrder
[i
.doc
];
56 int fj
= fieldOrder
[j
.doc
];
64 public virtual System
.IComparable
SortValue(ScoreDoc i
)
66 return (System
.Int32
) fieldOrder
[i
.doc
];
69 public virtual int SortType()
74 private class AnonymousClassScoreDocComparator1
: ScoreDocComparator
76 public AnonymousClassScoreDocComparator1(float[] fieldOrder
)
78 InitBlock(fieldOrder
);
80 private void InitBlock(float[] fieldOrder
)
82 this.fieldOrder
= fieldOrder
;
84 private float[] fieldOrder
;
86 public int Compare(ScoreDoc i
, ScoreDoc j
)
88 float fi
= fieldOrder
[i
.doc
];
89 float fj
= fieldOrder
[j
.doc
];
97 public virtual System
.IComparable
SortValue(ScoreDoc i
)
99 return (float) fieldOrder
[i
.doc
];
102 public virtual int SortType()
104 return SortField
.FLOAT
;
107 private class AnonymousClassScoreDocComparator2
: ScoreDocComparator
109 public AnonymousClassScoreDocComparator2(Lucene
.Net
.Search
.StringIndex index
)
113 private void InitBlock(Lucene
.Net
.Search
.StringIndex index
)
117 private Lucene
.Net
.Search
.StringIndex index
;
119 public int Compare(ScoreDoc i
, ScoreDoc j
)
121 int fi
= index
.order
[i
.doc
];
122 int fj
= index
.order
[j
.doc
];
130 public virtual System
.IComparable
SortValue(ScoreDoc i
)
132 return index
.lookup
[index
.order
[i
.doc
]];
135 public virtual int SortType()
137 return SortField
.STRING
;
140 private class AnonymousClassScoreDocComparator3
: ScoreDocComparator
142 public AnonymousClassScoreDocComparator3(System
.Globalization
.CompareInfo collator
, System
.String
[] index
)
144 InitBlock(collator
, index
);
146 private void InitBlock(System
.Globalization
.CompareInfo collator
, System
.String
[] index
)
148 this.collator
= collator
;
151 private System
.Globalization
.CompareInfo collator
;
152 private System
.String
[] index
;
154 public int Compare(ScoreDoc i
, ScoreDoc j
)
156 return collator
.Compare(index
[i
.doc
].ToString(), index
[j
.doc
].ToString());
159 public virtual System
.IComparable
SortValue(ScoreDoc i
)
164 public virtual int SortType()
166 return SortField
.STRING
;
170 /// <summary> Creates a hit queue sorted by the given list of fields.</summary>
171 /// <param name="reader"> Index to use.
173 /// <param name="fields">Field names, in priority order (highest priority first). Cannot be <code>null</code> or empty.
175 /// <param name="size"> The number of hits to retain. Must be greater than zero.
177 /// <throws> IOException </throws>
178 internal FieldSortedHitQueue(IndexReader reader
, SortField
[] fields
, int size
)
180 int n
= fields
.Length
;
181 comparators
= new ScoreDocComparator
[n
];
182 this.fields
= new SortField
[n
];
183 for (int i
= 0; i
< n
; ++i
)
185 System
.String fieldname
= fields
[i
].GetField();
186 comparators
[i
] = GetCachedComparator(reader
, fieldname
, fields
[i
].GetType(), fields
[i
].GetLocale(), fields
[i
].GetFactory());
187 this.fields
[i
] = new SortField(fieldname
, comparators
[i
].SortType(), fields
[i
].GetReverse());
193 /// <summary>Stores a comparator corresponding to each Field being sorted by </summary>
194 protected internal ScoreDocComparator
[] comparators
;
196 /// <summary>Stores the sort criteria being used. </summary>
197 protected internal SortField
[] fields
;
199 /// <summary>Stores the maximum score value encountered, for normalizing.
200 /// we only care about scores greater than 1.0 - if all the scores
201 /// are less than 1.0, we don't have to normalize.
203 protected internal float maxscore
= 1.0f
;
206 /// <summary> Returns whether <code>a</code> is less relevant than <code>b</code>.</summary>
207 /// <param name="a">ScoreDoc
209 /// <param name="b">ScoreDoc
211 /// <returns> <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
213 public override bool LessThan(System
.Object a
, System
.Object b
)
215 ScoreDoc docA
= (ScoreDoc
) a
;
216 ScoreDoc docB
= (ScoreDoc
) b
;
218 // keep track of maximum score
219 if (docA
.score
> maxscore
)
220 maxscore
= docA
.score
;
221 if (docB
.score
> maxscore
)
222 maxscore
= docB
.score
;
225 int n
= comparators
.Length
;
227 for (int i
= 0; i
< n
&& c
== 0; ++i
)
229 c
= (fields
[i
].reverse
) ? comparators
[i
].Compare(docB
, docA
) : comparators
[i
].Compare(docA
, docB
);
231 // avoid random sort order that could lead to duplicates (bug #31241):
233 return docA
.doc
> docB
.doc
;
238 /// <summary> Given a FieldDoc object, stores the values used
239 /// to sort the given document. These values are not the raw
240 /// values out of the index, but the internal representation
241 /// of them. This is so the given search hit can be collated
242 /// by a MultiSearcher with other search hits.
244 /// <param name="doc"> The FieldDoc to store sort values into.
246 /// <returns> The same FieldDoc passed in.
248 /// <seealso cref="Searchable#Search(Query,Filter,int,Sort)">
250 internal virtual FieldDoc
FillFields(FieldDoc doc
)
252 int n
= comparators
.Length
;
253 System
.IComparable
[] fields
= new System
.IComparable
[n
];
254 for (int i
= 0; i
< n
; ++i
)
255 fields
[i
] = comparators
[i
].SortValue(doc
);
258 doc
.score
/= maxscore
; // normalize scores
263 /// <summary>Returns the SortFields being used by this hit queue. </summary>
264 internal virtual SortField
[] GetFields()
269 /// <summary>Internal cache of comparators. Similar to FieldCache, only
270 /// caches comparators instead of term values.
272 internal static readonly System
.Collections
.IDictionary Comparators
= new System
.Collections
.Hashtable();
274 /// <summary>Returns a comparator if it is in the cache. </summary>
275 internal static ScoreDocComparator
Lookup(IndexReader reader
, System
.String field
, int type
, System
.Object factory
)
277 FieldCacheImpl
.Entry entry
= (factory
!= null) ? new FieldCacheImpl
.Entry(field
, factory
) : new FieldCacheImpl
.Entry(field
, type
);
278 lock (Comparators
.SyncRoot
)
280 System
.Collections
.Hashtable readerCache
= (System
.Collections
.Hashtable
) Comparators
[reader
];
281 if (readerCache
== null)
283 return (ScoreDocComparator
) readerCache
[entry
];
287 /// <summary>Stores a comparator into the cache. </summary>
288 internal static System
.Object
Store(IndexReader reader
, System
.String field
, int type
, System
.Object factory
, System
.Object value_Renamed
)
290 FieldCacheImpl
.Entry entry
= (factory
!= null) ? new FieldCacheImpl
.Entry(field
, factory
) : new FieldCacheImpl
.Entry(field
, type
);
291 lock (Comparators
.SyncRoot
)
293 System
.Collections
.Hashtable readerCache
= (System
.Collections
.Hashtable
) Comparators
[reader
];
294 if (readerCache
== null)
296 readerCache
= new System
.Collections
.Hashtable();
297 Comparators
[reader
] = readerCache
;
299 System
.Object tempObject
;
300 tempObject
= readerCache
[entry
];
301 readerCache
[entry
] = value_Renamed
;
306 internal static ScoreDocComparator
GetCachedComparator(IndexReader reader
, System
.String fieldname
, int type
, System
.Globalization
.CultureInfo locale
, SortComparatorSource factory
)
308 if (type
== SortField
.DOC
)
309 return Lucene
.Net
.Search
.ScoreDocComparator_Fields
.INDEXORDER
;
310 if (type
== SortField
.SCORE
)
311 return Lucene
.Net
.Search
.ScoreDocComparator_Fields
.RELEVANCE
;
312 ScoreDocComparator comparator
= Lookup(reader
, fieldname
, type
, factory
);
313 if (comparator
== null)
319 comparator
= ComparatorAuto(reader
, fieldname
);
323 comparator
= ComparatorInt(reader
, fieldname
);
326 case SortField
.FLOAT
:
327 comparator
= ComparatorFloat(reader
, fieldname
);
330 case SortField
.STRING
:
332 comparator
= ComparatorStringLocale(reader
, fieldname
, locale
);
334 comparator
= ComparatorString(reader
, fieldname
);
337 case SortField
.CUSTOM
:
338 comparator
= factory
.NewComparator(reader
, fieldname
);
342 throw new System
.SystemException("unknown Field type: " + type
);
345 Store(reader
, fieldname
, type
, factory
, comparator
);
350 /// <summary> Returns a comparator for sorting hits according to a Field containing integers.</summary>
351 /// <param name="reader"> Index to use.
353 /// <param name="fieldname"> Field containg integer values.
355 /// <returns> Comparator for sorting hits.
357 /// <throws> IOException If an error occurs reading the index. </throws>
358 internal static ScoreDocComparator
ComparatorInt(IndexReader reader
, System
.String fieldname
)
360 System
.String field
= String
.Intern(fieldname
);
361 int[] fieldOrder
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetInts(reader
, field
);
362 return new AnonymousClassScoreDocComparator(fieldOrder
);
365 /// <summary> Returns a comparator for sorting hits according to a Field containing floats.</summary>
366 /// <param name="reader"> Index to use.
368 /// <param name="fieldname"> Field containg float values.
370 /// <returns> Comparator for sorting hits.
372 /// <throws> IOException If an error occurs reading the index. </throws>
373 internal static ScoreDocComparator
ComparatorFloat(IndexReader reader
, System
.String fieldname
)
375 System
.String field
= String
.Intern(fieldname
);
376 float[] fieldOrder
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetFloats(reader
, field
);
377 return new AnonymousClassScoreDocComparator1(fieldOrder
);
380 /// <summary> Returns a comparator for sorting hits according to a Field containing strings.</summary>
381 /// <param name="reader"> Index to use.
383 /// <param name="fieldname"> Field containg string values.
385 /// <returns> Comparator for sorting hits.
387 /// <throws> IOException If an error occurs reading the index. </throws>
388 internal static ScoreDocComparator
ComparatorString(IndexReader reader
, System
.String fieldname
)
390 System
.String field
= String
.Intern(fieldname
);
391 Lucene
.Net
.Search
.StringIndex index
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetStringIndex(reader
, field
);
392 return new AnonymousClassScoreDocComparator2(index
);
395 /// <summary> Returns a comparator for sorting hits according to a Field containing strings.</summary>
396 /// <param name="reader"> Index to use.
398 /// <param name="fieldname"> Field containg string values.
400 /// <returns> Comparator for sorting hits.
402 /// <throws> IOException If an error occurs reading the index. </throws>
403 internal static ScoreDocComparator
ComparatorStringLocale(IndexReader reader
, System
.String fieldname
, System
.Globalization
.CultureInfo locale
)
405 System
.Globalization
.CompareInfo collator
= locale
.CompareInfo
;
406 System
.String field
= String
.Intern(fieldname
);
407 System
.String
[] index
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetStrings(reader
, field
);
408 return new AnonymousClassScoreDocComparator3(collator
, index
);
411 /// <summary> Returns a comparator for sorting hits according to values in the given Field.
412 /// The terms in the Field are looked at to determine whether they contain integers,
413 /// floats or strings. Once the type is determined, one of the other static methods
414 /// in this class is called to get the comparator.
416 /// <param name="reader"> Index to use.
418 /// <param name="fieldname"> Field containg values.
420 /// <returns> Comparator for sorting hits.
422 /// <throws> IOException If an error occurs reading the index. </throws>
423 internal static ScoreDocComparator
ComparatorAuto(IndexReader reader
, System
.String fieldname
)
425 System
.String field
= String
.Intern(fieldname
);
426 System
.Object lookupArray
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetAuto(reader
, field
);
427 if (lookupArray
is Lucene
.Net
.Search
.StringIndex
)
429 return ComparatorString(reader
, field
);
431 else if (lookupArray
is int[])
433 return ComparatorInt(reader
, field
);
435 else if (lookupArray
is float[])
437 return ComparatorFloat(reader
, field
);
439 else if (lookupArray
is System
.String
[])
441 return ComparatorString(reader
, field
);
445 throw new System
.SystemException("unknown data type in Field '" + field
+ "'");