From 2239a52453811b01459f9959ddf05155723ad3fb Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Wed, 26 Jun 2013 04:44:26 +0000 Subject: [PATCH] SOLR-2345: refactor out GeoDistValueSourceParser from HaversineConstFunction git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1496731 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/solr/search/ValueSourceParser.java | 16 +- .../distance/GeoDistValueSourceParser.java | 165 ++++++++++++++++++ .../distance/HaversineConstFunction.java | 145 +-------------- 3 files changed, 170 insertions(+), 156 deletions(-) create mode 100644 solr/core/src/java/org/apache/solr/search/function/distance/GeoDistValueSourceParser.java diff --git a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java index b0d7841e583..e426b8b8fac 100755 --- a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java +++ b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java @@ -293,7 +293,7 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin { } }); - addParser("geodist", HaversineConstFunction.parser); + addParser("geodist", new GeoDistValueSourceParser()); addParser("hsin", new ValueSourceParser() { @Override @@ -309,18 +309,8 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin { ValueSource one = fp.parseValueSource(); ValueSource two = fp.parseValueSource(); if (fp.hasMoreArguments()) { - - - List s1 = new ArrayList(); - s1.add(one); - s1.add(two); - pv1 = new VectorValueSource(s1); - ValueSource x2 = fp.parseValueSource(); - ValueSource y2 = fp.parseValueSource(); - List s2 = new ArrayList(); - s2.add(x2); - s2.add(y2); - pv2 = new VectorValueSource(s2); + pv1 = new VectorValueSource(Arrays.asList(one, two));//x1, y1 + pv2 = new VectorValueSource(Arrays.asList(fp.parseValueSource(), fp.parseValueSource()));//x2, y2 } else { //check to see if we have multiValue source if (one instanceof MultiValueSource && two instanceof MultiValueSource){ diff --git a/solr/core/src/java/org/apache/solr/search/function/distance/GeoDistValueSourceParser.java b/solr/core/src/java/org/apache/solr/search/function/distance/GeoDistValueSourceParser.java new file mode 100644 index 00000000000..789980a71f1 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/function/distance/GeoDistValueSourceParser.java @@ -0,0 +1,165 @@ +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 com.spatial4j.core.distance.DistanceUtils; +import com.spatial4j.core.exception.InvalidShapeException; +import com.spatial4j.core.io.ParseUtils; +import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.queries.function.valuesource.ConstNumberSource; +import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource; +import org.apache.lucene.queries.function.valuesource.MultiValueSource; +import org.apache.lucene.queries.function.valuesource.VectorValueSource; +import org.apache.solr.common.params.SpatialParams; +import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.FunctionQParser; +import org.apache.solr.search.SyntaxError; +import org.apache.solr.search.ValueSourceParser; + +import java.util.Arrays; +import java.util.List; + +/** + * Parses "geodist" creating {@link HaversineConstFunction} or {@link HaversineFunction}. + */ +public class GeoDistValueSourceParser extends ValueSourceParser { + + @Override + public ValueSource parse(FunctionQParser fp) throws SyntaxError { + // TODO: dispatch through SpatialQueryable in the future? + List 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 SyntaxError("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 SyntaxError("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 SyntaxError("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 SyntaxError("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 VectorValueSource makeMV(List sources, List orig) throws SyntaxError { + ValueSource vs1 = sources.get(0); + ValueSource vs2 = sources.get(1); + + if (vs1 instanceof MultiValueSource || vs2 instanceof MultiValueSource) { + throw new SyntaxError("geodist - invalid parameters:" + orig); + } + return new VectorValueSource(sources); + } + + private MultiValueSource parsePoint(FunctionQParser fp) throws SyntaxError { + String pt = fp.getParam(SpatialParams.POINT); + if (pt == null) return null; + double[] point = null; + try { + point = ParseUtils.parseLatitudeLongitude(pt); + } catch (InvalidShapeException e) { + throw new SyntaxError("Bad spatial pt:" + pt); + } + return new VectorValueSource(Arrays.asList(new DoubleConstValueSource(point[0]), new DoubleConstValueSource(point[1]))); + } + + private double[] getConstants(MultiValueSource vs) { + if (!(vs instanceof VectorValueSource)) return null; + List 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 MultiValueSource parseSfield(FunctionQParser fp) throws SyntaxError { + 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 SyntaxError("Spatial field must implement MultiValueSource:" + sf); + } + return (MultiValueSource)vs; + } + +} diff --git a/solr/core/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java b/solr/core/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java index b9efc028ede..335272088e5 100755 --- a/solr/core/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java +++ b/solr/core/src/java/org/apache/solr/search/function/distance/HaversineConstFunction.java @@ -16,27 +16,15 @@ package org.apache.solr.search.function.distance; * limitations under the License. */ +import com.spatial4j.core.distance.DistanceUtils; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.queries.function.docvalues.DoubleDocValues; -import org.apache.lucene.queries.function.valuesource.ConstNumberSource; -import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource; -import org.apache.lucene.queries.function.valuesource.MultiValueSource; import org.apache.lucene.queries.function.valuesource.VectorValueSource; import org.apache.lucene.search.IndexSearcher; -import com.spatial4j.core.io.ParseUtils; -import com.spatial4j.core.distance.DistanceUtils; -import com.spatial4j.core.exception.InvalidShapeException; -import org.apache.solr.common.params.SpatialParams; -import org.apache.solr.schema.SchemaField; -import org.apache.solr.search.FunctionQParser; -import org.apache.solr.search.SyntaxError; -import org.apache.solr.search.ValueSourceParser; import java.io.IOException; -import java.util.Arrays; -import java.util.List; import java.util.Map; import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS; @@ -46,135 +34,6 @@ import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS; */ public class HaversineConstFunction extends ValueSource { - public static ValueSourceParser parser = new ValueSourceParser() { - @Override - public ValueSource parse(FunctionQParser fp) throws SyntaxError - { - // TODO: dispatch through SpatialQueryable in the future? - List 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 SyntaxError("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 SyntaxError("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 SyntaxError("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 SyntaxError("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 sources, List orig) throws SyntaxError { - ValueSource vs1 = sources.get(0); - ValueSource vs2 = sources.get(1); - - if (vs1 instanceof MultiValueSource || vs2 instanceof MultiValueSource) { - throw new SyntaxError("geodist - invalid parameters:" + orig); - } - return new VectorValueSource(sources); - } - - private static MultiValueSource parsePoint(FunctionQParser fp) throws SyntaxError { - String pt = fp.getParam(SpatialParams.POINT); - if (pt == null) return null; - double[] point = null; - try { - point = ParseUtils.parseLatitudeLongitude(pt); - } catch (InvalidShapeException e) { - throw new SyntaxError("Bad spatial pt:" + pt); - } - return new VectorValueSource(Arrays.asList(new DoubleConstValueSource(point[0]),new DoubleConstValueSource(point[1]))); - } - - private static double[] getConstants(MultiValueSource vs) { - if (!(vs instanceof VectorValueSource)) return null; - List 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 SyntaxError { - 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 SyntaxError("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 @@ -184,7 +43,6 @@ public class HaversineConstFunction extends ValueSource { 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; @@ -257,4 +115,5 @@ public class HaversineConstFunction extends ValueSource { public String description() { return name() + '(' + p2 + ',' + latCenter + ',' + lonCenter + ')'; } + }