You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by ry...@apache.org on 2007/06/20 03:38:19 UTC

svn commit: r548887 - /lucene/solr/trunk/src/java/org/apache/solr/schema/RandomSortField.java

Author: ryan
Date: Tue Jun 19 18:38:18 2007
New Revision: 548887

URL: http://svn.apache.org/viewvc?view=rev&rev=548887
Log:
SOLR-264 - updating the RandomSortField so it follows the Comparator contract.  This uses the: field name hash, docID, and reader version as a random generator seed.  

This may not be the final version, but it is better then the existing one...

Modified:
    lucene/solr/trunk/src/java/org/apache/solr/schema/RandomSortField.java

Modified: lucene/solr/trunk/src/java/org/apache/solr/schema/RandomSortField.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/schema/RandomSortField.java?view=diff&rev=548887&r1=548886&r2=548887
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/schema/RandomSortField.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/schema/RandomSortField.java Tue Jun 19 18:38:18 2007
@@ -18,7 +18,6 @@
 package org.apache.solr.schema;
 
 import java.io.IOException;
-import java.util.Random;
 
 import org.apache.lucene.document.Fieldable;
 import org.apache.lucene.index.IndexReader;
@@ -28,13 +27,15 @@
 import org.apache.lucene.search.SortField;
 import org.apache.solr.request.TextResponseWriter;
 import org.apache.solr.request.XMLWriter;
+import org.apache.solr.search.function.DocValues;
 import org.apache.solr.search.function.ValueSource;
 
 /**
  * Utility Field used for random sorting.  It should not be passed a value.
  * 
- * To enable random sorting, you will need to add something like this 
- * to the schema.xml
+ * This random sorting implementation uses the dynamic field name to set the
+ * random 'seed'.  To get random sorting order, you need to use a random
+ * dynamic field name.  For example, you will need to configure schema.xml:
  * 
  * <types>
  *  ...
@@ -43,63 +44,164 @@
  * </types>
  * <fields>
  *  ...
- *  <field name="random" type="random" indexed="true" stored="false"/>
+ *  <dynamicField name="random*" type="rand" indexed="true" stored="false"/>
  *  ...
  * </fields>
+ * 
+ *  http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_1234%20desc
+ *  http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_2345%20desc
+ *  http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_ABDC%20desc
+ *  http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_21%20desc
  *  
+ * Note that multiple calls to the same URL will return the same sorting order.
+ * 
  * @author ryan
+ * @author yonik
  * @version $Id$
  * @since solr 1.3
  */
-public class RandomSortField extends FieldType 
-{
-  /** Special comparator for sorting hits in random order */
-  private static final ScoreDocComparator COMPARE = new ScoreDocComparator() {
-    final Random rand = new Random();
-    
-    public int compare (ScoreDoc i, ScoreDoc j) {
-      return (rand.nextInt() & 0x2) - 1; // (rand.nextBoolean()) ? 1 : -1; 
+public class RandomSortField extends FieldType {
+  // Thomas Wang's hash32shift function, from http://www.cris.com/~Ttwang/tech/inthash.htm
+  // slightly modified to return only positive integers.
+  private static int hash(int key) {
+    key = ~key + (key << 15); // key = (key << 15) - key - 1;
+    key = key ^ (key >>> 12);
+    key = key + (key << 2);
+    key = key ^ (key >>> 4);
+    key = key * 2057; // key = (key + (key << 3)) + (key << 11);
+    key = key ^ (key >>> 16);
+    return key >>> 1; 
+  }
+
+  private static class RandomComparator implements ScoreDocComparator {
+    final int seed;
+
+    RandomComparator(int seed) {
+      this.seed = seed;
     }
-    public Comparable sortValue (ScoreDoc i) {
-      return new Float(rand.nextFloat());
+
+    public int compare(ScoreDoc i, ScoreDoc j) {
+      return hash(i.doc + seed) - hash(j.doc + seed);
     }
+
+    public Comparable sortValue(ScoreDoc i) {
+      return new Integer(hash(i.doc + seed));
+    }
+
     public int sortType() {
       return SortField.CUSTOM;
     }
   };
-  
-  /** use random sorting order.  */
+
+  /** given a field name, get a random hash seed -- using dynamic fields, you can force the random order to change */
+  private static int getSeed(String fieldName) {
+    return fieldName.hashCode();      
+  }
+
   private static class RandomSort extends SortField {
-    public RandomSort( String n )
-    {
-      super( n, SortField.CUSTOM );
+    public RandomSort(String n, boolean reverse) {
+      super(n, SortField.CUSTOM, reverse);
     }
 
     @Override
     public SortComparatorSource getFactory() {
+      final int seed = getSeed(getField());
+      
       return new SortComparatorSource() {
         public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
-          return COMPARE;
+          return new RandomComparator( (int)(seed^reader.getVersion()) );
+        }
+
+        @Override
+        public int hashCode() {
+          return getField().hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+          return (o instanceof RandomSort) && getField().equals(((RandomSort) o).getField());
         }
       };
     }
   }
-  
+
+  public class RandomValueSource extends ValueSource {
+    private final String field;
+    final int seed;
+
+    public RandomValueSource(String field) {
+      this.field=field;
+      seed = getSeed(field);     
+    }
+
+    @Override
+    public String description() {
+      return field;
+    }
+
+    @Override
+    public DocValues getValues(IndexReader reader) throws IOException {
+      return new DocValues() {
+        @Override
+        public float floatVal(int doc) {
+          return (float)hash(doc+seed);
+        }
+
+        @Override
+        public int intVal(int doc) {
+          return (int)hash(doc+seed);
+        }
+
+        @Override
+        public long longVal(int doc) {
+          return (long)hash(doc+seed);
+        }
+
+        @Override
+        public double doubleVal(int doc) {
+          return (double)hash(doc+seed);
+        }
+
+        @Override
+        public String strVal(int doc) {
+          return Integer.toString(hash(doc+seed));
+        }
+
+        @Override
+        public String toString(int doc) {
+          return description() + '=' + intVal(doc);
+        }
+      };
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof RandomValueSource)) return false;
+      RandomValueSource other = (RandomValueSource)o;
+      return this.field.equals(other.field);
+    }
+
+    @Override
+    public int hashCode() {
+      return field.hashCode();
+    };
+  }
+
   @Override
-  public SortField getSortField(SchemaField field,boolean reverse) {
-    return new RandomSort(field.getName());
+  public SortField getSortField(SchemaField field, boolean reverse) {
+    return new RandomSort(field.getName(), reverse);
   }
 
   @Override
   public ValueSource getValueSource(SchemaField field) {
-    throw new UnsupportedOperationException("Random field does not have a value source");
+    return new RandomValueSource(field.getName());
   }
 
   @Override
-  public void write(XMLWriter xmlWriter, String name, Fieldable f) {}
+  public void write(XMLWriter xmlWriter, String name, Fieldable f) throws IOException { }
 
   @Override
-  public void write(TextResponseWriter writer, String name, Fieldable f) {}
+  public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException { }
 }