mirror of https://github.com/apache/lucene.git
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:
parent
f7c4994154
commit
d029a5f222
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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=<location_field> pt=<lat,lon> d=<distance>}</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,
|
||||
|
|
|
@ -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.
|
||||
|
@ -237,6 +232,8 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
|
|||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 + ')';
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Loading…
Reference in New Issue