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.
18 using IndexReader
= Lucene
.Net
.Index
.IndexReader
;
19 using PriorityQueue
= Lucene
.Net
.Util
.PriorityQueue
;
21 namespace Lucene
.Net
.Search
24 /// <summary> Expert: A hit queue for sorting by hits by terms in more than one field.
25 /// Uses <code>FieldCache.DEFAULT</code> for maintaining internal term lookup tables.
27 /// <p>Created: Dec 8, 2003 12:56:03 PM
30 /// <author> Tim Jones (Nacimiento Software)
32 /// <since> lucene 1.4
34 /// <version> $Id: FieldSortedHitQueue.cs,v 1.3 2006/10/02 17:09:04 joeshaw Exp $
36 /// <seealso cref="Searcher.Search(Query,Filter,int,Sort)">
38 /// <seealso cref="FieldCache">
40 class FieldSortedHitQueue
: PriorityQueue
42 private class AnonymousClassScoreDocComparator
: ScoreDocComparator
44 public AnonymousClassScoreDocComparator(int[] fieldOrder
)
46 InitBlock(fieldOrder
);
48 private void InitBlock(int[] fieldOrder
)
50 this.fieldOrder
= fieldOrder
;
53 private int[] fieldOrder
;
55 public int Compare(ScoreDoc i
, ScoreDoc j
)
57 int fi
= fieldOrder
[i
.doc
];
58 int fj
= fieldOrder
[j
.doc
];
66 public virtual System
.IComparable
SortValue(ScoreDoc i
)
68 return (System
.Int32
) fieldOrder
[i
.doc
];
71 public virtual int SortType()
76 private class AnonymousClassScoreDocComparator1
: ScoreDocComparator
78 public AnonymousClassScoreDocComparator1(float[] fieldOrder
)
80 InitBlock(fieldOrder
);
82 private void InitBlock(float[] fieldOrder
)
84 this.fieldOrder
= fieldOrder
;
86 private float[] fieldOrder
;
88 public int Compare(ScoreDoc i
, ScoreDoc j
)
90 float fi
= fieldOrder
[i
.doc
];
91 float fj
= fieldOrder
[j
.doc
];
99 public virtual System
.IComparable
SortValue(ScoreDoc i
)
101 return (float) fieldOrder
[i
.doc
];
104 public virtual int SortType()
106 return SortField
.FLOAT
;
109 private class AnonymousClassScoreDocComparator2
: ScoreDocComparator
111 public AnonymousClassScoreDocComparator2(Lucene
.Net
.Search
.StringIndex index
)
115 private void InitBlock(Lucene
.Net
.Search
.StringIndex index
)
119 private Lucene
.Net
.Search
.StringIndex index
;
121 public int Compare(ScoreDoc i
, ScoreDoc j
)
123 int fi
= index
.order
[i
.doc
];
124 int fj
= index
.order
[j
.doc
];
132 public virtual System
.IComparable
SortValue(ScoreDoc i
)
134 return index
.lookup
[index
.order
[i
.doc
]];
137 public virtual int SortType()
139 return SortField
.STRING
;
142 private class AnonymousClassScoreDocComparator3
: ScoreDocComparator
144 public AnonymousClassScoreDocComparator3(System
.Globalization
.CompareInfo collator
, System
.String
[] index
)
146 InitBlock(collator
, index
);
148 private void InitBlock(System
.Globalization
.CompareInfo collator
, System
.String
[] index
)
150 this.collator
= collator
;
153 private System
.Globalization
.CompareInfo collator
;
154 private System
.String
[] index
;
156 public int Compare(ScoreDoc i
, ScoreDoc j
)
158 return collator
.Compare(index
[i
.doc
].ToString(), index
[j
.doc
].ToString());
161 public virtual System
.IComparable
SortValue(ScoreDoc i
)
166 public virtual int SortType()
168 return SortField
.STRING
;
172 /// <summary> Creates a hit queue sorted by the given list of fields.</summary>
173 /// <param name="reader"> Index to use.
175 /// <param name="fields">Field names, in priority order (highest priority first). Cannot be <code>null</code> or empty.
177 /// <param name="size"> The number of hits to retain. Must be greater than zero.
179 /// <throws> IOException </throws>
180 internal FieldSortedHitQueue(IndexReader reader
, SortField
[] fields
, int size
)
182 int n
= fields
.Length
;
183 comparators
= new ScoreDocComparator
[n
];
184 this.fields
= new SortField
[n
];
185 for (int i
= 0; i
< n
; ++i
)
187 System
.String fieldname
= fields
[i
].GetField();
188 comparators
[i
] = GetCachedComparator(reader
, fieldname
, fields
[i
].GetType(), fields
[i
].GetLocale(), fields
[i
].GetFactory());
189 this.fields
[i
] = new SortField(fieldname
, comparators
[i
].SortType(), fields
[i
].GetReverse());
195 /// <summary>Stores a comparator corresponding to each field being sorted by </summary>
196 protected internal ScoreDocComparator
[] comparators
;
198 /// <summary>Stores the sort criteria being used. </summary>
199 protected internal SortField
[] fields
;
201 /// <summary>Stores the maximum score value encountered, needed for normalizing. </summary>
202 protected internal float maxscore
= System
.Single
.NegativeInfinity
;
204 /// <summary>returns the maximum score encountered by elements inserted via insert()</summary>
205 public virtual float GetMaxScore()
210 // The signature of this method takes a FieldDoc in order to avoid
211 // the unneeded cast to retrieve the score.
213 public virtual bool Insert(FieldDoc fdoc
)
215 maxscore
= System
.Math
.Max(maxscore
, fdoc
.score
);
216 return base.Insert(fdoc
);
219 // This overrides PriorityQueue.insert() so that insert(FieldDoc) that
220 // keeps track of the score isn't accidentally bypassed.
222 public override bool Insert(System
.Object fdoc
)
224 return Insert((FieldDoc
) fdoc
);
227 /// <summary> Returns whether <code>a</code> is less relevant than <code>b</code>.</summary>
228 /// <param name="a">ScoreDoc
230 /// <param name="b">ScoreDoc
232 /// <returns> <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
234 public override bool LessThan(System
.Object a
, System
.Object b
)
236 ScoreDoc docA
= (ScoreDoc
) a
;
237 ScoreDoc docB
= (ScoreDoc
) b
;
240 int n
= comparators
.Length
;
242 for (int i
= 0; i
< n
&& c
== 0; ++i
)
244 c
= (fields
[i
].reverse
)?comparators
[i
].Compare(docB
, docA
):comparators
[i
].Compare(docA
, docB
);
246 // avoid random sort order that could lead to duplicates (bug #31241):
248 return docA
.doc
> docB
.doc
;
253 /// <summary> Given a FieldDoc object, stores the values used
254 /// to sort the given document. These values are not the raw
255 /// values out of the index, but the internal representation
256 /// of them. This is so the given search hit can be collated
257 /// by a MultiSearcher with other search hits.
259 /// <param name="doc"> The FieldDoc to store sort values into.
261 /// <returns> The same FieldDoc passed in.
263 /// <seealso cref="Searchable.Search(Weight,Filter,int,Sort)">
265 internal virtual FieldDoc
FillFields(FieldDoc doc
)
267 int n
= comparators
.Length
;
268 System
.IComparable
[] fields
= new System
.IComparable
[n
];
269 for (int i
= 0; i
< n
; ++i
)
270 fields
[i
] = comparators
[i
].SortValue(doc
);
272 //if (maxscore > 1.0f) doc.score /= maxscore; // normalize scores
277 /// <summary>Returns the SortFields being used by this hit queue. </summary>
278 internal virtual SortField
[] GetFields()
283 /// <summary>Internal cache of comparators. Similar to FieldCache, only
284 /// caches comparators instead of term values.
286 internal static readonly System
.Collections
.IDictionary Comparators
= new System
.Collections
.Hashtable();
288 /// <summary>Returns a comparator if it is in the cache. </summary>
289 internal static ScoreDocComparator
Lookup(IndexReader reader
, System
.String field
, int type
, System
.Object factory
)
291 FieldCacheImpl
.Entry entry
= (factory
!= null) ? new FieldCacheImpl
.Entry(field
, factory
) : new FieldCacheImpl
.Entry(field
, type
);
292 lock (Comparators
.SyncRoot
)
294 System
.Collections
.Hashtable readerCache
= (System
.Collections
.Hashtable
) Comparators
[reader
];
295 if (readerCache
== null)
297 return (ScoreDocComparator
) readerCache
[entry
];
301 /// <summary>Stores a comparator into the cache. </summary>
302 internal static System
.Object
Store(IndexReader reader
, System
.String field
, int type
, System
.Object factory
, System
.Object value_Renamed
)
304 FieldCacheImpl
.Entry entry
= (factory
!= null) ? new FieldCacheImpl
.Entry(field
, factory
) : new FieldCacheImpl
.Entry(field
, type
);
305 lock (Comparators
.SyncRoot
)
307 System
.Collections
.Hashtable readerCache
= (System
.Collections
.Hashtable
) Comparators
[reader
];
308 if (readerCache
== null)
310 readerCache
= new System
.Collections
.Hashtable();
311 Comparators
[reader
] = readerCache
;
313 System
.Object tempObject
;
314 tempObject
= readerCache
[entry
];
315 readerCache
[entry
] = value_Renamed
;
320 internal static ScoreDocComparator
GetCachedComparator(IndexReader reader
, System
.String fieldname
, int type
, System
.Globalization
.CultureInfo locale
, SortComparatorSource factory
)
322 if (type
== SortField
.DOC
)
323 return Lucene
.Net
.Search
.ScoreDocComparator_Fields
.INDEXORDER
;
324 if (type
== SortField
.SCORE
)
325 return Lucene
.Net
.Search
.ScoreDocComparator_Fields
.RELEVANCE
;
326 ScoreDocComparator comparator
= Lookup(reader
, fieldname
, type
, factory
);
327 if (comparator
== null)
333 comparator
= ComparatorAuto(reader
, fieldname
);
337 comparator
= ComparatorInt(reader
, fieldname
);
340 case SortField
.FLOAT
:
341 comparator
= ComparatorFloat(reader
, fieldname
);
344 case SortField
.STRING
:
346 comparator
= ComparatorStringLocale(reader
, fieldname
, locale
);
348 comparator
= ComparatorString(reader
, fieldname
);
351 case SortField
.CUSTOM
:
352 comparator
= factory
.NewComparator(reader
, fieldname
);
356 throw new System
.SystemException("unknown field type: " + type
);
359 Store(reader
, fieldname
, type
, factory
, comparator
);
364 /// <summary> Returns a comparator for sorting hits according to a field containing integers.</summary>
365 /// <param name="reader"> Index to use.
367 /// <param name="fieldname"> Field containg integer values.
369 /// <returns> Comparator for sorting hits.
371 /// <throws> IOException If an error occurs reading the index. </throws>
372 internal static ScoreDocComparator
ComparatorInt(IndexReader reader
, System
.String fieldname
)
374 System
.String field
= String
.Intern(fieldname
);
375 int[] fieldOrder
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetInts(reader
, field
);
376 return new AnonymousClassScoreDocComparator(fieldOrder
);
379 /// <summary> Returns a comparator for sorting hits according to a field containing floats.</summary>
380 /// <param name="reader"> Index to use.
382 /// <param name="fieldname"> Field containg float values.
384 /// <returns> Comparator for sorting hits.
386 /// <throws> IOException If an error occurs reading the index. </throws>
387 internal static ScoreDocComparator
ComparatorFloat(IndexReader reader
, System
.String fieldname
)
389 System
.String field
= String
.Intern(fieldname
);
390 float[] fieldOrder
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetFloats(reader
, field
);
391 return new AnonymousClassScoreDocComparator1(fieldOrder
);
394 /// <summary> Returns a comparator for sorting hits according to a field containing strings.</summary>
395 /// <param name="reader"> Index to use.
397 /// <param name="fieldname"> Field containg string values.
399 /// <returns> Comparator for sorting hits.
401 /// <throws> IOException If an error occurs reading the index. </throws>
402 internal static ScoreDocComparator
ComparatorString(IndexReader reader
, System
.String fieldname
)
404 System
.String field
= String
.Intern(fieldname
);
405 Lucene
.Net
.Search
.StringIndex index
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetStringIndex(reader
, field
);
406 return new AnonymousClassScoreDocComparator2(index
);
409 /// <summary> Returns a comparator for sorting hits according to a field containing strings.</summary>
410 /// <param name="reader"> Index to use.
412 /// <param name="fieldname"> Field containg string values.
414 /// <returns> Comparator for sorting hits.
416 /// <throws> IOException If an error occurs reading the index. </throws>
417 internal static ScoreDocComparator
ComparatorStringLocale(IndexReader reader
, System
.String fieldname
, System
.Globalization
.CultureInfo locale
)
419 System
.Globalization
.CompareInfo collator
= locale
.CompareInfo
;
420 System
.String field
= String
.Intern(fieldname
);
421 System
.String
[] index
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetStrings(reader
, field
);
422 return new AnonymousClassScoreDocComparator3(collator
, index
);
425 /// <summary> Returns a comparator for sorting hits according to values in the given field.
426 /// The terms in the field are looked at to determine whether they contain integers,
427 /// floats or strings. Once the type is determined, one of the other static methods
428 /// in this class is called to get the comparator.
430 /// <param name="reader"> Index to use.
432 /// <param name="fieldname"> Field containg values.
434 /// <returns> Comparator for sorting hits.
436 /// <throws> IOException If an error occurs reading the index. </throws>
437 internal static ScoreDocComparator
ComparatorAuto(IndexReader reader
, System
.String fieldname
)
439 System
.String field
= String
.Intern(fieldname
);
440 System
.Object lookupArray
= Lucene
.Net
.Search
.FieldCache_Fields
.DEFAULT
.GetAuto(reader
, field
);
441 if (lookupArray
is Lucene
.Net
.Search
.StringIndex
)
443 return ComparatorString(reader
, field
);
445 else if (lookupArray
is int[])
447 return ComparatorInt(reader
, field
);
449 else if (lookupArray
is float[])
451 return ComparatorFloat(reader
, field
);
453 else if (lookupArray
is System
.String
[])
455 return ComparatorString(reader
, field
);
459 throw new System
.SystemException("unknown data type in field '" + field
+ "'");