SOLR-1568: friendly geodist function

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1002739 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2010-09-29 17:05:29 +00:00
parent f7c4994154
commit d029a5f222
17 changed files with 569 additions and 111 deletions

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -25,8 +25,8 @@ import org.apache.solr.search.SpatialOptions;
/**
* 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 {

View File

@ -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);

View File

@ -33,24 +33,7 @@ import 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>{!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 extends QParser {
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;

View File

@ -21,11 +21,35 @@ import org.apache.solr.common.util.NamedList;
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,

View File

@ -38,12 +38,7 @@ import org.apache.solr.search.function.distance.*;
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 implements NamedListInitializedPlugin {
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 ValueSourceParser {
// 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 ValueSource {
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;
}
}

View File

@ -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();
}

View File

@ -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 ValueSource {
}
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;
}
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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 + ')';
}
}

View File

@ -134,9 +134,9 @@ public class SpatialFilterTest extends SolrTestCaseJ4 {
}
}
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);
}

View File

@ -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 extends SolrTestCaseJ4 {
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 extends SolrTestCaseJ4 {
}
@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();

View File

@ -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>

View File

@ -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"/>