/*
* 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 NumericField = Lucene.Net.Documents.NumericField;
using IndexReader = Lucene.Net.Index.IndexReader;
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
///
///
/// lucene 1.4
///
/// $Id: SortField.java 801344 2009-08-05 18:05:06Z yonik $
///
///
///
[Serializable]
public class SortField: System.Runtime.Serialization.ISerializable
{
/// 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;
/// Guess type of sort based on field contents. A regular expression is used
/// to look at the first term indexed for the field and determine if it
/// represents an integer number, a floating point number, or just arbitrary
/// string characters.
///
/// Please specify the exact type, instead.
/// Especially, guessing does not work with the new
/// {@link NumericField} type.
///
[Obsolete("Please specify the exact type, instead. Especially, guessing does not work with the new NumericField type.")]
public const int 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 {@link #STRING}, 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 = AUTO; // 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 SortComparatorSource factory;
private Lucene.Net.Search.Parser parser;
// Used for CUSTOM sort
private FieldComparatorSource comparatorSource;
private bool useLegacy = false; // remove in Lucene 3.0
/// Creates a sort by terms in the given field where the type of term value
/// is determined dynamically ({@link #AUTO AUTO}).
///
/// Name of field to sort by, cannot be
/// null
.
///
/// Please specify the exact type instead.
///
[Obsolete("Please specify the exact type instead.")]
public SortField(System.String field)
{
InitFieldType(field, AUTO);
}
/// Creates a sort, possibly in reverse, by terms in the given field where
/// the type of term value is determined dynamically ({@link #AUTO AUTO}).
///
/// Name of field to sort by, cannot be null
.
///
/// True if natural order should be reversed.
///
/// Please specify the exact type instead.
///
[Obsolete("Please specify the exact type instead.")]
public SortField(System.String field, bool reverse)
{
InitFieldType(field, AUTO);
this.reverse = reverse;
}
/// 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 {@link FieldCache.Parser}.
///
/// Name of field to sort by. Must not be null.
///
/// Instance of a {@link FieldCache.Parser},
/// which must subclass one of the existing numeric
/// parsers from {@link FieldCache}. 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 {@link FieldCache.Parser}.
///
/// Name of field to sort by. Must not be null.
///
/// Instance of a {@link FieldCache.Parser},
/// which must subclass one of the existing numeric
/// parsers from {@link FieldCache}. 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.
///
/// use SortField (String field, FieldComparatorSource comparator)
///
[Obsolete("use SortField (String field, FieldComparatorSource comparator)")]
public SortField(System.String field, SortComparatorSource comparator)
{
InitFieldType(field, CUSTOM);
SetUseLegacySearch(true);
this.factory = comparator;
}
/// 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.
///
/// use SortField (String field, FieldComparatorSource comparator, boolean reverse)
///
[Obsolete("use SortField(String field, FieldComparatorSource comparator, boolean reverse)")]
public SortField(System.String field, SortComparatorSource comparator, bool reverse)
{
InitFieldType(field, CUSTOM);
SetUseLegacySearch(true);
this.reverse = reverse;
this.factory = 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 null
.
///
public virtual System.String GetField()
{
return field;
}
/// Returns the type of contents in the field.
/// One of the constants SCORE, DOC, AUTO, STRING, INT or FLOAT.
///
public new virtual int GetType()
{
return type;
}
/// Returns the Locale by which term values are interpreted.
/// May return null
if no Locale was specified.
///
/// Locale, or null
.
///
public virtual System.Globalization.CultureInfo GetLocale()
{
return locale;
}
/// Returns the instance of a {@link FieldCache} 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 {@link FieldCache} parser, or null
.
///
public virtual Lucene.Net.Search.Parser GetParser()
{
return parser;
}
/// Returns whether the sort should be reversed.
/// True if natural order should be reversed.
///
public virtual bool GetReverse()
{
return reverse;
}
/// use {@link #GetComparatorSource()}
///
[Obsolete("use GetComparatorSource()")]
public virtual SortComparatorSource GetFactory()
{
return factory;
}
public virtual FieldComparatorSource GetComparatorSource()
{
return comparatorSource;
}
/// Use legacy IndexSearch implementation: search with a DirectoryReader rather
/// than passing a single hit collector to multiple SegmentReaders.
///
///
/// true for legacy behavior
///
/// will be removed in Lucene 3.0.
///
[Obsolete("will be removed in Lucene 3.0.")]
public virtual void SetUseLegacySearch(bool legacy)
{
this.useLegacy = legacy;
}
/// if true, IndexSearch will use legacy sorting search implementation.
/// eg. multiple Priority Queues.
///
/// will be removed in Lucene 3.0.
///
[Obsolete("will be removed in Lucene 3.0.")]
public virtual bool GetUseLegacySearch()
{
return this.useLegacy;
}
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 AUTO:
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
/// {@link SortComparatorSource} (deprecated) or {@link
/// FieldCache.Parser} 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.factory == null?this.factory == null:other.factory.Equals(this.factory)) && (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
/// {@link SortComparatorSource} (deprecated) or {@link
/// FieldCache.Parser} 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 (factory != null)
hash += (factory.GetHashCode() ^ 0x34987555);
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);
// }
///
/// Lucene.Net specific. Needed for Serialization
///
///
///
public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
//Should we add other fields as in NumericRangeQuery? {{DIGY}}
info.AddValue("type", type);
info.AddValue("useLegacy", useLegacy);
info.AddValue("reverse", reverse);
info.AddValue("locale", locale);
info.AddValue("comparatorSource", comparatorSource);
info.AddValue("factory", factory);
info.AddValue("parser", parser);
info.AddValue("field", field);
}
///
/// Lucene.Net specific. Needed for deserialization
///
///
///
protected SortField(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
//Should we add other fields as in NumericRangeQuery? {{DIGY}}
type = (int)info.GetValue("type", typeof(int));
useLegacy = (bool)info.GetValue("useLegacy", typeof(bool));
reverse = (bool)info.GetValue("reverse", typeof(bool));
locale = (System.Globalization.CultureInfo)info.GetValue("locale", typeof(System.Globalization.CultureInfo));
comparatorSource = (FieldComparatorSource)info.GetValue("comparatorSource", typeof(FieldComparatorSource));
factory = (SortComparatorSource)info.GetValue("factory", typeof(FieldComparatorSource));
parser = (Parser)info.GetValue("parser", typeof(Parser));
field = StringHelper.Intern((string)info.GetValue("field", typeof(string)));
}
/// Returns the {@link FieldComparator} 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 {@link
/// Sort}. The comparator is primary if sortPos==0,
/// secondary if sortPos==1, etc. Some comparators can
/// optimize themselves when they are the primary sort.
///
/// {@link FieldComparator} 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(factory == null && 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);
}
}
/// Attempts to detect the given field type for an IndexReader.
///
///
[Obsolete]
internal static int DetectFieldType(IndexReader reader, System.String fieldKey)
{
System.String field = StringHelper.Intern(fieldKey);
TermEnum enumerator = reader.Terms(new Term(field));
try
{
Term term = enumerator.Term();
if (term == null)
{
throw new System.SystemException("no terms in field " + field + " - cannot determine sort type");
}
int ret = 0;
if ((System.Object) term.Field() == (System.Object) field)
{
System.String termtext = term.Text().Trim();
int tmpI32; long tmpI64; float tmpF;
if (System.Int32.TryParse(termtext, out tmpI32)) ret = SortField.INT;
else if (System.Int64.TryParse(termtext, out tmpI64)) ret = SortField.LONG;
else if (SupportClass.Single.TryParse(termtext, out tmpF)) ret = SortField.FLOAT;
else ret = SortField.STRING;
}
else
{
throw new System.SystemException("field \"" + field + "\" does not appear to be indexed");
}
return ret;
}
finally
{
enumerator.Close();
}
}
}
}