/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using PriorityQueue = Lucene.Net.Util.PriorityQueue;
namespace Lucene.Net.Search
{
/// Expert: Collects sorted results from Searchable's and collates them.
/// The elements put into this queue must be of type FieldDoc.
///
/// Created: Feb 11, 2004 2:04:21 PM
///
///
/// lucene 1.4
///
/// $Id: FieldDocSortedHitQueue.java 695514 2008-09-15 15:42:11Z otis $
///
class FieldDocSortedHitQueue:PriorityQueue
{
// this cannot contain AUTO fields - any AUTO fields should
// have been resolved by the time this class is used.
internal volatile SortField[] fields;
// used in the case where the fields are sorted by locale
// based strings
internal volatile System.Globalization.CompareInfo[] collators;
/// Creates a hit queue sorted by the given list of fields.
/// Fieldable names, in priority order (highest priority first).
///
/// The number of hits to retain. Must be greater than zero.
///
internal FieldDocSortedHitQueue(SortField[] fields, int size)
{
this.fields = fields;
this.collators = HasCollators(fields);
Initialize(size);
}
/// Allows redefinition of sort fields if they are null
.
/// This is to handle the case using ParallelMultiSearcher where the
/// original list contains AUTO and we don't know the actual sort
/// type until the values come back. The fields can only be set once.
/// This method is thread safe.
///
///
///
internal virtual void SetFields(SortField[] fields)
{
lock (this)
{
if (this.fields == null)
{
this.fields = fields;
this.collators = HasCollators(fields);
}
}
}
/// Returns the fields being used to sort.
internal virtual SortField[] GetFields()
{
return fields;
}
/// Returns an array of collators, possibly null
. The collators
/// correspond to any SortFields which were given a specific locale.
///
/// Array of sort fields.
///
/// Array, possibly null
.
///
private System.Globalization.CompareInfo[] HasCollators(SortField[] fields)
{
if (fields == null)
return null;
System.Globalization.CompareInfo[] ret = new System.Globalization.CompareInfo[fields.Length];
for (int i = 0; i < fields.Length; ++i)
{
System.Globalization.CultureInfo locale = fields[i].GetLocale();
if (locale != null)
ret[i] = locale.CompareInfo;
}
return ret;
}
/// Returns whether a
is less relevant than b
.
/// ScoreDoc
///
/// ScoreDoc
///
/// true
if document a
should be sorted after document b
.
///
public override bool LessThan(System.Object a, System.Object b)
{
FieldDoc docA = (FieldDoc) a;
FieldDoc docB = (FieldDoc) b;
int n = fields.Length;
int c = 0;
for (int i = 0; i < n && c == 0; ++i)
{
int type = fields[i].GetType();
switch (type)
{
case SortField.SCORE: {
float r1 = (float) ((System.Single) docA.fields[i]);
float r2 = (float) ((System.Single) docB.fields[i]);
if (r1 > r2)
c = - 1;
if (r1 < r2)
c = 1;
break;
}
case SortField.DOC:
case SortField.INT: {
int i1 = ((System.Int32) docA.fields[i]);
int i2 = ((System.Int32) docB.fields[i]);
if (i1 < i2)
c = - 1;
if (i1 > i2)
c = 1;
break;
}
case SortField.LONG: {
long l1 = (long) ((System.Int64) docA.fields[i]);
long l2 = (long) ((System.Int64) docB.fields[i]);
if (l1 < l2)
c = - 1;
if (l1 > l2)
c = 1;
break;
}
case SortField.STRING: {
System.String s1 = (System.String) docA.fields[i];
System.String s2 = (System.String) docB.fields[i];
// null values need to be sorted first, because of how FieldCache.getStringIndex()
// works - in that routine, any documents without a value in the given field are
// put first. If both are null, the next SortField is used
if (s1 == null)
c = (s2 == null)?0:- 1;
else if (s2 == null)
c = 1;
//
else if (fields[i].GetLocale() == null)
{
c = String.CompareOrdinal(s1, s2);
}
else
{
c = collators[i].Compare(s1.ToString(), s2.ToString());
}
break;
}
case SortField.FLOAT: {
float f1 = (float) ((System.Single) docA.fields[i]);
float f2 = (float) ((System.Single) docB.fields[i]);
if (f1 < f2)
c = - 1;
if (f1 > f2)
c = 1;
break;
}
case SortField.DOUBLE: {
double d1 = ((System.Double) docA.fields[i]);
double d2 = ((System.Double) docB.fields[i]);
if (d1 < d2)
c = - 1;
if (d1 > d2)
c = 1;
break;
}
case SortField.BYTE: {
int i1 = (sbyte) ((System.SByte) docA.fields[i]);
int i2 = (sbyte) ((System.SByte) docB.fields[i]);
if (i1 < i2)
c = - 1;
if (i1 > i2)
c = 1;
break;
}
case SortField.SHORT: {
int i1 = (short) ((System.Int16) docA.fields[i]);
int i2 = (short) ((System.Int16) docB.fields[i]);
if (i1 < i2)
c = - 1;
if (i1 > i2)
c = 1;
break;
}
case SortField.CUSTOM: {
c = docA.fields[i].CompareTo(docB.fields[i]);
break;
}
case SortField.AUTO: {
// we cannot handle this - even if we determine the type of object (Float or
// Integer), we don't necessarily know how to compare them (both SCORE and
// FLOAT contain floats, but are sorted opposite of each other). Before
// we get here, each AUTO should have been replaced with its actual value.
throw new System.SystemException("FieldDocSortedHitQueue cannot use an AUTO SortField");
}
default: {
throw new System.SystemException("invalid SortField type: " + type);
}
}
if (fields[i].GetReverse())
{
c = - c;
}
}
// avoid random sort order that could lead to duplicates (bug #31241):
if (c == 0)
return docA.doc > docB.doc;
return c > 0;
}
}
}