/*
* 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 System.Globalization;
using Lucene.Net.Support;
using NumericField = Lucene.Net.Documents.NumericField;
using IndexReader = Lucene.Net.Index.IndexReader;
using Single = Lucene.Net.Support.Single;
using Term = Lucene.Net.Index.Term;
using TermEnum = Lucene.Net.Index.TermEnum;
using StringHelper = Lucene.Net.Util.StringHelper;
namespace Lucene.Net.Search
{
/// Stores information about how to sort documents by terms in an individual
/// field. Fields must be indexed in order to sort by them.
///
/// Created: Feb 11, 2004 1:25:29 PM
///
///
[Serializable]
public class SortField
{
/// Sort by document score (relevancy). Sort values are Float and higher
/// values are at the front.
///
public const int SCORE = 0;
/// Sort by document number (index order). Sort values are Integer and lower
/// values are at the front.
///
public const int DOC = 1;
// reserved, in Lucene 2.9, there was a constant: AUTO = 2
/// Sort using term values as Strings. Sort values are String and lower
/// values are at the front.
///
public const int STRING = 3;
/// Sort using term values as encoded Integers. Sort values are Integer and
/// lower values are at the front.
///
public const int INT = 4;
/// Sort using term values as encoded Floats. Sort values are Float and
/// lower values are at the front.
///
public const int FLOAT = 5;
/// Sort using term values as encoded Longs. Sort values are Long and
/// lower values are at the front.
///
public const int LONG = 6;
/// Sort using term values as encoded Doubles. Sort values are Double and
/// lower values are at the front.
///
public const int DOUBLE = 7;
/// Sort using term values as encoded Shorts. Sort values are Short and
/// lower values are at the front.
///
public const int SHORT = 8;
/// Sort using a custom Comparator. Sort values are any Comparable and
/// sorting is done according to natural order.
///
public const int CUSTOM = 9;
/// Sort using term values as encoded Bytes. Sort values are Byte and
/// lower values are at the front.
///
public const int BYTE = 10;
/// Sort using term values as Strings, but comparing by
/// value (using String.compareTo) for all comparisons.
/// This is typically slower than , which
/// uses ordinals to do the sorting.
///
public const int STRING_VAL = 11;
// IMPLEMENTATION NOTE: the FieldCache.STRING_INDEX is in the same "namespace"
// as the above static int values. Any new values must not have the same value
// as FieldCache.STRING_INDEX.
/// Represents sorting by document score (relevancy).
public static readonly SortField FIELD_SCORE = new SortField(null, SCORE);
/// Represents sorting by document number (index order).
public static readonly SortField FIELD_DOC = new SortField(null, DOC);
private System.String field;
private int type; // defaults to determining type dynamically
private System.Globalization.CultureInfo locale; // defaults to "natural order" (no Locale)
internal bool reverse = false; // defaults to natural order
private Lucene.Net.Search.Parser parser;
// Used for CUSTOM sort
private FieldComparatorSource comparatorSource;
/// Creates a sort by terms in the given field with the type of term
/// values explicitly given.
///
/// Name of field to sort by. Can be null if
/// type is SCORE or DOC.
///
/// Type of values in the terms.
///
public SortField(System.String field, int type)
{
InitFieldType(field, type);
}
/// Creates a sort, possibly in reverse, by terms in the given field with the
/// type of term values explicitly given.
///
/// Name of field to sort by. Can be null if
/// type is SCORE or DOC.
///
/// Type of values in the terms.
///
/// True if natural order should be reversed.
///
public SortField(System.String field, int type, bool reverse)
{
InitFieldType(field, type);
this.reverse = reverse;
}
/// Creates a sort by terms in the given field, parsed
/// to numeric values using a custom .
///
/// Name of field to sort by. Must not be null.
///
/// Instance of a ,
/// which must subclass one of the existing numeric
/// parsers from . Sort type is inferred
/// by testing which numeric parser the parser subclasses.
///
/// IllegalArgumentException if the parser fails to
/// subclass an existing numeric parser, or field is null
///
public SortField(System.String field, Lucene.Net.Search.Parser parser):this(field, parser, false)
{
}
/// Creates a sort, possibly in reverse, by terms in the given field, parsed
/// to numeric values using a custom .
///
/// Name of field to sort by. Must not be null.
///
/// Instance of a ,
/// which must subclass one of the existing numeric
/// parsers from . Sort type is inferred
/// by testing which numeric parser the parser subclasses.
///
/// True if natural order should be reversed.
///
/// IllegalArgumentException if the parser fails to
/// subclass an existing numeric parser, or field is null
///
public SortField(System.String field, Lucene.Net.Search.Parser parser, bool reverse)
{
if (parser is Lucene.Net.Search.IntParser)
InitFieldType(field, INT);
else if (parser is Lucene.Net.Search.FloatParser)
InitFieldType(field, FLOAT);
else if (parser is Lucene.Net.Search.ShortParser)
InitFieldType(field, SHORT);
else if (parser is Lucene.Net.Search.ByteParser)
InitFieldType(field, BYTE);
else if (parser is Lucene.Net.Search.LongParser)
InitFieldType(field, LONG);
else if (parser is Lucene.Net.Search.DoubleParser)
InitFieldType(field, DOUBLE);
else
{
throw new System.ArgumentException("Parser instance does not subclass existing numeric parser from FieldCache (got " + parser + ")");
}
this.reverse = reverse;
this.parser = parser;
}
/// Creates a sort by terms in the given field sorted
/// according to the given locale.
///
/// Name of field to sort by, cannot be null.
///
/// Locale of values in the field.
///
public SortField(System.String field, System.Globalization.CultureInfo locale)
{
InitFieldType(field, STRING);
this.locale = locale;
}
/// Creates a sort, possibly in reverse, by terms in the given field sorted
/// according to the given locale.
///
/// Name of field to sort by, cannot be null.
///
/// Locale of values in the field.
///
public SortField(System.String field, System.Globalization.CultureInfo locale, bool reverse)
{
InitFieldType(field, STRING);
this.locale = locale;
this.reverse = reverse;
}
/// Creates a sort with a custom comparison function.
/// Name of field to sort by; cannot be null.
///
/// Returns a comparator for sorting hits.
///
public SortField(System.String field, FieldComparatorSource comparator)
{
InitFieldType(field, CUSTOM);
this.comparatorSource = comparator;
}
/// Creates a sort, possibly in reverse, with a custom comparison function.
/// Name of field to sort by; cannot be null.
///
/// Returns a comparator for sorting hits.
///
/// True if natural order should be reversed.
///
public SortField(System.String field, FieldComparatorSource comparator, bool reverse)
{
InitFieldType(field, CUSTOM);
this.reverse = reverse;
this.comparatorSource = comparator;
}
// Sets field & type, and ensures field is not NULL unless
// type is SCORE or DOC
private void InitFieldType(System.String field, int type)
{
this.type = type;
if (field == null)
{
if (type != SCORE && type != DOC)
throw new System.ArgumentException("field can only be null when type is SCORE or DOC");
}
else
{
this.field = StringHelper.Intern(field);
}
}
/// Returns the name of the field. Could return null
/// if the sort is by SCORE or DOC.
///
/// Name of field, possibly <c>null</c>.
public virtual string Field
{
get { return field; }
}
/// Returns the type of contents in the field.
/// One of the constants SCORE, DOC, STRING, INT or FLOAT.
public virtual int Type
{
get { return type; }
}
/// Returns the Locale by which term values are interpreted.
/// May return null if no Locale was specified.
///
/// Locale, or <c>null</c>.
public virtual CultureInfo Locale
{
get { return locale; }
}
/// Returns the instance of a parser that fits to the given sort type.
/// May return null if no parser was specified. Sorting is using the default parser then.
///
/// An instance of a <see cref="FieldCache" /> parser, or <c>null</c>.
public virtual Parser Parser
{
get { return parser; }
}
/// Returns whether the sort should be reversed.
/// True if natural order should be reversed.
public virtual bool Reverse
{
get { return reverse; }
}
///
/// Returns the used for
/// custom sorting
///
public virtual FieldComparatorSource ComparatorSource
{
get { return comparatorSource; }
}
public override System.String ToString()
{
System.Text.StringBuilder buffer = new System.Text.StringBuilder();
switch (type)
{
case SCORE:
buffer.Append("");
break;
case DOC:
buffer.Append("");
break;
case STRING:
buffer.Append("");
break;
case STRING_VAL:
buffer.Append("");
break;
case BYTE:
buffer.Append("");
break;
case SHORT:
buffer.Append("");
break;
case INT:
buffer.Append("");
break;
case LONG:
buffer.Append("");
break;
case FLOAT:
buffer.Append("");
break;
case DOUBLE:
buffer.Append("");
break;
case CUSTOM:
buffer.Append("');
break;
default:
buffer.Append("??: \"").Append(field).Append("\">");
break;
}
if (locale != null)
buffer.Append('(').Append(locale).Append(')');
if (parser != null)
buffer.Append('(').Append(parser).Append(')');
if (reverse)
buffer.Append('!');
return buffer.ToString();
}
/// Returns true if o is equal to this. If a
/// or
/// was provided, it must properly
/// implement equals (unless a singleton is always used).
///
public override bool Equals(System.Object o)
{
if (this == o)
return true;
if (!(o is SortField))
return false;
SortField other = (SortField) o;
return ((System.Object) other.field == (System.Object) this.field && other.type == this.type &&
other.reverse == this.reverse &&
(other.locale == null ? this.locale == null : other.locale.Equals(this.locale)) &&
(other.comparatorSource == null
? this.comparatorSource == null
: other.comparatorSource.Equals(this.comparatorSource)) &&
(other.parser == null ? this.parser == null : other.parser.Equals(this.parser)));
}
/// Returns true if o is equal to this. If a
/// (deprecated) or
/// was provided, it must properly
/// implement hashCode (unless a singleton is always
/// used).
///
public override int GetHashCode()
{
int hash = type ^ 0x346565dd + (reverse ? Boolean.TrueString.GetHashCode() : Boolean.FalseString.GetHashCode()) ^ unchecked((int) 0xaf5998bb);
if (field != null)
hash += (field.GetHashCode() ^ unchecked((int) 0xff5685dd));
if (locale != null)
{
hash += (locale.GetHashCode() ^ 0x08150815);
}
if (comparatorSource != null)
hash += comparatorSource.GetHashCode();
if (parser != null)
hash += (parser.GetHashCode() ^ 0x3aaf56ff);
return hash;
}
//// field must be interned after reading from stream
// private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
// in.defaultReadObject();
// if (field != null)
// field = StringHelper.intern(field);
// }
[System.Runtime.Serialization.OnDeserialized]
internal void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
field = StringHelper.Intern(field);
}
/// Returns the to use for
/// sorting.
///
/// NOTE: This API is experimental and might change in
/// incompatible ways in the next release.
///
///
/// number of top hits the queue will store
///
/// position of this SortField within
///. The comparator is primary if sortPos==0,
/// secondary if sortPos==1, etc. Some comparators can
/// optimize themselves when they are the primary sort.
///
/// to use when sorting
///
public virtual FieldComparator GetComparator(int numHits, int sortPos)
{
if (locale != null)
{
// TODO: it'd be nice to allow FieldCache.getStringIndex
// to optionally accept a Locale so sorting could then use
// the faster StringComparator impls
return new FieldComparator.StringComparatorLocale(numHits, field, locale);
}
switch (type)
{
case SortField.SCORE:
return new FieldComparator.RelevanceComparator(numHits);
case SortField.DOC:
return new FieldComparator.DocComparator(numHits);
case SortField.INT:
return new FieldComparator.IntComparator(numHits, field, parser);
case SortField.FLOAT:
return new FieldComparator.FloatComparator(numHits, field, parser);
case SortField.LONG:
return new FieldComparator.LongComparator(numHits, field, parser);
case SortField.DOUBLE:
return new FieldComparator.DoubleComparator(numHits, field, parser);
case SortField.BYTE:
return new FieldComparator.ByteComparator(numHits, field, parser);
case SortField.SHORT:
return new FieldComparator.ShortComparator(numHits, field, parser);
case SortField.CUSTOM:
System.Diagnostics.Debug.Assert(comparatorSource != null);
return comparatorSource.NewComparator(field, numHits, sortPos, reverse);
case SortField.STRING:
return new FieldComparator.StringOrdValComparator(numHits, field, sortPos, reverse);
case SortField.STRING_VAL:
return new FieldComparator.StringValComparator(numHits, field);
default:
throw new System.SystemException("Illegal sort type: " + type);
}
}
}
}