You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by yo...@apache.org on 2010/09/29 19:05:29 UTC

svn commit: r1002739 - in /lucene/dev/trunk/solr: ./ src/java/org/apache/solr/schema/ src/java/org/apache/solr/search/ src/java/org/apache/solr/search/function/ src/java/org/apache/solr/search/function/distance/ src/test/org/apache/solr/search/ src/tes...

Author: yonik
Date: Wed Sep 29 17:05:29 2010
New Revision: 1002739

URL: http://svn.apache.org/viewvc?rev=1002739&view=rev
Log:
SOLR-1568: friendly geodist function

Added:
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParser.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParser.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParserPlugin.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/ValueSourceParser.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstValueSource.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/VectorValueSource.java
    lucene/dev/trunk/solr/src/test/org/apache/solr/search/SpatialFilterTest.java
    lucene/dev/trunk/solr/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java
    lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema11.xml
    lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema12.xml

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Wed Sep 29 17:05:29 2010
@@ -95,10 +95,10 @@ New Features
 ----------------------
 
 * SOLR-1302: Added several new distance based functions, including
-  Great Circle (haversine), Manhattan, Euclidean and String (using the
-  StringDistance methods in the Lucene spellchecker).
+  Great Circle (haversine) for geo search, Manhattan, Euclidean
+  and String (using the StringDistance methods in the Lucene spellchecker).
   Also added geohash(), deg() and rad() convenience functions.
-  See http://wiki.apache.org/solr/FunctionQuery. (gsingers)
+  See http://wiki.apache.org/solr/FunctionQuery. (yonik, gsingers)
 
 * SOLR-1553: New dismax parser implementation (accessible as "edismax")
   that supports full lucene syntax, improved reserved char escaping,
@@ -130,8 +130,10 @@ New Features
 
 * SOLR-1653: Add PatternReplaceCharFilter (koji)
 
-* SOLR-1131: FieldTypes can now output multiple Fields per Type and still be searched.  This can be handy for hiding the details of a particular
-  implementation such as in the spatial case. (Chris Mattmann, shalin, noble, gsingers, yonik)
+* SOLR-1131: FieldTypes can now output multiple Fields per Type and still be
+  searched.  This can be handy for hiding the details of a particular
+  implementation such as in the spatial case.
+  (Chris Mattmann, shalin, noble, gsingers, yonik)
 
 * SOLR-1586: Add support for Geohash FieldType (Chris Mattmann, gsingers)
 
@@ -210,9 +212,10 @@ New Features
 
 * SOLR-1984: Add HyphenationCompoundWordTokenFilterFactory. (PB via rmuir)
 
-* SOLR-1568: Added "native" filtering support for PointType, GeohashField.  Added LatLonType with filtering support too.  See
-  	     http://wiki.apache.org/solr/SpatialSearch and the example.  Refactored some items in Lucene spatial. 
-	     Removed SpatialTileField as the underlying CartesianTier is broken beyond repair and is going to be moved. (gsingers) 
+* SOLR-1568: Added native filtering support via geofilt for spatial field
+  types LatLonType, PointType, GeohashField. 
+  See http://wiki.apache.org/solr/SpatialSearch for examples. 
+  (yonik, gsingers)
    
 * SOLR-2015: Add a boolean attribute autoGeneratePhraseQueries to TextField.
   autoGeneratePhraseQueries="true" (the default) causes the query parser to
@@ -494,8 +497,6 @@ Bug Fixes
   substitution/dereferencing.  Properly encode local params in
   distributed faceting.  (yonik)
 
-* SOLR-2114: Fixed parsing error in hsin function.  The function signature has changed slightly. (gsingers)
-
 * SOLR-2083: SpellCheckComponent misreports suggestions when distributed (James Dyer via gsingers)  
 
 * SOLR-2108: Fixed false positives when using wildcard queries on fields with reversed
@@ -516,8 +517,6 @@ Other Changes
 * SOLR-1592: Refactor XMLWriter startTag to allow arbitrary attributes to be written
   (Chris A. Mattmann via noble)
 
-* SOLR-1561: Added Lucene 2.9.1 spatial contrib jar to lib. (gsingers)
-
 * SOLR-1570: Log warnings if uniqueKey is multi-valued or not stored (hossman, shalin) 
 
 * SOLR-1558: QueryElevationComponent only works if the uniqueKey field is

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java Wed Sep 29 17:05:29 2010
@@ -43,8 +43,6 @@ import java.util.Set;
 
 /**
  * Represents a Latitude/Longitude as a 2 dimensional point.  Latitude is <b>always</b> specified first.
- * Can also, optionally, integrate in Spatial Tile capabilities.  The default is for tile fields from 4 - 15,
- * just as in the SpatialTileField that we are extending.
  */
 public class LatLonType extends AbstractSubTypeFieldType implements SpatialQueryable {
   protected static final int LAT = 0;
@@ -511,7 +509,7 @@ class SpatialDistanceQuery extends Query
   {
     float boost = getBoost();
     return (boost!=1.0?"(":"") +
-            "sfilt(latlonSource="+origField +"(" + latSource + "," + lonSource + ")"
+            "geofilt(latlonSource="+origField +"(" + latSource + "," + lonSource + ")"
             +",latCenter="+latCenter+",lonCenter="+lonCenter
             +",dist=" + dist
             +",latMin=" + latMin + ",latMax="+latMax

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java Wed Sep 29 17:05:29 2010
@@ -41,11 +41,8 @@ import java.util.List;
 import java.util.ArrayList;
 
 /**
- * A point type that indexes a point in an n-dimensional space as separate fields and uses
- * range queries for bounding box calculations.
- * <p/>
- * <p/>
- * NOTE: There can only be one sub type
+ * A point type that indexes a point in an n-dimensional space as separate fields and supports range queries.
+ * See {@link LatLonType} for geo-spatial queries.
  */
 public class PointType extends CoordinateFieldType implements SpatialQueryable {
 

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java Wed Sep 29 17:05:29 2010
@@ -25,8 +25,8 @@ import org.apache.solr.search.SpatialOpt
 
 /**
  * Indicate that the implementing class is capable of generating a Query against spatial resources.
- * For example, the PointType is capable of creating a query that restricts the document space down
- * to documents that are within a certain distance of a given point. *
+ * For example, the LatLonType is capable of creating a query that restricts the document space down
+ * to documents that are within a certain distance of a given point on Earth. *
  *
  **/
 public interface SpatialQueryable {

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParser.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParser.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParser.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParser.java Wed Sep 29 17:05:29 2010
@@ -152,7 +152,7 @@ public abstract class QParser {
   //                       $x+=foo (append to global for limited scope)
 
   /** check both local and global params */
-  protected String getParam(String name) {
+  public String getParam(String name) {
     String val;
     if (localParams != null) {
       val = localParams.get(name);

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParser.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParser.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParser.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParser.java Wed Sep 29 17:05:29 2010
@@ -33,24 +33,7 @@ import org.apache.solr.schema.SpatialQue
 
 
 /**
- * Creates a spatial Filter based on the type of spatial point used.
- * <p/>
- * The field must implement {@link org.apache.solr.schema.SpatialQueryable}
- * <p/>
- * All units are in Kilometers
- * <p/>
- * <p/>
- * Syntax:
- * <pre>{!sfilt fl=location [units=[K|M]] [meas=[0-INF|hsin|sqe]] }&pt=49.32,-79.0&d=20</pre>
- * <p/>
- * Parameters:
- * <ul>
- * <li>fl - The fields to filter on.  Must implement XXXX. Required.  If more than one, XXXX</li>
- * <li>pt - The point to use as a reference.  Must match the dimension of the field. Required.</li>
- * <li>d - The distance in the units specified. Required.</li>
- * <li>meas - The distance measure to use.  Default is Euclidean (2-norm).  If a number between 0-INF is used, then the Vector Distance is used.  hsin = Haversine, sqe = Squared Euclidean</li>
- * </ul> *
- *
+ * @see {@link SpatialFilterQParserPlugin}
  */
 public class SpatialFilterQParser extends QParser {
   boolean bbox;  // do bounding box only
@@ -66,17 +49,17 @@ public class SpatialFilterQParser extend
   public Query parse() throws ParseException {
     //if more than one, we need to treat them as a point...
     //TODO: Should we accept multiple fields
-    String[] fields = localParams.getParams(CommonParams.FL);
+    String[] fields = localParams.getParams("f");
     if (fields == null || fields.length == 0) {
       String field = getParam(SpatialParams.FIELD);
       if (field == null)
-        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, " missing field for spatial request");
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, " missing sfield for spatial request");
       fields = new String[] {field};
     }
     
     String pointStr = getParam(SpatialParams.POINT);
     if (pointStr == null) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, SpatialParams.POINT + " is not properly specified");
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, SpatialParams.POINT + " missing.");
     }
 
     double dist = -1;

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParserPlugin.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParserPlugin.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParserPlugin.java Wed Sep 29 17:05:29 2010
@@ -21,11 +21,35 @@ import org.apache.solr.common.util.Named
 import org.apache.solr.request.SolrQueryRequest;
 
 /**
- * Creates a {@link org.apache.solr.search.QParser} that can create Spatial {@link org.apache.lucene.search.Filter}s.
- * The filters are tied to implementations of {@link org.apache.solr.schema.SpatialQueryable}
+ * Creates a spatial Filter based on the type of spatial point used.
+ * <p/>
+ * The field must implement {@link org.apache.solr.schema.SpatialQueryable}
+ * <p/>
+ * All units are in Kilometers
+ * <p/>
+ * <p/>
+ * Syntax:
+ * <pre>{!geofilt sfield=&lt;location_field&gt; pt=&lt;lat,lon&gt; d=&lt;distance&gt;}</pre>
+ * <p/>
+ * Parameters:
+ * <ul>
+ * <li>sfield - The field to filter on. Required.</li>
+ * <li>pt - The point to use as a reference.  Must match the dimension of the field. Required.</li>
+ * <li>d - The distance in km.  Requited.</li>
+ * </ul>
+ * The distance measure used currently depends on the FieldType.  LatLonType defaults to using haversine, PointType defaults to Euclidean (2-norm).
+ *
+ * <p/>
+ * Examples:
+ * <pre>fq={!geofilt sfield=store pt=10.312,-20.556 d=3.5}</pre>
+ * <pre>fq={!geofilt sfield=store}&pt=10.312,-20&d=3.5</pre>
+ * <pre>fq={!geofilt}&sfield=store&pt=10.312,-20&d=3.5</pre>
+ * <p/>
+ * Note: The geofilt for LatLonType is capable of also producing scores equal to the computed distance from the point
+ * to the field, making it useful as a component of the main query or a boosting query.
  */
 public class SpatialFilterQParserPlugin extends QParserPlugin {
-  public static String NAME = "sfilt";
+  public static String NAME = "geofilt";
 
   @Override
   public QParser createParser(String qstr, SolrParams localParams,

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/ValueSourceParser.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/ValueSourceParser.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/ValueSourceParser.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/ValueSourceParser.java Wed Sep 29 17:05:29 2010
@@ -38,12 +38,7 @@ import org.apache.solr.search.function.d
 import org.apache.solr.util.plugin.NamedListInitializedPlugin;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Collections;
+import java.util.*;
 
 /**
  * A factory that parses user queries to generate ValueSource instances.
@@ -236,7 +231,9 @@ public abstract class ValueSourceParser 
         return new JoinDocFreqValueSource( f0, qf );
       }
     });
-    
+
+    addParser("geodist", HaversineConstFunction.parser);
+
     addParser("hsin", new ValueSourceParser() {
       public ValueSource parse(FunctionQParser fp) throws ParseException {
 
@@ -699,7 +696,7 @@ class DateValueSourceParser extends Valu
 
 
 // Private for now - we need to revisit how to handle typing in function queries
-class LongConstValueSource extends ValueSource {
+class LongConstValueSource extends ConstNumberSource {
   final long constant;
   final double dv;
   final float fv;
@@ -751,61 +748,30 @@ class LongConstValueSource extends Value
     LongConstValueSource other = (LongConstValueSource) o;
     return this.constant == other.constant;
   }
-}
 
-// Private for now - we need to revisit how to handle typing in function queries
-class DoubleConstValueSource extends ValueSource {
-  final double constant;
-  private final float fv;
-  private final long lv;
-
-  public DoubleConstValueSource(double constant) {
-    this.constant = constant;
-    this.fv = (float)constant;
-    this.lv = (long)constant;
+  @Override
+  public int getInt() {
+    return (int)constant;
   }
 
-  public String description() {
-    return "const(" + constant + ")";
+  @Override
+  public long getLong() {
+    return constant;
   }
 
-  public DocValues getValues(Map context, IndexReader reader) throws IOException {
-    return new DocValues() {
-      public float floatVal(int doc) {
-        return fv;
-      }
-
-      public int intVal(int doc) {
-        return (int) lv;
-      }
-
-      public long longVal(int doc) {
-        return lv;
-      }
-
-      public double doubleVal(int doc) {
-        return constant;
-      }
-
-      public String strVal(int doc) {
-        return Double.toString(constant);
-      }
-
-      public String toString(int doc) {
-        return description();
-      }
-    };
+  @Override
+  public float getFloat() {
+    return fv;
   }
 
-  public int hashCode() {
-    long bits = Double.doubleToRawLongBits(constant);
-    return (int)(bits ^ (bits >>> 32));
+  @Override
+  public double getDouble() {
+    return dv;
   }
 
-  public boolean equals(Object o) {
-    if (DoubleConstValueSource.class != o.getClass()) return false;
-    DoubleConstValueSource other = (DoubleConstValueSource) o;
-    return this.constant == other.constant;
+  @Override
+  public Number getNumber() {
+    return constant;
   }
 }
 

Added: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java?rev=1002739&view=auto
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java (added)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java Wed Sep 29 17:05:29 2010
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+
+package org.apache.solr.search.function;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <code>ConstNumberSource</code> is the base class for all constant numbers
+ */
+public abstract class ConstNumberSource extends ValueSource {
+  public abstract int getInt();
+  public abstract long getLong();
+  public abstract float getFloat();
+  public abstract double getDouble();  
+  public abstract Number getNumber();  
+}

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstNumberSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstValueSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstValueSource.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstValueSource.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/ConstValueSource.java Wed Sep 29 17:05:29 2010
@@ -25,7 +25,7 @@ import java.util.Map;
 /**
  * <code>ConstValueSource</code> returns a constant for all documents
  */
-public class ConstValueSource extends ValueSource {
+public class ConstValueSource extends ConstNumberSource {
   final float constant;
   private final double dv;
 
@@ -66,8 +66,33 @@ public class ConstValueSource extends Va
   }
 
   public boolean equals(Object o) {
-    if (ConstValueSource.class != o.getClass()) return false;
+    if (!(o instanceof ConstValueSource)) return false;
     ConstValueSource other = (ConstValueSource)o;
     return  this.constant == other.constant;
   }
+
+  @Override
+  public int getInt() {
+    return (int)constant;
+  }
+
+  @Override
+  public long getLong() {
+    return (long)constant;
+  }
+
+  @Override
+  public float getFloat() {
+    return constant;
+  }
+
+  @Override
+  public double getDouble() {
+    return dv;
+  }
+
+  @Override
+  public Number getNumber() {
+    return constant;
+  }
 }

Added: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java?rev=1002739&view=auto
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java (added)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java Wed Sep 29 17:05:29 2010
@@ -0,0 +1,103 @@
+/**
+ * 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.
+ */
+
+package org.apache.solr.search.function;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class DoubleConstValueSource extends ConstNumberSource {
+  final double constant;
+  private final float fv;
+  private final long lv;
+
+  public DoubleConstValueSource(double constant) {
+    this.constant = constant;
+    this.fv = (float)constant;
+    this.lv = (long)constant;
+  }
+
+  public String description() {
+    return "const(" + constant + ")";
+  }
+
+  public DocValues getValues(Map context, IndexReader reader) throws IOException {
+    return new DocValues() {
+      public float floatVal(int doc) {
+        return fv;
+      }
+
+      public int intVal(int doc) {
+        return (int) lv;
+      }
+
+      public long longVal(int doc) {
+        return lv;
+      }
+
+      public double doubleVal(int doc) {
+        return constant;
+      }
+
+      public String strVal(int doc) {
+        return Double.toString(constant);
+      }
+
+      public String toString(int doc) {
+        return description();
+      }
+    };
+  }
+
+  public int hashCode() {
+    long bits = Double.doubleToRawLongBits(constant);
+    return (int)(bits ^ (bits >>> 32));
+  }
+
+  public boolean equals(Object o) {
+    if (!(o instanceof DoubleConstValueSource)) return false;
+    DoubleConstValueSource other = (DoubleConstValueSource) o;
+    return this.constant == other.constant;
+  }
+
+  @Override
+  public int getInt() {
+    return (int)lv;
+  }
+
+  @Override
+  public long getLong() {
+    return lv;
+  }
+
+  @Override
+  public float getFloat() {
+    return fv;
+  }
+
+  @Override
+  public double getDouble() {
+    return constant;
+  }
+
+  @Override
+  public Number getNumber() {
+    return constant;
+  }
+}

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/DoubleConstValueSource.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/VectorValueSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/VectorValueSource.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/VectorValueSource.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/VectorValueSource.java Wed Sep 29 17:05:29 2010
@@ -33,7 +33,7 @@ import java.util.Map;
  */
 //Not crazy about the name, but...
 public class VectorValueSource extends MultiValueSource {
-  protected List<ValueSource> sources;
+  protected final List<ValueSource> sources;
 
 
   public VectorValueSource(List<ValueSource> sources) {

Added: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java?rev=1002739&view=auto
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java (added)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java Wed Sep 29 17:05:29 2010
@@ -0,0 +1,266 @@
+package org.apache.solr.search.function.distance;
+/**
+ * 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.index.IndexReader;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.spatial.DistanceUtils;
+import org.apache.lucene.spatial.tier.InvalidGeoException;
+import org.apache.solr.common.params.SpatialParams;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.FunctionQParser;
+import org.apache.solr.search.ValueSourceParser;
+import org.apache.solr.search.function.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Haversine function with one point constant
+ */
+public class HaversineConstFunction extends ValueSource {
+
+  public static ValueSourceParser parser = new ValueSourceParser() {
+    public ValueSource parse(FunctionQParser fp) throws ParseException
+    {
+      // TODO: dispatch through SpatialQueriable in the future?
+      List<ValueSource> sources = fp.parseValueSourceList();
+
+      // "m" is a multi-value source, "x" is a single-value source
+      // allow (m,m) (m,x,x) (x,x,m) (x,x,x,x)
+      // if not enough points are present, "pt" will be checked first, followed by "sfield".      
+
+      MultiValueSource mv1 = null;
+      MultiValueSource mv2 = null;
+
+      if (sources.size() == 0) {
+        // nothing to do now
+      } else if (sources.size() == 1) {
+        ValueSource vs = sources.get(0);
+        if (!(vs instanceof MultiValueSource)) {
+          throw new ParseException("geodist - invalid parameters:" + sources);
+        }
+        mv1 = (MultiValueSource)vs;
+      } else if (sources.size() == 2) {
+        ValueSource vs1 = sources.get(0);
+        ValueSource vs2 = sources.get(1);
+
+        if (vs1 instanceof MultiValueSource && vs2 instanceof MultiValueSource) {
+          mv1 = (MultiValueSource)vs1;
+          mv2 = (MultiValueSource)vs2;
+        } else {
+          mv1 = makeMV(sources, sources);
+        }
+      } else if (sources.size()==3) {
+        ValueSource vs1 = sources.get(0);
+        ValueSource vs2 = sources.get(1);
+        if (vs1 instanceof MultiValueSource) {     // (m,x,x)
+          mv1 = (MultiValueSource)vs1;
+          mv2 = makeMV(sources.subList(1,3), sources);
+        } else {                                   // (x,x,m)
+          mv1 = makeMV(sources.subList(0,2), sources);
+          vs1 = sources.get(2);
+          if (!(vs1 instanceof MultiValueSource)) {
+            throw new ParseException("geodist - invalid parameters:" + sources);
+          }
+          mv2 = (MultiValueSource)vs1;
+        }
+      } else if (sources.size()==4) {
+        mv1 = makeMV(sources.subList(0,2), sources);
+        mv2 = makeMV(sources.subList(2,4), sources);
+      } else if (sources.size() > 4) {
+        throw new ParseException("geodist - invalid parameters:" + sources);
+      }
+
+      if (mv1 == null) {
+        mv1 = parsePoint(fp);
+        mv2 = parseSfield(fp);
+      } else if (mv2 == null) {
+        mv2 = parsePoint(fp);
+        if (mv2 == null)
+          mv2 = parseSfield(fp);
+      }
+
+      if (mv1 == null || mv2 == null) {
+        throw new ParseException("geodist - not enough parameters:" + sources);
+      }
+
+      // We have all the parameters at this point, now check if one of the points is constant
+      double[] constants;
+      constants = getConstants(mv1);
+      MultiValueSource other = mv2;
+      if (constants == null) {
+        constants = getConstants(mv2);
+        other = mv1;
+      }
+
+      if (constants != null && other instanceof VectorValueSource) {
+        return new HaversineConstFunction(constants[0], constants[1], (VectorValueSource)other);
+      }      
+
+      return new HaversineFunction(mv1, mv2, DistanceUtils.EARTH_MEAN_RADIUS_KM, true);
+    }
+  };
+
+  /** make a MultiValueSource from two non MultiValueSources */
+  private static VectorValueSource makeMV(List<ValueSource> sources, List<ValueSource> orig) throws ParseException {
+    ValueSource vs1 = sources.get(0);
+    ValueSource vs2 = sources.get(1);
+
+    if (vs1 instanceof MultiValueSource || vs2 instanceof MultiValueSource) {
+      throw new ParseException("geodist - invalid parameters:" + orig);
+    }
+    return  new VectorValueSource(sources);
+  }
+
+  private static MultiValueSource parsePoint(FunctionQParser fp) throws ParseException {
+    String pt = fp.getParam(SpatialParams.POINT);
+    if (pt == null) return null;
+    double[] point = null;
+    try {
+      point = DistanceUtils.parseLatitudeLongitude(pt);
+    } catch (InvalidGeoException e) {
+      throw new ParseException("Bad spatial pt:" + pt);
+    }
+    return new VectorValueSource(Arrays.asList(new ValueSource[] {new DoubleConstValueSource(point[0]),new DoubleConstValueSource(point[1])}));
+  }
+
+  private static double[] getConstants(MultiValueSource vs) {
+    if (!(vs instanceof VectorValueSource)) return null;
+    List<ValueSource> sources = ((VectorValueSource)vs).getSources();
+    if (sources.get(0) instanceof ConstNumberSource && sources.get(1) instanceof ConstNumberSource) {
+      return new double[] { ((ConstNumberSource) sources.get(0)).getDouble(), ((ConstNumberSource) sources.get(1)).getDouble()};
+    }
+    return null;
+  }
+
+  private static MultiValueSource parseSfield(FunctionQParser fp) throws ParseException {
+    String sfield = fp.getParam(SpatialParams.FIELD);
+    if (sfield == null) return null;
+    SchemaField sf = fp.getReq().getSchema().getField(sfield);
+    ValueSource vs = sf.getType().getValueSource(sf, fp);
+    if (!(vs instanceof MultiValueSource)) {
+      throw new ParseException("Spatial field must implement MultiValueSource:" + sf);
+    }
+    return (MultiValueSource)vs;
+  }
+
+
+  //////////////////////////////////////////////////////////////////////////////////////
+
+  private final double latCenter;
+  private final double lonCenter;
+  private final VectorValueSource p2;  // lat+lon, just saved for display/debugging
+  private final ValueSource latSource;
+  private final ValueSource lonSource;
+
+  private final double latCenterRad_cos; // cos(latCenter)
+  private static final double EARTH_MEAN_DIAMETER = DistanceUtils.EARTH_MEAN_RADIUS_KM * 2;
+
+
+  public HaversineConstFunction(double latCenter, double lonCenter, VectorValueSource vs) {
+    this.latCenter = latCenter;
+    this.lonCenter = lonCenter;
+    this.p2 = vs;
+    this.latSource = p2.getSources().get(0);
+    this.lonSource = p2.getSources().get(1);
+    this.latCenterRad_cos = Math.cos(latCenter * DistanceUtils.DEGREES_TO_RADIANS);
+  }
+
+  protected String name() {
+    return "geodist";
+  }
+
+  @Override
+  public DocValues getValues(Map context, IndexReader reader) throws IOException {
+    final DocValues latVals = latSource.getValues(context, reader);
+    final DocValues lonVals = lonSource.getValues(context, reader);
+    final double latCenterRad = this.latCenter * DistanceUtils.DEGREES_TO_RADIANS;
+    final double lonCenterRad = this.lonCenter * DistanceUtils.DEGREES_TO_RADIANS;
+    final double latCenterRad_cos = this.latCenterRad_cos;
+
+    return new DocValues() {
+      public float floatVal(int doc) {
+        return (float) doubleVal(doc);
+      }
+
+      public int intVal(int doc) {
+        return (int) doubleVal(doc);
+      }
+
+      public long longVal(int doc) {
+        return (long) doubleVal(doc);
+      }
+
+      public double doubleVal(int doc) {
+        double latRad = latVals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
+        double lonRad = lonVals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
+        double diffX = latCenterRad - latRad;
+        double diffY = lonCenterRad - lonRad;
+        double hsinX = Math.sin(diffX * 0.5);
+        double hsinY = Math.sin(diffY * 0.5);
+        double h = hsinX * hsinX +
+                (latCenterRad_cos * Math.cos(latRad) * hsinY * hsinY);
+        return (EARTH_MEAN_DIAMETER * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)));
+      }
+
+      public String strVal(int doc) {
+        return Double.toString(doubleVal(doc));
+      }
+
+      @Override
+      public String toString(int doc) {
+        return name() + '(' + latVals.toString(doc) + ',' + lonVals.toString(doc) + ',' + latCenter + ',' + lonCenter + ')';
+      }
+    };
+  }
+
+  @Override
+  public void createWeight(Map context, Searcher searcher) throws IOException {
+    latSource.createWeight(context, searcher);
+    lonSource.createWeight(context, searcher);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof HaversineConstFunction)) return false;
+    HaversineConstFunction other = (HaversineConstFunction) o;
+    return this.latCenter == other.latCenter
+        && this.lonCenter == other.lonCenter
+        && this.p2.equals(other.p2);
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = p2.hashCode();
+    long temp;
+    temp = Double.doubleToRawLongBits(latCenter);
+    result = 31 * result + (int) (temp ^ (temp >>> 32));
+    temp = Double.doubleToRawLongBits(lonCenter);
+    result = 31 * result + (int) (temp ^ (temp >>> 32));
+    return result;
+  }
+
+  public String description() {
+    return name() + '(' + p2 + ',' + latCenter + ',' + lonCenter + ')';
+  }
+}

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: lucene/dev/trunk/solr/src/test/org/apache/solr/search/SpatialFilterTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/test/org/apache/solr/search/SpatialFilterTest.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/test/org/apache/solr/search/SpatialFilterTest.java (original)
+++ lucene/dev/trunk/solr/src/test/org/apache/solr/search/SpatialFilterTest.java Wed Sep 29 17:05:29 2010
@@ -134,9 +134,9 @@ public class SpatialFilterTest extends S
       }
     }
 
-    String method = exact ? "sfilt" : "bbox";
+    String method = exact ? "geofilt" : "bbox";
 
-    assertQ(req("fl", "id", "q","*:*", "rows", "1000", "fq", "{!"+method+" fl=" +fieldName +"}",
+    assertQ(req("fl", "id", "q","*:*", "rows", "1000", "fq", "{!"+method+" sfield=" +fieldName +"}",
               "pt", pt, "d", String.valueOf(distance)),
               tests);
   }

Modified: lucene/dev/trunk/solr/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java (original)
+++ lucene/dev/trunk/solr/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java Wed Sep 29 17:05:29 2010
@@ -32,7 +32,7 @@ import static org.junit.Assert.*;
 public class DistanceFunctionTest extends SolrTestCaseJ4 {
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-functionquery.xml", "schema11.xml");
+    initCore("solrconfig.xml", "schema12.xml");
   }
 
   @Test
@@ -63,8 +63,8 @@ public class DistanceFunctionTest extend
 
     assertQ(req("fl", "id,point_hash,score", "q", "{!func}recip(ghhsin(" + DistanceUtils.EARTH_MEAN_RADIUS_KM + ", point_hash, \"" + GeoHashUtils.encode(32, -79) + "\"), 1, 1, 0)"),
             "//*[@numFound='7']", 
-            "//result/doc[1]/float[@name='id'][.='6.0']",
-            "//result/doc[2]/float[@name='id'][.='7.0']"//all the rest don't matter
+            "//result/doc[1]/str[@name='id'][.='6']",
+            "//result/doc[2]/str[@name='id'][.='7']"//all the rest don't matter
             );
 
 
@@ -72,6 +72,49 @@ public class DistanceFunctionTest extend
 
   }
 
+
+  @Test
+  public void testLatLon() throws Exception {
+    assertU(adoc("id", "100", "store", "1,2"));
+    assertU(commit());
+   
+    assertJQ(req("defType","func", "q","geodist(1,2,3,4)","fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+    // throw in some decimal points
+    assertJQ(req("defType","func", "q","geodist(1.0,2,3,4.0)","fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+    // default to reading pt
+    assertJQ(req("defType","func", "q","geodist(1,2)","pt","3,4", "fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+    // default to reading pt first
+    assertJQ(req("defType","func", "q","geodist(1,2)","pt","3,4", "sfield","store", "fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+    // if pt missing, use sfield
+    assertJQ(req("defType","func", "q","geodist(3,4)","sfield","store", "fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+    // read both pt and sfield
+    assertJQ(req("defType","func", "q","geodist()","pt","3,4","sfield","store", "fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+    // param substitution
+    assertJQ(req("defType","func", "q","geodist($a,$b)","a","3,4","b","store", "fq","id:100","fl","id,score")
+      ,"/response/docs/[0]/score==314.40338"
+    );
+
+  }
+
+  
   @Test
   public void testVector() throws Exception {
     clearIndex();

Modified: lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema11.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema11.xml?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema11.xml (original)
+++ lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema11.xml Wed Sep 29 17:05:29 2010
@@ -257,9 +257,15 @@
     <!-- Poly field -->
     <fieldType name="xy" class="solr.PointType" dimension="2" subFieldType="double"/>
     <fieldType name="xyd" class="solr.PointType" dimension="2" subFieldSuffix="*_d"/>
-
     <fieldtype name="geohash" class="solr.GeoHashField"/>
 
+    <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
+
+    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multi
+valued. -->
+    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
+
+
  </types>
 
 

Modified: lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema12.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema12.xml?rev=1002739&r1=1002738&r2=1002739&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema12.xml (original)
+++ lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema12.xml Wed Sep 29 17:05:29 2010
@@ -418,6 +418,15 @@
 
   <fieldType name="random" class="solr.RandomSortField" indexed="true" />  
   
+    <!-- Poly field -->
+    <fieldType name="xy" class="solr.PointType" dimension="2" subFieldType="double"/>
+    <fieldType name="xyd" class="solr.PointType" dimension="2" subFieldSuffix="*_d"/>
+    <fieldtype name="geohash" class="solr.GeoHashField"/>
+
+   <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
+    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
+    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
+
  </types>
 
 
@@ -508,7 +517,12 @@
    -->
    <field name="timestamp" type="date" indexed="true" stored="true"/>
 
-
+   <!-- 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"/>
+   <field name="point_hash" type="geohash" indexed="true" stored="true" multiValued="false"/>
+   <field name="store" type="location" indexed="true" stored="true"/>
+   <dynamicField name="*_coordinate"  type="tdouble" indexed="true"  stored="false"/>
 
 
    <dynamicField name="*_si"  type="sint"  indexed="true"  stored="true"/>