LUCENE-1478: allow SortField to use a custom numeric FieldCache parser

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@724484 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2008-12-08 21:07:45 +00:00
parent e0efcd52ea
commit efc985cbf4
9 changed files with 332 additions and 75 deletions

View File

@ -124,6 +124,10 @@ New features
handles a subset of this filter, has been deprecated.
(Andi Vajda, Steven Rowe via Mark Miller)
10. LUCENE-1478: Added new SortField constructor allowing you to
specify a custom FieldCache parser to generate numeric values from
terms for a field. (Uwe Schindler via Mike McCandless)
Optimizations
1. LUCENE-1427: Fixed QueryWrapperFilter to not waste time computing

View File

@ -21,6 +21,8 @@ import java.util.Date;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.ExtendedFieldCache;
/**
*This is a helper class to construct the trie-based index entries for numerical values.
@ -68,6 +70,26 @@ public final class TrieUtils {
/** Character used as lower end */
public static final char TRIE_CODED_SYMBOL_MIN=(char)0x100;
/**
* A parser instance for filling a {@link ExtendedFieldCache}, that parses trie encoded fields as longs,
* auto detecting the trie encoding variant using the String length.
*/
public static final ExtendedFieldCache.LongParser FIELD_CACHE_LONG_PARSER_AUTO=new ExtendedFieldCache.LongParser(){
public final long parseLong(String val) {
return trieCodedToLongAuto(val);
}
};
/**
* A parser instance for filling a {@link ExtendedFieldCache}, that parses trie encoded fields as doubles,
* auto detecting the trie encoding variant using the String length.
*/
public static final ExtendedFieldCache.DoubleParser FIELD_CACHE_DOUBLE_PARSER_AUTO=new ExtendedFieldCache.DoubleParser(){
public final double parseDouble(String val) {
return trieCodedToDoubleAuto(val);
}
};
private static TrieUtils defaultTrieVariant=TrieUtils.VARIANT_8BIT;
/**
@ -130,6 +152,22 @@ public final class TrieUtils {
return autoDetectVariant(s).trieCodedToDate(s);
}
/**
* A factory method, that generates a {@link SortField} instance for sorting trie encoded values,
* automatically detecting the trie encoding variant using the String length.
*/
public static final SortField getSortFieldAuto(final String field) {
return new SortField(field, FIELD_CACHE_LONG_PARSER_AUTO);
}
/**
* A factory method, that generates a {@link SortField} instance for sorting trie encoded values,
* automatically detecting the trie encoding variant using the String length.
*/
public static final SortField getSortFieldAuto(final String field, boolean reverse) {
return new SortField(field, FIELD_CACHE_LONG_PARSER_AUTO, reverse);
}
// TrieUtils instance's part
private TrieUtils(int bits) {
@ -338,6 +376,30 @@ public final class TrieUtils {
addConvertedTrieCodedDocumentField(ldoc, fieldname, longToTrieCoded(val), index, store);
}
/** A factory method, that generates a {@link SortField} instance for sorting trie encoded values. */
public SortField getSortField(final String field) {
return new SortField(field, FIELD_CACHE_LONG_PARSER);
}
/** A factory method, that generates a {@link SortField} instance for sorting trie encoded values. */
public SortField getSortField(final String field, boolean reverse) {
return new SortField(field, FIELD_CACHE_LONG_PARSER, reverse);
}
/** A parser instance for filling a {@link ExtendedFieldCache}, that parses trie encoded fields as longs. */
public final ExtendedFieldCache.LongParser FIELD_CACHE_LONG_PARSER=new ExtendedFieldCache.LongParser(){
public final long parseLong(String val) {
return trieCodedToLong(val);
}
};
/** A parser instance for filling a {@link ExtendedFieldCache}, that parses trie encoded fields as doubles. */
public final ExtendedFieldCache.DoubleParser FIELD_CACHE_DOUBLE_PARSER=new ExtendedFieldCache.DoubleParser(){
public final double parseDouble(String val) {
return trieCodedToDouble(val);
}
};
private final long mask;
/** Number of bits used in this trie variant (2, 4, or 8) */

View File

@ -163,7 +163,7 @@ public class TestTrieRangeQuery extends LuceneTestCase
private void testRangeSplit(final TrieUtils variant) throws Exception {
String field="ascfield"+variant.TRIE_BITS;
// 50 random tests, the tests may also return 0 results, if min>max, but this is ok
// 50 random tests
for (int i=0; i<50; i++) {
long lower=(long)(rnd.nextDouble()*10000L);
long upper=(long)(rnd.nextDouble()*10000L);
@ -188,4 +188,40 @@ public class TestTrieRangeQuery extends LuceneTestCase
testRangeSplit(TrieUtils.VARIANT_2BIT);
}
private void testSorting(final TrieUtils variant) throws Exception {
String field="field"+variant.TRIE_BITS;
// 10 random tests, the index order is ascending,
// so using a reverse sort field should retun descending documents
for (int i=0; i<10; i++) {
long lower=(long)(rnd.nextDouble()*10000L*distance);
long upper=(long)(rnd.nextDouble()*10000L*distance);
if (lower>upper) {
long a=lower; lower=upper; upper=a;
}
TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), variant);
TopDocs topDocs = searcher.search(tq, null, 10000, new Sort(variant.getSortField(field, true)));
if (topDocs.totalHits==0) continue;
ScoreDoc[] sd = topDocs.scoreDocs;
assertNotNull(sd);
long last=variant.trieCodedToLong(searcher.doc(sd[0].doc).get(field));
for (int j=1; j<sd.length; j++) {
long act=variant.trieCodedToLong(searcher.doc(sd[j].doc).get(field));
assertTrue("Docs should be sorted backwards", last>act );
last=act;
}
}
}
public void testSorting_8bit() throws Exception {
testSorting(TrieUtils.VARIANT_8BIT);
}
public void testSorting_4bit() throws Exception {
testSorting(TrieUtils.VARIANT_4BIT);
}
public void testSorting_2bit() throws Exception {
testSorting(TrieUtils.VARIANT_2BIT);
}
}

View File

@ -27,14 +27,14 @@ import java.io.IOException;
*
**/
public interface ExtendedFieldCache extends FieldCache {
public interface LongParser {
public interface LongParser extends Parser {
/**
* Return an long representation of this field's value.
*/
public long parseLong(String string);
}
public interface DoubleParser {
public interface DoubleParser extends Parser {
/**
* Return an long representation of this field's value.
*/

View File

@ -74,10 +74,18 @@ public interface FieldCache {
}
}
/**
* Marker interface as super-interface to all parsers. It
* is used to specify a custom parser to {@link
* SortField#SortField(String, FieldCache.Parser)}.
*/
public interface Parser {
}
/** Interface to parse bytes from document fields.
* @see FieldCache#getBytes(IndexReader, String, FieldCache.ByteParser)
*/
public interface ByteParser {
public interface ByteParser extends Parser {
/** Return a single Byte representation of this field's value. */
public byte parseByte(String string);
}
@ -85,7 +93,7 @@ public interface FieldCache {
/** Interface to parse shorts from document fields.
* @see FieldCache#getShorts(IndexReader, String, FieldCache.ShortParser)
*/
public interface ShortParser {
public interface ShortParser extends Parser {
/** Return a short representation of this field's value. */
public short parseShort(String string);
}
@ -93,7 +101,7 @@ public interface FieldCache {
/** Interface to parse ints from document fields.
* @see FieldCache#getInts(IndexReader, String, FieldCache.IntParser)
*/
public interface IntParser {
public interface IntParser extends Parser {
/** Return an integer representation of this field's value. */
public int parseInt(String string);
}
@ -101,7 +109,7 @@ public interface FieldCache {
/** Interface to parse floats from document fields.
* @see FieldCache#getFloats(IndexReader, String, FieldCache.FloatParser)
*/
public interface FloatParser {
public interface FloatParser extends Parser {
/** Return an float representation of this field's value. */
public float parseFloat(String string);
}

View File

@ -88,7 +88,7 @@ implements FieldCache {
static class Entry {
final String field; // which Fieldable
final int type; // which SortField type
final Object custom; // which custom comparator
final Object custom; // which custom comparator or parser
final Locale locale; // the locale we're sorting (if string)
/** Creates one of these objects. */
@ -99,7 +99,7 @@ implements FieldCache {
this.locale = locale;
}
/** Creates one of these objects for a custom comparator. */
/** Creates one of these objects for a custom comparator/parser. */
Entry (String field, Object custom) {
this.field = field.intern();
this.type = SortField.CUSTOM;
@ -107,6 +107,14 @@ implements FieldCache {
this.locale = null;
}
/** Creates one of these objects for a custom type with parser, needed by FieldSortedHitQueue. */
Entry (String field, int type, Parser parser) {
this.field = field.intern();
this.type = type;
this.custom = parser;
this.locale = null;
}
/** Two of these are equal iff they reference the same field and type. */
public boolean equals (Object o) {
if (o instanceof Entry) {

View File

@ -52,12 +52,17 @@ extends PriorityQueue {
this.fields = new SortField[n];
for (int i=0; i<n; ++i) {
String fieldname = fields[i].getField();
comparators[i] = getCachedComparator (reader, fieldname, fields[i].getType(), fields[i].getLocale(), fields[i].getFactory());
if (comparators[i].sortType() == SortField.STRING) {
this.fields[i] = new SortField (fieldname, fields[i].getLocale(), fields[i].getReverse());
comparators[i] = getCachedComparator (reader, fieldname, fields[i].getType(), fields[i].getParser(), fields[i].getLocale(), fields[i].getFactory());
// new SortField instances must only be created when auto-detection is in use
if (fields[i].getType() == SortField.AUTO) {
if (comparators[i].sortType() == SortField.STRING) {
this.fields[i] = new SortField (fieldname, fields[i].getLocale(), fields[i].getReverse());
} else {
this.fields[i] = new SortField (fieldname, comparators[i].sortType(), fields[i].getReverse());
}
} else {
this.fields[i] = new SortField (fieldname, comparators[i].sortType(), fields[i].getReverse());
assert comparators[i].sortType() == fields[i].getType();
this.fields[i] = fields[i];
}
}
initialize (size);
@ -157,13 +162,16 @@ extends PriorityQueue {
return fields;
}
static ScoreDocComparator getCachedComparator (IndexReader reader, String field, int type, Locale locale, SortComparatorSource factory)
static ScoreDocComparator getCachedComparator (IndexReader reader, String field, int type, FieldCache.Parser parser, Locale locale, SortComparatorSource factory)
throws IOException {
if (type == SortField.DOC) return ScoreDocComparator.INDEXORDER;
if (type == SortField.SCORE) return ScoreDocComparator.RELEVANCE;
FieldCacheImpl.Entry entry = (factory != null)
? new FieldCacheImpl.Entry (field, factory)
: new FieldCacheImpl.Entry (field, type, locale);
: ( (parser != null)
? new FieldCacheImpl.Entry (field, type, parser)
: new FieldCacheImpl.Entry (field, type, locale)
);
return (ScoreDocComparator)Comparators.get(reader, entry);
}
@ -177,29 +185,35 @@ extends PriorityQueue {
String fieldname = entry.field;
int type = entry.type;
Locale locale = entry.locale;
SortComparatorSource factory = (SortComparatorSource) entry.custom;
FieldCache.Parser parser = null;
SortComparatorSource factory = null;
if (entry.custom instanceof SortComparatorSource) {
factory = (SortComparatorSource) entry.custom;
} else {
parser = (FieldCache.Parser) entry.custom;
}
ScoreDocComparator comparator;
switch (type) {
case SortField.AUTO:
comparator = comparatorAuto (reader, fieldname);
break;
case SortField.INT:
comparator = comparatorInt (reader, fieldname);
comparator = comparatorInt (reader, fieldname, (FieldCache.IntParser)parser);
break;
case SortField.FLOAT:
comparator = comparatorFloat (reader, fieldname);
comparator = comparatorFloat (reader, fieldname, (FieldCache.FloatParser)parser);
break;
case SortField.LONG:
comparator = comparatorLong(reader, fieldname);
comparator = comparatorLong(reader, fieldname, (ExtendedFieldCache.LongParser)parser);
break;
case SortField.DOUBLE:
comparator = comparatorDouble(reader, fieldname);
comparator = comparatorDouble(reader, fieldname, (ExtendedFieldCache.DoubleParser)parser);
break;
case SortField.SHORT:
comparator = comparatorShort(reader, fieldname);
comparator = comparatorShort(reader, fieldname, (FieldCache.ShortParser)parser);
break;
case SortField.BYTE:
comparator = comparatorByte(reader, fieldname);
comparator = comparatorByte(reader, fieldname, (FieldCache.ByteParser)parser);
break;
case SortField.STRING:
if (locale != null) comparator = comparatorStringLocale (reader, fieldname, locale);
@ -222,10 +236,12 @@ extends PriorityQueue {
* @return Comparator for sorting hits.
* @throws IOException If an error occurs reading the index.
*/
static ScoreDocComparator comparatorByte(final IndexReader reader, final String fieldname)
static ScoreDocComparator comparatorByte(final IndexReader reader, final String fieldname, final FieldCache.ByteParser parser)
throws IOException {
final String field = fieldname.intern();
final byte[] fieldOrder = FieldCache.DEFAULT.getBytes(reader, field);
final byte[] fieldOrder = (parser==null)
? FieldCache.DEFAULT.getBytes(reader, field)
: FieldCache.DEFAULT.getBytes(reader, field, parser);
return new ScoreDocComparator() {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
@ -241,7 +257,7 @@ extends PriorityQueue {
}
public int sortType() {
return SortField.INT;
return SortField.BYTE;
}
};
}
@ -253,10 +269,12 @@ extends PriorityQueue {
* @return Comparator for sorting hits.
* @throws IOException If an error occurs reading the index.
*/
static ScoreDocComparator comparatorShort(final IndexReader reader, final String fieldname)
static ScoreDocComparator comparatorShort(final IndexReader reader, final String fieldname, final FieldCache.ShortParser parser)
throws IOException {
final String field = fieldname.intern();
final short[] fieldOrder = FieldCache.DEFAULT.getShorts(reader, field);
final short[] fieldOrder = (parser==null)
? FieldCache.DEFAULT.getShorts(reader, field)
: FieldCache.DEFAULT.getShorts(reader, field, parser);
return new ScoreDocComparator() {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
@ -284,10 +302,12 @@ extends PriorityQueue {
* @return Comparator for sorting hits.
* @throws IOException If an error occurs reading the index.
*/
static ScoreDocComparator comparatorInt (final IndexReader reader, final String fieldname)
static ScoreDocComparator comparatorInt (final IndexReader reader, final String fieldname, final FieldCache.IntParser parser)
throws IOException {
final String field = fieldname.intern();
final int[] fieldOrder = FieldCache.DEFAULT.getInts (reader, field);
final int[] fieldOrder = (parser==null)
? FieldCache.DEFAULT.getInts(reader, field)
: FieldCache.DEFAULT.getInts(reader, field, parser);
return new ScoreDocComparator() {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
@ -315,10 +335,12 @@ extends PriorityQueue {
* @return Comparator for sorting hits.
* @throws IOException If an error occurs reading the index.
*/
static ScoreDocComparator comparatorLong (final IndexReader reader, final String fieldname)
static ScoreDocComparator comparatorLong (final IndexReader reader, final String fieldname, final ExtendedFieldCache.LongParser parser)
throws IOException {
final String field = fieldname.intern();
final long[] fieldOrder = ExtendedFieldCache.EXT_DEFAULT.getLongs (reader, field);
final long[] fieldOrder = (parser==null)
? ExtendedFieldCache.EXT_DEFAULT.getLongs (reader, field)
: ExtendedFieldCache.EXT_DEFAULT.getLongs (reader, field, parser);
return new ScoreDocComparator() {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
@ -347,10 +369,12 @@ extends PriorityQueue {
* @return Comparator for sorting hits.
* @throws IOException If an error occurs reading the index.
*/
static ScoreDocComparator comparatorFloat (final IndexReader reader, final String fieldname)
static ScoreDocComparator comparatorFloat (final IndexReader reader, final String fieldname, final FieldCache.FloatParser parser)
throws IOException {
final String field = fieldname.intern();
final float[] fieldOrder = FieldCache.DEFAULT.getFloats (reader, field);
final float[] fieldOrder = (parser==null)
? FieldCache.DEFAULT.getFloats (reader, field)
: FieldCache.DEFAULT.getFloats (reader, field, parser);
return new ScoreDocComparator () {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
@ -378,10 +402,12 @@ extends PriorityQueue {
* @return Comparator for sorting hits.
* @throws IOException If an error occurs reading the index.
*/
static ScoreDocComparator comparatorDouble(final IndexReader reader, final String fieldname)
static ScoreDocComparator comparatorDouble(final IndexReader reader, final String fieldname, final ExtendedFieldCache.DoubleParser parser)
throws IOException {
final String field = fieldname.intern();
final double[] fieldOrder = ExtendedFieldCache.EXT_DEFAULT.getDoubles (reader, field);
final double[] fieldOrder = (parser==null)
? ExtendedFieldCache.EXT_DEFAULT.getDoubles (reader, field)
: ExtendedFieldCache.EXT_DEFAULT.getDoubles (reader, field, parser);
return new ScoreDocComparator () {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
@ -488,11 +514,11 @@ extends PriorityQueue {
if (lookupArray instanceof FieldCache.StringIndex) {
return comparatorString (reader, field);
} else if (lookupArray instanceof int[]) {
return comparatorInt (reader, field);
return comparatorInt (reader, field, null);
} else if (lookupArray instanceof long[]) {
return comparatorLong (reader, field);
return comparatorLong (reader, field, null);
} else if (lookupArray instanceof float[]) {
return comparatorFloat (reader, field);
return comparatorFloat (reader, field, null);
} else if (lookupArray instanceof String[]) {
return comparatorString (reader, field);
} else {

View File

@ -99,13 +99,14 @@ implements Serializable {
private Locale locale; // defaults to "natural order" (no Locale)
boolean reverse = false; // defaults to natural order
private SortComparatorSource factory;
private FieldCache.Parser parser;
/** Creates a sort by terms in the given field where the type of term value
* is determined dynamically ({@link #AUTO AUTO}).
* @param field Name of field to sort by, cannot be <code>null</code>.
*/
public SortField (String field) {
this.field = field.intern();
initFieldType(field, AUTO);
}
/** Creates a sort, possibly in reverse, by terms in the given field where
@ -114,7 +115,7 @@ implements Serializable {
* @param reverse True if natural order should be reversed.
*/
public SortField (String field, boolean reverse) {
this.field = field.intern();
initFieldType(field, AUTO);
this.reverse = reverse;
}
@ -125,8 +126,7 @@ implements Serializable {
* @param type Type of values in the terms.
*/
public SortField (String field, int type) {
this.field = (field != null) ? field.intern() : field;
this.type = type;
initFieldType(field, type);
}
/** Creates a sort, possibly in reverse, by terms in the given field with the
@ -137,19 +137,73 @@ implements Serializable {
* @param reverse True if natural order should be reversed.
*/
public SortField (String field, int type, boolean reverse) {
this.field = (field != null) ? field.intern() : field;
this.type = type;
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}.
* @param field Name of field to sort by. Must not be null.
* @param parser Instance of a {@link FieldCache.Parser},
* which must subclass one of the existing numeric
* parsers from {@link FieldCache} or {@link
* ExtendedFieldCache}. Sort type is inferred by testing
* which numeric parser the parser subclasses.
* @throws IllegalArgumentException if the parser fails to
* subclass an existing numeric parser, or field is null
*/
public SortField (String field, FieldCache.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}.
* @param field Name of field to sort by. Must not be null.
* @param parser Instance of a {@link FieldCache.Parser},
* which must subclass one of the existing numeric
* parsers from {@link FieldCache} or {@link
* ExtendedFieldCache}. Sort type is inferred by testing
* which numeric parser the parser subclasses.
* @param reverse True if natural order should be reversed.
* @throws IllegalArgumentException if the parser fails to
* subclass an existing numeric parser, or field is null
*/
public SortField (String field, FieldCache.Parser parser, boolean reverse) {
if (parser instanceof FieldCache.IntParser) this.type=INT;
else if (parser instanceof FieldCache.FloatParser) this.type=FLOAT;
else if (parser instanceof FieldCache.ShortParser) this.type=SHORT;
else if (parser instanceof FieldCache.ByteParser) this.type=BYTE;
else if (parser instanceof ExtendedFieldCache.LongParser) this.type=LONG;
else if (parser instanceof ExtendedFieldCache.DoubleParser) this.type=DOUBLE;
else
throw new IllegalArgumentException("Parser instance does not subclass existing numeric parser from FieldCache or ExtendedFieldCache (got" + parser + ")");
initFieldType(field, type);
this.reverse = reverse;
this.parser = parser;
}
// Sets field & type, and ensures field is not NULL unless
// type is SCORE or DOC
private void initFieldType(String field, int type) {
this.type = type;
if (field == null) {
if (type != SCORE && type != DOC)
throw new IllegalArgumentException("field can only be null when type is SCORE or DOC");
} else {
this.field = field.intern();
}
}
/** Creates a sort by terms in the given field sorted
* according to the given locale.
* @param field Name of field to sort by, cannot be <code>null</code>.
* @param locale Locale of values in the field.
*/
public SortField (String field, Locale locale) {
this.field = field.intern();
this.type = STRING;
initFieldType(field, STRING);
this.locale = locale;
}
@ -159,8 +213,7 @@ implements Serializable {
* @param locale Locale of values in the field.
*/
public SortField (String field, Locale locale, boolean reverse) {
this.field = field.intern();
this.type = STRING;
initFieldType(field, STRING);
this.locale = locale;
this.reverse = reverse;
}
@ -170,8 +223,7 @@ implements Serializable {
* @param comparator Returns a comparator for sorting hits.
*/
public SortField (String field, SortComparatorSource comparator) {
this.field = (field != null) ? field.intern() : field;
this.type = CUSTOM;
initFieldType(field, CUSTOM);
this.factory = comparator;
}
@ -181,8 +233,7 @@ implements Serializable {
* @param reverse True if natural order should be reversed.
*/
public SortField (String field, SortComparatorSource comparator, boolean reverse) {
this.field = (field != null) ? field.intern() : field;
this.type = CUSTOM;
initFieldType(field, CUSTOM);
this.reverse = reverse;
this.factory = comparator;
}
@ -210,6 +261,14 @@ implements Serializable {
return locale;
}
/** Returns the instance of a {@link FieldCache} parser that fits to the given sort type.
* May return <code>null</code> if no parser was specified. Sorting is using the default parser then.
* @return An instance of a {@link FieldCache} parser, or <code>null</code>.
*/
public FieldCache.Parser getParser() {
return parser;
}
/** Returns whether the sort should be reversed.
* @return True if natural order should be reversed.
*/
@ -240,14 +299,16 @@ implements Serializable {
}
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 <code>o</code> is equal to this. If a
* {@link #SortComparatorSource} was provided, it must
* properly implement equals. */
* {@link SortComparatorSource} or {@link
* FieldCache.Parser} was provided, it must properly
* implement equals (unless a singleton is always used). */
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SortField)) return false;
@ -258,17 +319,21 @@ implements Serializable {
&& 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.parser == null ? this.parser == null : other.parser.equals(this.parser))
);
}
/** Returns a hash code value for this object. If a
* {@link #SortComparatorSource} was provided, it must
* properly implement hashCode. */
/** Returns true if <code>o</code> is equal to this. If a
* {@link SortComparatorSource} or {@link
* FieldCache.Parser} was provided, it must properly
* implement hashCode (unless a singleton is always
* used). */
public int hashCode() {
int hash=type^0x346565dd + Boolean.valueOf(reverse).hashCode()^0xaf5998bb;
if (field != null) hash += field.hashCode()^0xff5685dd;
if (locale != null) hash += locale.hashCode()^0x08150815;
if (factory != null) hash += factory.hashCode()^0x34987555;
if (parser != null) hash += parser.hashCode()^0x3aaf56ff;
return hash;
}
}

View File

@ -98,21 +98,21 @@ implements Serializable {
// the string field to sort by string
// the i18n field includes accented characters for testing locale-specific sorting
private String[][] data = new String[][] {
// tracer contents int float string custom i18n long double, 'short', byte
{ "A", "x a", "5", "4f", "c", "A-3", "p\u00EAche", "10", "-4.0", "3", "126"},//A, x
{ "B", "y a", "5", "3.4028235E38", "i", "B-10", "HAT", "1000000000", "40.0", "24", "1"},//B, y
{ "C", "x a b c", "2147483647", "1.0", "j", "A-2", "p\u00E9ch\u00E9", "99999999", "40.00002343", "125", "15"},//C, x
{ "D", "y a b c", "-1", "0.0f", "a", "C-0", "HUT", String.valueOf(Long.MAX_VALUE), String.valueOf(Double.MIN_VALUE), String.valueOf(Short.MIN_VALUE), String.valueOf(Byte.MIN_VALUE)},//D, y
{ "E", "x a b c d", "5", "2f", "h", "B-8", "peach", String.valueOf(Long.MIN_VALUE), String.valueOf(Double.MAX_VALUE), String.valueOf(Short.MAX_VALUE), String.valueOf(Byte.MAX_VALUE)},//E,x
{ "F", "y a b c d", "2", "3.14159f", "g", "B-1", "H\u00C5T", "-44", "343.034435444", "-3", "0"},//F,y
{ "G", "x a b c d", "3", "-1.0", "f", "C-100", "sin", "323254543543", "4.043544", "5", "100"},//G,x
{ "H", "y a b c d", "0", "1.4E-45", "e", "C-88", "H\u00D8T", "1023423423005","4.043545", "10", "-50"},//H,y
{ "I", "x a b c d e f", "-2147483648", "1.0e+0", "d", "A-10", "s\u00EDn", "332422459999", "4.043546", "-340", "51"},//I,x
{ "J", "y a b c d e f", "4", ".5", "b", "C-7", "HOT", "34334543543", "4.0000220343", "300", "2"},//J,y
{ "W", "g", "1", null, null, null, null, null, null, null, null},
{ "X", "g", "1", "0.1", null, null, null, null, null, null, null},
{ "Y", "g", "1", "0.2", null, null, null, null, null, null, null},
{ "Z", "f g", null, null, null, null, null, null, null, null, null}
// tracer contents int float string custom i18n long double, 'short', byte, 'custom parser encoding'
{ "A", "x a", "5", "4f", "c", "A-3", "p\u00EAche", "10", "-4.0", "3", "126", "J"},//A, x
{ "B", "y a", "5", "3.4028235E38", "i", "B-10", "HAT", "1000000000", "40.0", "24", "1", "I"},//B, y
{ "C", "x a b c", "2147483647", "1.0", "j", "A-2", "p\u00E9ch\u00E9", "99999999", "40.00002343", "125", "15", "H"},//C, x
{ "D", "y a b c", "-1", "0.0f", "a", "C-0", "HUT", String.valueOf(Long.MAX_VALUE), String.valueOf(Double.MIN_VALUE), String.valueOf(Short.MIN_VALUE), String.valueOf(Byte.MIN_VALUE), "G"},//D, y
{ "E", "x a b c d", "5", "2f", "h", "B-8", "peach", String.valueOf(Long.MIN_VALUE), String.valueOf(Double.MAX_VALUE), String.valueOf(Short.MAX_VALUE), String.valueOf(Byte.MAX_VALUE), "F"},//E,x
{ "F", "y a b c d", "2", "3.14159f", "g", "B-1", "H\u00C5T", "-44", "343.034435444", "-3", "0", "E"},//F,y
{ "G", "x a b c d", "3", "-1.0", "f", "C-100", "sin", "323254543543", "4.043544", "5", "100", "D"},//G,x
{ "H", "y a b c d", "0", "1.4E-45", "e", "C-88", "H\u00D8T", "1023423423005","4.043545", "10", "-50", "C"},//H,y
{ "I", "x a b c d e f", "-2147483648", "1.0e+0", "d", "A-10", "s\u00EDn", "332422459999", "4.043546", "-340", "51", "B"},//I,x
{ "J", "y a b c d e f", "4", ".5", "b", "C-7", "HOT", "34334543543", "4.0000220343", "300", "2", "A"},//J,y
{ "W", "g", "1", null, null, null, null, null, null, null, null, null},
{ "X", "g", "1", "0.1", null, null, null, null, null, null, null, null},
{ "Y", "g", "1", "0.2", null, null, null, null, null, null, null, null},
{ "Z", "f g", null, null, null, null, null, null, null, null, null, null}
};
// create an index of all the documents, or just the x, or just the y documents
@ -132,8 +132,9 @@ implements Serializable {
if (data[i][6] != null) doc.add (new Field ("i18n", data[i][6], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][7] != null) doc.add (new Field ("long", data[i][7], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][8] != null) doc.add (new Field ("double", data[i][8], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][8] != null) doc.add (new Field ("short", data[i][9], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][8] != null) doc.add (new Field ("byte", data[i][10], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][9] != null) doc.add (new Field ("short", data[i][9], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][10] != null) doc.add (new Field ("byte", data[i][10], Field.Store.NO, Field.Index.NOT_ANALYZED));
if (data[i][11] != null) doc.add (new Field ("parser", data[i][11], Field.Store.NO, Field.Index.NOT_ANALYZED));
doc.setBoost(2); // produce some scores above 1.0
writer.addDocument (doc);
}
@ -218,6 +219,53 @@ implements Serializable {
assertMatches (full, queryY, sort, "DJHFB");
}
// test sorts where the type of field is specified and a custom field parser is used, that
// uses a simple char encoding. The sorted string contains a character beginning from 'A' that
// is mapped to a numeric value using some "funny" algorithm to be different for each data type.
public void testCustomFieldParserSort() throws Exception {
sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.IntParser(){
public final int parseInt(final String val) {
return (int) (val.charAt(0)-'A') * 123456;
}
}), SortField.FIELD_DOC });
assertMatches (full, queryA, sort, "JIHGFEDCBA");
sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.FloatParser(){
public final float parseFloat(final String val) {
return (float) Math.sqrt( (double) val.charAt(0) );
}
}), SortField.FIELD_DOC });
assertMatches (full, queryA, sort, "JIHGFEDCBA");
sort.setSort (new SortField[] { new SortField ("parser", new ExtendedFieldCache.LongParser(){
public final long parseLong(final String val) {
return (long) (val.charAt(0)-'A') * 1234567890L;
}
}), SortField.FIELD_DOC });
assertMatches (full, queryA, sort, "JIHGFEDCBA");
sort.setSort (new SortField[] { new SortField ("parser", new ExtendedFieldCache.DoubleParser(){
public final double parseDouble(final String val) {
return Math.pow( (double) val.charAt(0), (double) (val.charAt(0)-'A') );
}
}), SortField.FIELD_DOC });
assertMatches (full, queryA, sort, "JIHGFEDCBA");
sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.ByteParser(){
public final byte parseByte(final String val) {
return (byte) (val.charAt(0)-'A');
}
}), SortField.FIELD_DOC });
assertMatches (full, queryA, sort, "JIHGFEDCBA");
sort.setSort (new SortField[] { new SortField ("parser", new FieldCache.ShortParser(){
public final short parseShort(final String val) {
return (short) (val.charAt(0)-'A');
}
}), SortField.FIELD_DOC });
assertMatches (full, queryA, sort, "JIHGFEDCBA");
}
// test sorts when there's nothing in the index
public void testEmptyIndex() throws Exception {
Searcher empty = getEmptyIndex();