lucene-java-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mikemcc...@apache.org
Subject 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/
Date Mon, 08 Dec 2008 21:07:45 GMT
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();



Mime
View raw message