You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@lucene.apache.org by mi...@apache.org on 2008/12/08 22:07:45 UTC
svn commit: r724484 - in /lucene/java/trunk: ./
contrib/queries/src/java/org/apache/lucene/search/trie/
contrib/queries/src/test/org/apache/lucene/search/trie/
src/java/org/apache/lucene/search/ src/test/org/apache/lucene/search/
Author: mikemccand
Date: Mon Dec 8 13:07:45 2008
New Revision: 724484
URL: http://svn.apache.org/viewvc?rev=724484&view=rev
Log:
LUCENE-1478: allow SortField to use a custom numeric FieldCache parser
Modified:
lucene/java/trunk/CHANGES.txt
lucene/java/trunk/contrib/queries/src/java/org/apache/lucene/search/trie/TrieUtils.java
lucene/java/trunk/contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java
lucene/java/trunk/src/java/org/apache/lucene/search/ExtendedFieldCache.java
lucene/java/trunk/src/java/org/apache/lucene/search/FieldCache.java
lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheImpl.java
lucene/java/trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java
lucene/java/trunk/src/java/org/apache/lucene/search/SortField.java
lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java
Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/java/trunk/CHANGES.txt?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Mon Dec 8 13:07:45 2008
@@ -124,6 +124,10 @@
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
Modified: lucene/java/trunk/contrib/queries/src/java/org/apache/lucene/search/trie/TrieUtils.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/contrib/queries/src/java/org/apache/lucene/search/trie/TrieUtils.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/contrib/queries/src/java/org/apache/lucene/search/trie/TrieUtils.java (original)
+++ lucene/java/trunk/contrib/queries/src/java/org/apache/lucene/search/trie/TrieUtils.java Mon Dec 8 13:07:45 2008
@@ -21,6 +21,8 @@
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 @@
/** 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 @@
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 @@
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) */
Modified: lucene/java/trunk/contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java (original)
+++ lucene/java/trunk/contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java Mon Dec 8 13:07:45 2008
@@ -163,7 +163,7 @@
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 @@
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);
+ }
+
}
Modified: lucene/java/trunk/src/java/org/apache/lucene/search/ExtendedFieldCache.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/ExtendedFieldCache.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/ExtendedFieldCache.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/ExtendedFieldCache.java Mon Dec 8 13:07:45 2008
@@ -27,14 +27,14 @@
*
**/
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.
*/
Modified: lucene/java/trunk/src/java/org/apache/lucene/search/FieldCache.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/FieldCache.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/FieldCache.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/FieldCache.java Mon Dec 8 13:07:45 2008
@@ -74,10 +74,18 @@
}
}
+ /**
+ * 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 @@
/** 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 @@
/** 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 @@
/** 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);
}
Modified: lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheImpl.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheImpl.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheImpl.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/FieldCacheImpl.java Mon Dec 8 13:07:45 2008
@@ -88,7 +88,7 @@
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 @@
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 @@
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) {
Modified: lucene/java/trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/FieldSortedHitQueue.java Mon Dec 8 13:07:45 2008
@@ -52,12 +52,17 @@
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 @@
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 @@
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 @@
* @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 @@
}
public int sortType() {
- return SortField.INT;
+ return SortField.BYTE;
}
};
}
@@ -253,10 +269,12 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
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 {
Modified: lucene/java/trunk/src/java/org/apache/lucene/search/SortField.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/search/SortField.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/SortField.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/SortField.java Mon Dec 8 13:07:45 2008
@@ -99,13 +99,14 @@
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 @@
* @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 @@
* @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,9 +137,64 @@
* @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
@@ -148,8 +203,7 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
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 @@
}
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 @@
&& 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;
}
}
Modified: lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java?rev=724484&r1=724483&r2=724484&view=diff
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestSort.java Mon Dec 8 13:07:45 2008
@@ -98,21 +98,21 @@
// 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 @@
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 @@
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();