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 gs...@apache.org on 2009/12/24 14:03:24 UTC

svn commit: r893746 [2/2] - in /lucene/solr/trunk: ./ example/exampledocs/ example/solr/conf/ src/common/org/apache/solr/common/ src/java/org/apache/solr/schema/ src/java/org/apache/solr/search/ src/java/org/apache/solr/search/function/ src/java/org/ap...

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/DistanceUtils.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/DistanceUtils.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/DistanceUtils.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/DistanceUtils.java Thu Dec 24 13:03:22 2009
@@ -1,4 +1,6 @@
 package org.apache.solr.search.function.distance;
+
+import org.apache.solr.common.SolrException;
 /**
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -50,5 +52,44 @@
     return result;
   }
 
-
+  /**
+   * Given a string containing <i>dimension</i> values encoded in it, separated by commas, return a String array of length <i>dimension</i>
+   * containing the values.
+   * @param out A preallocated array.  Must be size dimension.  If it is not it will be resized.
+   * @param externalVal The value to parse
+   * @param dimension The expected number of values for the point
+   * @return An array of the values that make up the point (aka vector)
+   *
+   * @throws {@link SolrException} if the dimension specified does not match the number of values in the externalValue.
+   */
+  public static String[] parsePoint(String[] out, String externalVal, int dimension) {
+    //TODO: Should we support sparse vectors?
+    if (out==null || out.length != dimension) out=new String[dimension];
+    int idx = externalVal.indexOf(',');
+    int end = idx;
+    int start = 0;
+    int i = 0;
+    if (idx == -1 && dimension == 1 && externalVal.length() > 0){//we have a single point, dimension better be 1
+      out[0] = externalVal.trim();
+      i = 1;
+    }
+    else if (idx > 0) {//if it is zero, that is an error
+      //Parse out a comma separated list of point values, as in: 73.5,89.2,7773.4
+      for (; i < dimension; i++){
+        while (start<end && externalVal.charAt(start)==' ') start++;
+        while (end>start && externalVal.charAt(end-1)==' ') end--;
+        out[i] = externalVal.substring(start, end);
+        start = idx+1;
+        end = externalVal.indexOf(',', start);
+        if (end == -1){
+          end = externalVal.length();
+        }
+      }
+    } 
+    if (i != dimension){
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "incompatible dimension (" + dimension +
+              ") and values (" + externalVal + ").  Only " + i + " values specified");
+    }
+    return out;
+  }
 }

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/HaversineFunction.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/HaversineFunction.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/HaversineFunction.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/HaversineFunction.java Thu Dec 24 13:03:22 2009
@@ -18,6 +18,8 @@
 
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.Searcher;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.search.MultiValueSource;
 import org.apache.solr.search.function.DocValues;
 import org.apache.solr.search.function.ValueSource;
 
@@ -29,28 +31,31 @@
  * Calculate the Haversine formula (distance) between any two points on a sphere
  * Takes in four value sources: (latA, lonA); (latB, lonB).
  * <p/>
- * Assumes the value sources are in radians
+ * Assumes the value sources are in radians unless
  * <p/>
  * See http://en.wikipedia.org/wiki/Great-circle_distance and
  * http://en.wikipedia.org/wiki/Haversine_formula for the actual formula and
  * also http://www.movable-type.co.uk/scripts/latlong.html
- *
- * @see org.apache.solr.search.function.RadianFunction
  */
 public class HaversineFunction extends ValueSource {
 
-  private ValueSource x1;
-  private ValueSource y1;
-  private ValueSource x2;
-  private ValueSource y2;
+  private MultiValueSource p1;
+  private MultiValueSource p2;
+  private boolean convertToRadians = false;
   private double radius;
 
-  public HaversineFunction(ValueSource x1, ValueSource y1, ValueSource x2, ValueSource y2, double radius) {
-    this.x1 = x1;
-    this.y1 = y1;
-    this.x2 = x2;
-    this.y2 = y2;
+  public HaversineFunction(MultiValueSource p1, MultiValueSource p2, double radius) {
+    this(p1, p2, radius, false);
+  }
+
+  public HaversineFunction(MultiValueSource p1, MultiValueSource p2, double radius, boolean convertToRads){
+    this.p1 = p1;
+    this.p2 = p2;
+    if (p1.dimension() != 2 || p2.dimension() != 2) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Illegal dimension for value sources");
+    }
     this.radius = radius;
+    this.convertToRadians = convertToRads;
   }
 
   protected String name() {
@@ -59,28 +64,40 @@
 
   /**
    * @param doc  The doc to score
-   * @param x1DV
-   * @param y1DV
-   * @param x2DV
-   * @param y2DV
+   * @param p1DV
+   * @param p2DV
    * @return The haversine distance formula
    */
-  protected double distance(int doc, DocValues x1DV, DocValues y1DV, DocValues x2DV, DocValues y2DV) {
-    double x1 = x1DV.doubleVal(doc); //in radians
-    double y1 = y1DV.doubleVal(doc);
-    double x2 = x2DV.doubleVal(doc);
-    double y2 = y2DV.doubleVal(doc);
+  protected double distance(int doc, DocValues p1DV, DocValues p2DV) {
 
+    double[] p1D = new double[2];
+    double[] p2D = new double[2];
+    p1DV.doubleVal(doc, p1D);
+    p2DV.doubleVal(doc, p2D);
+    double x1;
+    double y1;
+    double x2;
+    double y2;
+    if (convertToRadians) {
+      x1 = p1D[0] * DistanceUtils.DEGREES_TO_RADIANS;
+      y1 = p1D[1] * DistanceUtils.DEGREES_TO_RADIANS;
+      x2 = p2D[0] * DistanceUtils.DEGREES_TO_RADIANS;
+      y2 = p2D[1] * DistanceUtils.DEGREES_TO_RADIANS;
+    } else {
+      x1 = p1D[0];
+      y1 = p1D[1];
+      x2 = p2D[0];
+      y2 = p2D[1];
+    }
     return DistanceUtils.haversine(x1, y1, x2, y2, radius);
   }
 
 
   @Override
   public DocValues getValues(Map context, IndexReader reader) throws IOException {
-    final DocValues x1DV = x1.getValues(context, reader);
-    final DocValues y1DV = y1.getValues(context, reader);
-    final DocValues x2DV = x2.getValues(context, reader);
-    final DocValues y2DV = y2.getValues(context, reader);
+    final DocValues vals1 = p1.getValues(context, reader);
+
+    final DocValues vals2 = p2.getValues(context, reader);
     return new DocValues() {
       public float floatVal(int doc) {
         return (float) doubleVal(doc);
@@ -95,7 +112,7 @@
       }
 
       public double doubleVal(int doc) {
-        return (double) distance(doc, x1DV, y1DV, x2DV, y2DV);
+        return (double) distance(doc, vals1, vals2);
       }
 
       public String strVal(int doc) {
@@ -106,8 +123,7 @@
       public String toString(int doc) {
         StringBuilder sb = new StringBuilder();
         sb.append(name()).append('(');
-        sb.append(x1DV.toString(doc)).append(',').append(y1DV.toString(doc)).append(',')
-                .append(x2DV.toString(doc)).append(',').append(y2DV.toString(doc));
+        sb.append(vals1.toString(doc)).append(',').append(vals2.toString(doc));
         sb.append(')');
         return sb.toString();
       }
@@ -116,10 +132,9 @@
 
   @Override
   public void createWeight(Map context, Searcher searcher) throws IOException {
-    x1.createWeight(context, searcher);
-    x2.createWeight(context, searcher);
-    y1.createWeight(context, searcher);
-    y2.createWeight(context, searcher);
+    p1.createWeight(context, searcher);
+    p2.createWeight(context, searcher);
+
   }
 
   @Override
@@ -127,20 +142,16 @@
     if (this.getClass() != o.getClass()) return false;
     HaversineFunction other = (HaversineFunction) o;
     return this.name().equals(other.name())
-            && x1.equals(other.x1) &&
-            y1.equals(other.y1) &&
-            x2.equals(other.x2) &&
-            y2.equals(other.y2) && radius == other.radius;
+            && p1.equals(other.p1) &&
+            p2.equals(other.p2) && radius == other.radius;
   }
 
   @Override
   public int hashCode() {
     int result;
     long temp;
-    result = x1.hashCode();
-    result = 31 * result + y1.hashCode();
-    result = 31 * result + x2.hashCode();
-    result = 31 * result + y2.hashCode();
+    result = p1.hashCode();
+    result = 31 * result + p2.hashCode();
     result = 31 * result + name().hashCode();
     temp = Double.doubleToRawLongBits(radius);
     result = 31 * result + (int) (temp ^ (temp >>> 32));
@@ -150,7 +161,7 @@
   public String description() {
     StringBuilder sb = new StringBuilder();
     sb.append(name()).append('(');
-    sb.append(x1).append(',').append(y1).append(',').append(x2).append(',').append(y2);
+    sb.append(p1).append(',').append(p2);
     sb.append(')');
     return sb.toString();
   }

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/SquaredEuclideanFunction.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/SquaredEuclideanFunction.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/SquaredEuclideanFunction.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/SquaredEuclideanFunction.java Thu Dec 24 13:03:22 2009
@@ -18,6 +18,7 @@
 
 import org.apache.solr.search.function.DocValues;
 import org.apache.solr.search.function.ValueSource;
+import org.apache.solr.search.MultiValueSource;
 
 import java.util.List;
 
@@ -30,8 +31,8 @@
 public class SquaredEuclideanFunction extends VectorDistanceFunction {
   protected String name = "sqedist";
 
-  public SquaredEuclideanFunction(List<ValueSource> sources1, List<ValueSource> sources2) {
-    super(-1, sources1, sources2);//overriding distance, so power doesn't matter here
+  public SquaredEuclideanFunction(MultiValueSource source1, MultiValueSource source2) {
+    super(-1, source1, source2);//overriding distance, so power doesn't matter here
   }
 
 
@@ -43,11 +44,16 @@
   /**
    * @param doc The doc to score
    */
-  protected double distance(int doc, DocValues[] docValues1, DocValues[] docValues2) {
+  protected double distance(int doc, DocValues dv1, DocValues dv2) {
     double result = 0;
-    for (int i = 0; i < docValues1.length; i++) {
-      result += Math.pow(docValues1[i].doubleVal(doc) - docValues2[i].doubleVal(doc), 2);
-    }
+    double [] vals1 = new double[source1.dimension()];
+    double [] vals2 = new double[source1.dimension()];
+    dv1.doubleVal(doc, vals1);
+    dv2.doubleVal(doc, vals2);
+    for (int i = 0; i < vals1.length; i++) {
+        double v = vals1[i] - vals2[i];
+        result += v * v;
+      }
     return result;
   }
 

Modified: lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/VectorDistanceFunction.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/VectorDistanceFunction.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/VectorDistanceFunction.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/function/distance/VectorDistanceFunction.java Thu Dec 24 13:03:22 2009
@@ -21,9 +21,9 @@
 import org.apache.solr.common.SolrException;
 import org.apache.solr.search.function.DocValues;
 import org.apache.solr.search.function.ValueSource;
+import org.apache.solr.search.MultiValueSource;
 
 import java.io.IOException;
-import java.util.List;
 import java.util.Map;
 
 
@@ -41,18 +41,18 @@
  * @see SquaredEuclideanFunction for the special case
  */
 public class VectorDistanceFunction extends ValueSource {
-  protected List<ValueSource> sources1, sources2;
+  protected MultiValueSource source1, source2;
   protected float power;
   protected float oneOverPower;
 
-  public VectorDistanceFunction(float power, List<ValueSource> sources1, List<ValueSource> sources2) {
-    this.power = power;
-    this.oneOverPower = 1 / power;
-    this.sources1 = sources1;
-    this.sources2 = sources2;
-    if ((sources1.size() != sources2.size())) {
+  public VectorDistanceFunction(float power, MultiValueSource source1, MultiValueSource source2) {
+    if ((source1.dimension() != source2.dimension())) {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Illegal number of sources");
     }
+    this.power = power;
+    this.oneOverPower = 1 / power;
+    this.source1 = source1;
+    this.source2 = source2;
   }
 
   protected String name() {
@@ -63,37 +63,39 @@
    * Calculate the distance
    *
    * @param doc        The current doc
-   * @param docValues1 The values from the first set of value sources
-   * @param docValues2 The values from the second set of value sources
+   * @param dv1 The values from the first MultiValueSource
+   * @param dv2 The values from the second MultiValueSource
    * @return The distance
    */
-  protected double distance(int doc, DocValues[] docValues1, DocValues[] docValues2) {
+  protected double distance(int doc, DocValues dv1, DocValues dv2) {
     double result = 0;
     //Handle some special cases:
+    double [] vals1 = new double[source1.dimension()];
+    double [] vals2 = new double[source1.dimension()];
+    dv1.doubleVal(doc, vals1);
+    dv2.doubleVal(doc, vals2);
     if (power == 0) {
-      for (int i = 0; i < docValues1.length; i++) {
-        //sparseness measure
-        result += docValues1[i].doubleVal(doc) - docValues2[i].doubleVal(doc) == 0 ? 0 : 1;
+      for (int i = 0; i < vals1.length; i++) {
+        result += vals1[i] - vals2[i] == 0 ? 0 :1;
       }
+
     } else if (power == 1.0) {
-      for (int i = 0; i < docValues1.length; i++) {
-        result += docValues1[i].doubleVal(doc) - docValues2[i].doubleVal(doc);
+      for (int i = 0; i < vals1.length; i++) {
+        result += vals1[i] - vals2[i];
       }
     } else if (power == 2.0) {
-      for (int i = 0; i < docValues1.length; i++) {
-        double v = docValues1[i].doubleVal(doc) - docValues2[i].doubleVal(doc);
+      for (int i = 0; i < vals1.length; i++) {
+        double v = vals1[i] - vals2[i];
         result += v * v;
       }
       result = Math.sqrt(result);
     } else if (power == Integer.MAX_VALUE || Double.isInfinite(power)) {//infininte norm?
-      for (int i = 0; i < docValues1.length; i++) {
-        //TODO: is this the correct infinite norm?
-        result = Math.max(docValues1[i].doubleVal(doc) - docValues2[i].doubleVal(doc), result);
+      for (int i = 0; i < vals1.length; i++) {
+        result = Math.max(vals1[i], vals2[i]);
       }
-
     } else {
-      for (int i = 0; i < docValues1.length; i++) {
-        result += Math.pow(docValues1[i].doubleVal(doc) - docValues2[i].doubleVal(doc), power);
+      for (int i = 0; i < vals1.length; i++) {
+        result += Math.pow(vals1[i] - vals2[i], power);
       }
       result = Math.pow(result, oneOverPower);
     }
@@ -103,19 +105,24 @@
 
   @Override
   public DocValues getValues(Map context, IndexReader reader) throws IOException {
-    final DocValues[] valsArr1 = new DocValues[sources1.size()];
-    int i = 0;
-    for (ValueSource source : sources1) {
-      valsArr1[i++] = source.getValues(context, reader);
-    }
-    final DocValues[] valsArr2 = new DocValues[sources2.size()];
-    i = 0;
-    for (ValueSource source : sources2) {
-      valsArr2[i++] = source.getValues(context, reader);
-    }
+
+    final DocValues vals1 = source1.getValues(context, reader);
+
+    final DocValues vals2 = source2.getValues(context, reader);
+
 
 
     return new DocValues() {
+      @Override
+      public byte byteVal(int doc) {
+        return (byte) doubleVal(doc);
+      }
+
+      @Override
+      public short shortVal(int doc) {
+        return (short)doubleVal(doc);
+      }
+
       public float floatVal(int doc) {
         return (float) doubleVal(doc);
       }
@@ -129,7 +136,7 @@
       }
 
       public double doubleVal(int doc) {
-        return distance(doc, valsArr1, valsArr2);
+        return distance(doc, vals1, vals2);
       }
 
       public String strVal(int doc) {
@@ -141,18 +148,8 @@
         StringBuilder sb = new StringBuilder();
         sb.append(name()).append('(').append(power).append(',');
         boolean firstTime = true;
-        for (DocValues vals : valsArr1) {
-          if (firstTime) {
-            firstTime = false;
-          } else {
-            sb.append(',');
-          }
-          sb.append(vals.toString(doc));
-        }
-        for (DocValues vals : valsArr2) {
-          sb.append(',');//we will always have valsArr1, else there is an error
-          sb.append(vals.toString(doc));
-        }
+        sb.append(vals1.toString(doc)).append(',');
+        sb.append(vals2.toString(doc));
         sb.append(')');
         return sb.toString();
       }
@@ -161,12 +158,8 @@
 
   @Override
   public void createWeight(Map context, Searcher searcher) throws IOException {
-    for (ValueSource source : sources1) {
-      source.createWeight(context, searcher);
-    }
-    for (ValueSource source : sources2) {
-      source.createWeight(context, searcher);
-    }
+    source1.createWeight(context, searcher);
+    source2.createWeight(context, searcher);
   }
 
   @Override
@@ -177,16 +170,16 @@
     VectorDistanceFunction that = (VectorDistanceFunction) o;
 
     if (Float.compare(that.power, power) != 0) return false;
-    if (!sources1.equals(that.sources1)) return false;
-    if (!sources2.equals(that.sources2)) return false;
+    if (!source1.equals(that.source1)) return false;
+    if (!source2.equals(that.source2)) return false;
 
     return true;
   }
 
   @Override
   public int hashCode() {
-    int result = sources1.hashCode();
-    result = 31 * result + sources2.hashCode();
+    int result = source1.hashCode();
+    result = 31 * result + source2.hashCode();
     result = 31 * result + Float.floatToRawIntBits(power);
     return result;
   }
@@ -195,19 +188,8 @@
   public String description() {
     StringBuilder sb = new StringBuilder();
     sb.append(name()).append('(').append(power).append(',');
-    boolean firstTime = true;
-    for (ValueSource source : sources1) {
-      if (firstTime) {
-        firstTime = false;
-      } else {
-        sb.append(',');
-      }
-      sb.append(source);
-    }
-    for (ValueSource source : sources2) {
-      sb.append(',');//we will always have sources1, else there is an error
-      sb.append(source);
-    }
+    sb.append(source1).append(',');
+    sb.append(source2);
     sb.append(')');
     return sb.toString();
   }

Modified: lucene/solr/trunk/src/java/org/apache/solr/update/DocumentBuilder.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/update/DocumentBuilder.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/update/DocumentBuilder.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/update/DocumentBuilder.java Thu Dec 24 13:03:22 2009
@@ -57,19 +57,35 @@
     // we don't check for a null val ourselves because a solr.FieldType
     // might actually want to map it to something.  If createField()
     // returns null, then we don't store the field.
-    Field field = sfield.createField(val, boost);
-    if (field != null) {
-      if (!sfield.multiValued()) {
-        String oldValue = map.put(sfield.getName(), val);
-        if (oldValue != null) {
-          throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"ERROR: multiple values encountered for non multiValued field " + sfield.getName()
-                  + ": first='" + oldValue + "' second='" + val + "'");
+    if (sfield.isPolyField()) {
+      Fieldable[] fields = sfield.createFields(val, boost);
+      if (fields != null && fields.length > 0) {
+        if (!sfield.multiValued()) {
+          String oldValue = map.put(sfield.getName(), val);
+          if (oldValue != null) {
+            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "ERROR: multiple values encountered for non multiValued field " + sfield.getName()
+                    + ": first='" + oldValue + "' second='" + val + "'");
+          }
+        }
+        // Add each field
+        for (Fieldable field : fields) {
+          doc.add(field);
+        }
+      }
+    } else {
+      Field field = sfield.createField(val, boost);
+      if (field != null) {
+        if (!sfield.multiValued()) {
+          String oldValue = map.put(sfield.getName(), val);
+          if (oldValue != null) {
+            throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"ERROR: multiple values encountered for non multiValued field " + sfield.getName()
+                    + ": first='" + oldValue + "' second='" + val + "'");
+          }
         }
       }
-       
-      // field.setBoost(boost);
       doc.add(field);
     }
+
   }
 
   /**
@@ -147,7 +163,7 @@
     for (SchemaField field : schema.getRequiredFields()) {
       if (doc.getField(field.getName() ) == null) {
         if (field.getDefaultValue() != null) {
-          doc.add( field.createField( field.getDefaultValue(), 1.0f ) );
+          addField(doc, field, field.getDefaultValue(), 1.0f);
         } else {
           if (missingFields==null) {
             missingFields = new ArrayList<String>(1);
@@ -176,6 +192,19 @@
     Document ret = doc; doc=null;
     return ret;
   }
+
+
+  private static void addField(Document doc, SchemaField field, String val, float boost) {
+    if (field.isPolyField()) {
+      Fieldable[] farr = field.getType().createFields(field, val, boost);
+      for (Fieldable f : farr) {
+        if (f != null) doc.add(f); // null fields are not added
+      }
+    } else {
+      Field f = field.createField(val, boost);
+      if (f != null) doc.add(f);  // null fields are not added
+    }
+  }
   
 
   /**
@@ -230,7 +259,9 @@
           isBinaryField = true;
           BinaryField binaryField = (BinaryField) sfield.getType();
           Field f = binaryField.createField(sfield,v,boost);
-          if(f != null) out.add(f);
+          if(f != null){
+            out.add(f);
+          }
           used = true;
         } else {
           // TODO!!! HACK -- date conversion
@@ -243,10 +274,7 @@
 
           if (sfield != null) {
             used = true;
-            Field f = sfield.createField(val, boost);
-            if (f != null) { // null fields are not added
-              out.add(f);
-            }
+            addField(out, sfield, val, boost);
           }
         }
 
@@ -263,17 +291,21 @@
           }
 
           used = true;
-          Field f = null;
+          //Don't worry about poly fields here
+          Fieldable [] fields = null;
           if (isBinaryField) {
             if (destinationField.getType() instanceof BinaryField) {
               BinaryField binaryField = (BinaryField) destinationField.getType();
-              f = binaryField.createField(destinationField, v, boost);
+              //TODO: safe to assume that binary fields only create one?
+              fields = new Field[]{binaryField.createField(destinationField, v, boost)};
             }
           } else {
-            f = destinationField.createField(cf.getLimitedValue(val), boost);
+            fields = destinationField.createFields(cf.getLimitedValue(val), boost);
           }
-          if (f != null) { // null fields are not added
-            out.add(f);
+          if (fields != null) { // null fields are not added
+            for (Fieldable f : fields) {
+              out.add(f);
+            }
           }
         }
         
@@ -297,7 +329,7 @@
     for (SchemaField field : schema.getRequiredFields()) {
       if (out.getField(field.getName() ) == null) {
         if (field.getDefaultValue() != null) {
-          out.add( field.createField( field.getDefaultValue(), 1.0f ) );
+          addField(out, field, field.getDefaultValue(), 1.0f);
         } 
         else {
           String id = schema.printableUniqueKey( out );

Modified: lucene/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/util/AbstractSolrTestCase.java Thu Dec 24 13:03:22 2009
@@ -231,6 +231,17 @@
     }
   }
 
+  public void assertQEx(String message, SolrQueryRequest req, SolrException.ErrorCode code ) {
+    try {
+      h.query(req);
+      fail( message );
+    } catch (SolrException e) {
+      assertEquals( code.code, e.code() );
+    } catch (Exception e2) {
+      throw new RuntimeException("Exception during query", e2);
+    }
+  }
+
   
   /**
    * @see TestHarness#optimize

Added: lucene/solr/trunk/src/test/org/apache/solr/schema/PolyFieldTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/schema/PolyFieldTest.java?rev=893746&view=auto
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/schema/PolyFieldTest.java (added)
+++ lucene/solr/trunk/src/test/org/apache/solr/schema/PolyFieldTest.java Thu Dec 24 13:03:22 2009
@@ -0,0 +1,196 @@
+package org.apache.solr.schema;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Fieldable;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.index.IndexReader;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.util.AbstractSolrTestCase;
+import org.apache.solr.common.SolrException;
+
+import java.util.Map;
+import java.util.Random;
+
+
+/**
+ * Test a whole slew of things related to PolyFields
+ */
+public class PolyFieldTest extends AbstractSolrTestCase {
+
+  @Override
+  public String getSchemaFile() {
+    return "schema.xml";
+  }
+
+  @Override
+  public String getSolrConfigFile() {
+    return "solrconfig.xml";
+  }
+
+  public void testSchemaBasics() throws Exception {
+    IndexSchema schema = h.getCore().getSchema();
+
+
+    SchemaField home = schema.getField("home");
+    assertNotNull(home);
+    assertTrue(home.isPolyField());
+
+    SchemaField[] dynFields = schema.getDynamicFieldPrototypes();
+    boolean seen = false;
+    for (SchemaField dynField : dynFields) {
+      if (dynField.getName().equals("*" + FieldType.POLY_FIELD_SEPARATOR + "double")) {
+        seen = true;
+      }
+    }
+    assertTrue("Didn't find the expected dynamic field", seen);
+    FieldType homeFT = schema.getFieldType("home");
+    assertEquals(home.getType(), homeFT);
+    FieldType xy = schema.getFieldTypeByName("xy");
+    assertNotNull(xy);
+    assertTrue(xy instanceof PointType);
+    assertTrue(xy.isPolyField());
+    home = schema.getFieldOrNull("home_0" + FieldType.POLY_FIELD_SEPARATOR + "double");
+    assertNotNull(home);
+    home = schema.getField("home");
+    assertNotNull(home);
+    homeFT = schema.getPolyFieldType("home");
+    assertNotNull(homeFT);
+
+    home = schema.getField("homed");//sub field suffix
+    assertNotNull(home);
+    assertTrue(home.isPolyField());
+
+    try {
+      FieldType bad = schema.getPolyFieldType("foo");
+      assertTrue(false);
+    } catch (Exception e) {
+    }
+    try {
+      FieldType bad = schema.getPolyFieldTypeNoEx("foo");
+      assertNull(bad);
+    } catch (Exception e) {
+      assertTrue(false);
+    }
+  }
+
+  public void testPointFieldType() throws Exception {
+    SolrCore core = h.getCore();
+    IndexSchema schema = core.getSchema();
+    SchemaField home = schema.getField("home");
+    assertNotNull(home);
+    assertTrue("home is not a poly field", home.isPolyField());
+    FieldType tmp = home.getType();
+    assertTrue(tmp instanceof PointType);
+    PointType pt = (PointType) tmp;
+    assertEquals(pt.getDimension(), 2);
+    double[] xy = new double[]{35.0, -79.34};
+    String point = xy[0] + "," + xy[1];
+    Fieldable[] fields = home.createFields(point, 2);
+    assertEquals(fields.length, 3);//should be 3, we have a stored field
+    //first two fields contain the values, third is just stored and contains the original
+    for (int i = 0; i < 3; i++) {
+      boolean hasValue = fields[1].tokenStreamValue() != null
+              || fields[1].getBinaryValue() != null
+              || fields[1].stringValue() != null;
+      assertTrue("Doesn't have a value: " + fields[1], hasValue);
+    }
+    /*assertTrue("first field " + fields[0].tokenStreamValue() +  " is not 35.0", pt.getSubType().toExternal(fields[0]).equals(String.valueOf(xy[0])));
+    assertTrue("second field is not -79.34", pt.getSubType().toExternal(fields[1]).equals(String.valueOf(xy[1])));
+    assertTrue("third field is not '35.0,-79.34'", pt.getSubType().toExternal(fields[2]).equals(point));*/
+
+
+    home = schema.getField("home_ns");
+    assertNotNull(home);
+    fields = home.createFields(point, 2);
+    assertEquals(fields.length, 2);//should be 2, since we aren't storing
+
+    home = schema.getField("home_ns");
+    assertNotNull(home);
+    try {
+      fields = home.createFields("35.0,foo", 2);
+      assertTrue(false);
+    } catch (Exception e) {
+      //
+    }
+  }
+
+  public void testSearching() throws Exception {
+    for (int i = 0; i < 50; i++) {
+      assertU(adoc("id", "" + i, "home", i + "," + (i * 100), "homed", (i * 1000) + "," + (i * 10000)));
+    }
+    assertU(commit());
+    IndexReader reader = h.getCore().getSearcher().get().getReader();
+    /*for (int i = 0; i < 50; i++){
+      Document doc = reader.document(i);
+      System.out.println("Doc: " + doc.get("homed_0___double"));
+    }*/
+    assertQ(req("fl", "*,score", "q", "*:*"), "//*[@numFound='50']");
+    assertQ(req("fl", "*,score", "q", "home:1,100"),
+            "//*[@numFound='1']",
+            "//str[@name='home'][.='1,100']");
+    assertQ(req("fl", "*,score", "q", "homed:1000,10000"),
+            "//*[@numFound='1']",
+            "//str[@name='homed'][.='1000,10000']");
+    assertQ(req("fl", "*,score", "q",
+            "{!func}sqedist(home, toMultiVS(0, 0))"),
+            "\"//*[@numFound='50']\"");
+    assertQ(req("fl", "*,score", "q",
+            "{!func}dist(2, home, toMultiVS(0, 0))"),
+            "\"//*[@numFound='50']\"");
+
+    assertQ(req("fl", "*,score", "q",
+            "home:[10,10000 TO 30,30000]"),
+            "\"//*[@numFound='3']\"");
+    assertQ(req("fl", "*,score", "q",
+            "homed:[1,1000 TO 2000,35000]"),
+            "\"//*[@numFound='2']\"");
+    //bad
+    
+    assertQEx("Query should throw an exception due to incorrect dimensions", req("fl", "*,score", "q",
+            "homed:[1 TO 2000]"), SolrException.ErrorCode.BAD_REQUEST);
+  }
+
+  
+
+  public void testSearchDetails() throws Exception {
+    SolrCore core = h.getCore();
+    IndexSchema schema = core.getSchema();
+    double[] xy = new double[]{35.0, -79.34};
+    String point = xy[0] + "," + xy[1];
+    //How about some queries?
+    //don't need a parser for this path currently.  This may change
+    assertU(adoc("id", "0", "home_ns", point));
+    assertU(commit());
+    SchemaField home = schema.getField("home_ns");
+    PointType pt = (PointType) home.getType();
+    assertEquals(pt.getDimension(), 2);
+    Query q = pt.getFieldQuery(null, home, point);
+    assertNotNull(q);
+    assertTrue(q instanceof BooleanQuery);
+    //should have two clauses, one for 35.0 and the other for -79.34
+    BooleanQuery bq = (BooleanQuery) q;
+    BooleanClause[] clauses = bq.getClauses();
+    assertEquals(clauses.length, 2);
+
+  }
+}
\ No newline at end of file

Propchange: lucene/solr/trunk/src/test/org/apache/solr/schema/PolyFieldTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/solr/trunk/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java Thu Dec 24 13:03:22 2009
@@ -16,9 +16,9 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.spatial.geohash.GeoHashUtils;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.util.AbstractSolrTestCase;
-import org.apache.lucene.spatial.geohash.GeoHashUtils;
 
 
 /**
@@ -44,20 +44,21 @@
     assertU(adoc("id", "2", "x_td", "0", "y_td", String.valueOf(Math.PI / 2), "gh_s", GeoHashUtils.encode(32.7693246, -78.9289094)));
     assertU(adoc("id", "3", "x_td", String.valueOf(Math.PI / 2), "y_td", String.valueOf(Math.PI / 2), "gh_s", GeoHashUtils.encode(32.7693246, -80.9289094)));
     assertU(adoc("id", "4", "x_td", String.valueOf(Math.PI / 4), "y_td", String.valueOf(Math.PI / 4), "gh_s", GeoHashUtils.encode(32.7693246, -81.9289094)));
+    assertU(adoc("id", "5", "x_td", "45.0", "y_td", "45.0",
+            "gh_s", GeoHashUtils.encode(32.7693246, -81.9289094)));
     assertU(commit());
     //Get the haversine distance between the point 0,0 and the docs above assuming a radius of 1
-    assertQ(req("fl", "*,score", "q", "{!func}hsin(x_td, y_td, 0, 0, 1)", "fq", "id:1"), "//float[@name='score']='0.0'");
-    assertQ(req("fl", "*,score", "q", "{!func}hsin(x_td, y_td, 0, 0, 1)", "fq", "id:2"), "//float[@name='score']='" + (float) (Math.PI / 2) + "'");
-    assertQ(req("fl", "*,score", "q", "{!func}hsin(x_td, y_td, 0, 0, 1)", "fq", "id:3"), "//float[@name='score']='" + (float) (Math.PI / 2) + "'");
-    assertQ(req("fl", "*,score", "q", "{!func}hsin(x_td, y_td, 0, 0, 1)", "fq", "id:4"), "//float[@name='score']='1.0471976'");
+    assertQ(req("fl", "*,score", "q", "{!func}hsin(1, x_td, y_td, 0, 0)", "fq", "id:1"), "//float[@name='score']='0.0'");
+    assertQ(req("fl", "*,score", "q", "{!func}hsin(1, x_td, y_td, 0, 0)", "fq", "id:2"), "//float[@name='score']='" + (float) (Math.PI / 2) + "'");
+    assertQ(req("fl", "*,score", "q", "{!func}hsin(1, x_td, y_td, 0, 0)", "fq", "id:3"), "//float[@name='score']='" + (float) (Math.PI / 2) + "'");
+    assertQ(req("fl", "*,score", "q", "{!func}hsin(1, x_td, y_td, 0, 0)", "fq", "id:4"), "//float[@name='score']='1.0471976'");
+    assertQ(req("fl", "*,score", "q", "{!func}hsin(1, x_td, y_td, 0, 0, true)", "fq", "id:5"), "//float[@name='score']='1.0471976'");
 
     //Geo Hash Haversine
     //Can verify here: http://www.movable-type.co.uk/scripts/latlong.html, but they use a slightly different radius for the earth, so just be close
-    assertQ(req("fl", "*,score", "q", "{!func}ghhsin(gh_s, \"" + GeoHashUtils.encode(32, -79) +
-            "\"," + Constants.EARTH_RADIUS_KM +
-            ")", "fq", "id:1"), "//float[@name='score']='122.30894'");
-    assertQ(req("fl", "*,score", "q", "{!func}ghhsin(gh_s, geohash(32, -79)," + Constants.EARTH_RADIUS_KM +
-            ")", "fq", "id:1"), "//float[@name='score']='122.30894'");
+    assertQ(req("fl", "*,score", "q", "{!func}ghhsin(" + Constants.EARTH_RADIUS_KM + ", gh_s, \"" + GeoHashUtils.encode(32, -79) +
+            "\",)", "fq", "id:1"), "//float[@name='score']='122.30894'");
+    assertQ(req("fl", "*,score", "q", "{!func}ghhsin(" + Constants.EARTH_RADIUS_KM + ", gh_s, geohash(32, -79))", "fq", "id:1"), "//float[@name='score']='122.30894'");
   }
 
   public void testVector() throws Exception {
@@ -66,6 +67,8 @@
     assertU(adoc("id", "3", "x_td", "1", "y_td", "1", "z_td", "1", "w_td", "1"));
     assertU(adoc("id", "4", "x_td", "1", "y_td", "0", "z_td", "0", "w_td", "0"));
     assertU(adoc("id", "5", "x_td", "2.3", "y_td", "5.5", "z_td", "7.9", "w_td", "-2.4"));
+    assertU(adoc("id", "6", "point", "1.0,0.0"));
+    assertU(adoc("id", "7", "point", "5.5,10.9"));
     assertU(commit());
     //two dimensions, notice how we only pass in 4 value sources
     assertQ(req("fl", "*,score", "q", "{!func}sqedist(x_td, y_td, 0, 0)", "fq", "id:1"), "//float[@name='score']='0.0'");
@@ -111,6 +114,15 @@
     assertQ(req("fl", "*,score", "q", "{!func}dist(1, x_td, y_td, 0, 0)", "fq", "id:3"), "//float[@name='score']='" + (float) 2.0 + "'");
     assertQ(req("fl", "*,score", "q", "{!func}dist(1, x_td, y_td, 0, 0)", "fq", "id:4"), "//float[@name='score']='1.0'");
     assertQ(req("fl", "*,score", "q", "{!func}dist(1, x_td, y_td, 0, 0)", "fq", "id:5"), "//float[@name='score']='" + (float) (2.3 + 5.5) + "'");
+
+
+    //Do point tests:
+    assertQ(req("fl", "*,score", "q", "{!func}dist(1, toMultiVS(x_td, y_td), toMultiVS(0, 0))", "fq", "id:5"),
+            "//float[@name='score']='" + (float) (2.3 + 5.5) + "'");
+
+    assertQ(req("fl", "*,score", "q", "{!func}dist(1, point, toMultiVS(0, 0))", "fq", "id:6"),
+            "//float[@name='score']='" + 0.0f + "'");
+
   }
 
 }

Modified: lucene/solr/trunk/src/test/org/apache/solr/update/DocumentBuilderTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/update/DocumentBuilderTest.java?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/update/DocumentBuilderTest.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/update/DocumentBuilderTest.java Thu Dec 24 13:03:22 2009
@@ -22,6 +22,7 @@
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.util.AbstractSolrTestCase;
+import org.apache.solr.schema.FieldType;
 
 /**
  * 
@@ -59,4 +60,17 @@
     Document out = DocumentBuilder.toDocument( doc, core.getSchema() );
     assertNull( out.get( "name" ) );
   }
+
+  public void testMultiField() throws Exception {
+    SolrCore core = h.getCore();
+
+    // make sure a null value is not indexed
+    SolrInputDocument doc = new SolrInputDocument();
+    doc.addField( "home", "2.2,3.3", 1.0f );
+    Document out = DocumentBuilder.toDocument( doc, core.getSchema() );
+    assertNotNull( out.get( "home" ) );//contains the stored value and term vector, if there is one
+    assertNotNull( out.getField( "home_0" + FieldType.POLY_FIELD_SEPARATOR + "double" ) );
+    assertNotNull( out.getField( "home_1" + FieldType.POLY_FIELD_SEPARATOR + "double" ) );
+  }
+
 }

Modified: lucene/solr/trunk/src/test/test-files/solr/conf/schema.xml
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/test-files/solr/conf/schema.xml?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/test-files/solr/conf/schema.xml (original)
+++ lucene/solr/trunk/src/test/test-files/solr/conf/schema.xml Thu Dec 24 13:03:22 2009
@@ -367,6 +367,12 @@
   </fieldtype>
 
   <fieldType name="uuid" class="solr.UUIDField" />
+    
+    <!-- Try out some point types -->
+  <fieldType name="xy" class="solr.PointType" dimension="2" subFieldType="double"/>
+  <fieldType name="tenD" class="solr.PointType" dimension="10" subFieldType="double"/>
+    <!-- Use the sub field suffix -->
+  <fieldType name="xyd" class="solr.PointType" dimension="2" subFieldSuffix="*_d"/>
   
  </types>
 
@@ -392,6 +398,15 @@
    <field name="shouldbestored" type="unstored" stored="true"/>
    <field name="shouldbeunindexed" type="unstored" indexed="false" stored="true"/>
 
+   <!-- Test points -->
+      <!-- Test points -->
+   <field name="home" type="xy" indexed="true" stored="true" multiValued="false"/>
+   <field name="homed" type="xyd" indexed="true" stored="true" multiValued="false"/>
+   <field name="home_ns" type="xy" indexed="true" stored="false" multiValued="false"/>
+   <field name="work" type="xy" indexed="true" stored="true" multiValued="false"/>
+
+   <field name="point10" type="tenD" indexed="true" stored="true" multiValued="false"/>
+
 
    <!-- test different combinations of indexed and stored -->
    <field name="bind" type="boolean" indexed="true" stored="false"/>

Modified: lucene/solr/trunk/src/test/test-files/solr/conf/schema11.xml
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/test-files/solr/conf/schema11.xml?rev=893746&r1=893745&r2=893746&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/test-files/solr/conf/schema11.xml (original)
+++ lucene/solr/trunk/src/test/test-files/solr/conf/schema11.xml Thu Dec 24 13:03:22 2009
@@ -251,6 +251,10 @@
     <fieldType name="tdoubles" class="solr.TrieDoubleField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true" />
     <fieldType name="tdates" class="solr.TrieDateField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true" />
 
+    <!-- Poly field -->
+    <fieldType name="xy" class="solr.PointType" dimension="2" subFieldType="double"/>
+    <fieldType name="xyd" class="solr.PointType" dimension="2" subFieldSuffix="*_d"/>
+
  </types>
 
 
@@ -275,7 +279,11 @@
 
    <!-- for testing, a type that does a transform to see if it's correctly done everywhere -->
    <field name="id" type="sfloat" indexed="true" stored="true" required="true" /> 
-   <field name="text" type="text" indexed="true" stored="false" /> 
+   <field name="text" type="text" indexed="true" stored="false" />
+
+   <!-- Test a point field for distances -->
+   <field name="point" type="xy" indexed="true" stored="true" multiValued="false"/>
+   <field name="pointD" type="xyd" indexed="true" stored="true" multiValued="false"/>
 
    <!-- Dynamic field definitions.  If a field name is not found, dynamicFields
         will be used if the name matches any of the patterns.