mirror of https://github.com/apache/lucene.git
LUCENE-4208 makeQuery return ConstantScoreQuery, standardize makeDistanceValueSource behavior
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1385074 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ba5b6a4a71
commit
577e25c7ff
|
@ -18,13 +18,14 @@ package org.apache.lucene.spatial;
|
|||
*/
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.queries.function.FunctionQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.queries.function.valuesource.ReciprocalFloatFunction;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.FilteredQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
|
||||
/**
|
||||
|
@ -99,26 +100,50 @@ public abstract class SpatialStrategy {
|
|||
public abstract Field[] createIndexableFields(Shape shape);
|
||||
|
||||
/**
|
||||
* The value source yields a number that is proportional to the distance between the query shape and indexed data.
|
||||
* Make a ValueSource returning the distance between the center of the
|
||||
* indexed shape and {@code queryPoint}. If there are multiple indexed shapes
|
||||
* then the closest one is chosen.
|
||||
*/
|
||||
public abstract ValueSource makeValueSource(SpatialArgs args);
|
||||
public abstract ValueSource makeDistanceValueSource(Point queryPoint);
|
||||
|
||||
/**
|
||||
* Make a query which has a score based on the distance from the data to the query shape.
|
||||
* The default implementation constructs a {@link FilteredQuery} based on
|
||||
* {@link #makeFilter(org.apache.lucene.spatial.query.SpatialArgs)} and
|
||||
* {@link #makeValueSource(org.apache.lucene.spatial.query.SpatialArgs)}.
|
||||
* Make a (ConstantScore) Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
|
||||
* and {@link Shape} from the supplied {@code args}.
|
||||
* The default implementation is
|
||||
* <pre>return new ConstantScoreQuery(makeFilter(args));</pre>
|
||||
*/
|
||||
public Query makeQuery(SpatialArgs args) {
|
||||
Filter filter = makeFilter(args);
|
||||
ValueSource vs = makeValueSource(args);
|
||||
return new FilteredQuery(new FunctionQuery(vs), filter);
|
||||
public ConstantScoreQuery makeQuery(SpatialArgs args) {
|
||||
return new ConstantScoreQuery(makeFilter(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a Filter
|
||||
* Make a Filter based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
|
||||
* and {@link Shape} from the supplied {@code args}.
|
||||
* <p />
|
||||
* If a subclasses implements
|
||||
* {@link #makeQuery(org.apache.lucene.spatial.query.SpatialArgs)}
|
||||
* then this method could be simply:
|
||||
* <pre>return new QueryWrapperFilter(makeQuery(args).getQuery());</pre>
|
||||
*/
|
||||
public abstract Filter makeFilter(SpatialArgs args);
|
||||
|
||||
/**
|
||||
* Returns a ValueSource with values ranging from 1 to 0, depending inversely
|
||||
* on the distance from {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point)}.
|
||||
* The formula is <code>c/(d + c)</code> where 'd' is the distance and 'c' is
|
||||
* one tenth the distance to the farthest edge from the center. Thus the
|
||||
* scores will be 1 for indexed points at the center of the query shape and as
|
||||
* low as ~0.1 at its furthest edges.
|
||||
*/
|
||||
public final ValueSource makeRecipDistanceValueSource(Shape queryShape) {
|
||||
Rectangle bbox = queryShape.getBoundingBox();
|
||||
double diagonalDist = ctx.getDistCalc().distance(
|
||||
ctx.makePoint(bbox.getMinX(), bbox.getMinY()), bbox.getMaxX(), bbox.getMaxY());
|
||||
double distToEdge = diagonalDist * 0.5;
|
||||
float c = (float)distToEdge * 0.1f;//one tenth
|
||||
return new ReciprocalFloatFunction(makeDistanceValueSource(queryShape.getCenter()), 1f, c, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+" field:"+fieldName+" ctx="+ctx;
|
||||
|
|
|
@ -84,8 +84,9 @@ public class BBoxSimilarityValueSource extends ValueSource {
|
|||
minX[doc], maxX[doc],
|
||||
minY[doc], maxY[doc]);
|
||||
return (float) similarity.score(rect, null);
|
||||
} else {
|
||||
return (float) similarity.score(null, null);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Explanation explain(int doc) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.apache.lucene.spatial.bbox;
|
|||
*/
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.DoubleField;
|
||||
|
@ -114,29 +115,33 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
//---------------------------------
|
||||
|
||||
@Override
|
||||
public ValueSource makeValueSource(SpatialArgs args) {
|
||||
Shape shape = args.getShape();
|
||||
if (!(shape instanceof Rectangle))
|
||||
throw new IllegalArgumentException("Can only get valueSource by Rectangle, not " + shape);
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint) {
|
||||
return new BBoxSimilarityValueSource(
|
||||
this, new AreaSimilarity((Rectangle)shape, queryPower, targetPower));
|
||||
this, new DistanceSimilarity(this.getSpatialContext(), queryPoint));
|
||||
}
|
||||
|
||||
public ValueSource makeBBoxAreaSimilarityValueSource(Rectangle queryBox) {
|
||||
return new BBoxSimilarityValueSource(
|
||||
this, new AreaSimilarity(queryBox, queryPower, targetPower));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter makeFilter(SpatialArgs args) {
|
||||
Query spatial = makeSpatialQuery(args);
|
||||
return new QueryWrapperFilter( spatial );
|
||||
return new QueryWrapperFilter(makeSpatialQuery(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query makeQuery(SpatialArgs args) {
|
||||
public ConstantScoreQuery makeQuery(SpatialArgs args) {
|
||||
return new ConstantScoreQuery(makeSpatialQuery(args));
|
||||
}
|
||||
|
||||
public Query makeQueryWithValueSource(SpatialArgs args, ValueSource valueSource) {
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
Query spatial = makeSpatialQuery(args);
|
||||
bq.add(new ConstantScoreQuery(spatial), BooleanClause.Occur.MUST);
|
||||
|
||||
|
||||
// This part does the scoring
|
||||
Query spatialRankingQuery = new FunctionQuery(makeValueSource(args));
|
||||
Query spatialRankingQuery = new FunctionQuery(valueSource);
|
||||
bq.add(spatialRankingQuery, BooleanClause.Occur.MUST);
|
||||
return bq;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package org.apache.lucene.spatial.bbox;
|
||||
|
||||
/*
|
||||
* 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.context.SpatialContext;
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
|
||||
/**
|
||||
* Returns the distance between the center of the indexed rectangle and the
|
||||
* query shape.
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class DistanceSimilarity implements BBoxSimilarity {
|
||||
private final Point queryPoint;
|
||||
private final DistanceCalculator distCalc;
|
||||
private final double nullValue;
|
||||
|
||||
public DistanceSimilarity(SpatialContext ctx, Point queryPoint) {
|
||||
this.queryPoint = queryPoint;
|
||||
this.distCalc = ctx.getDistCalc();
|
||||
this.nullValue = (ctx.isGeo() ? 180 : Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double score(Rectangle indexRect, Explanation exp) {
|
||||
double score;
|
||||
if (indexRect == null) {
|
||||
score = nullValue;
|
||||
} else {
|
||||
score = distCalc.distance(queryPoint, indexRect.getCenter());
|
||||
}
|
||||
if (exp != null) {
|
||||
exp.setValue((float)score);
|
||||
exp.setDescription(this.getClass().getSimpleName());
|
||||
exp.addDetail(new Explanation(-1f,""+queryPoint));
|
||||
exp.addDetail(new Explanation(-1f,""+indexRect));
|
||||
}
|
||||
return score;
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ package org.apache.lucene.spatial.prefix;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
@ -141,12 +140,7 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeValueSource(SpatialArgs args) {
|
||||
DistanceCalculator calc = grid.getSpatialContext().getDistCalc();
|
||||
return makeValueSource(args, calc);
|
||||
}
|
||||
|
||||
public ValueSource makeValueSource(SpatialArgs args, DistanceCalculator calc) {
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint) {
|
||||
PointPrefixTreeFieldCacheProvider p = provider.get( getFieldName() );
|
||||
if( p == null ) {
|
||||
synchronized (this) {//double checked locking idiom is okay since provider is threadsafe
|
||||
|
@ -157,8 +151,8 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
|
|||
}
|
||||
}
|
||||
}
|
||||
Point point = args.getShape().getCenter();
|
||||
return new ShapeFieldCacheDistanceValueSource(point, calc, p);
|
||||
|
||||
return new ShapeFieldCacheDistanceValueSource(ctx, p, queryPoint);
|
||||
}
|
||||
|
||||
public SpatialPrefixTree getGrid() {
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.apache.lucene.spatial.util;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
|
@ -38,26 +39,29 @@ import java.util.Map;
|
|||
public class ShapeFieldCacheDistanceValueSource extends ValueSource {
|
||||
|
||||
private final ShapeFieldCacheProvider<Point> provider;
|
||||
private final DistanceCalculator calculator;
|
||||
private final SpatialContext ctx;
|
||||
private final Point from;
|
||||
|
||||
public ShapeFieldCacheDistanceValueSource(Point from, DistanceCalculator calc, ShapeFieldCacheProvider<Point> provider) {
|
||||
public ShapeFieldCacheDistanceValueSource(SpatialContext ctx, ShapeFieldCacheProvider<Point> provider, Point from) {
|
||||
this.ctx = ctx;
|
||||
this.from = from;
|
||||
this.provider = provider;
|
||||
this.calculator = calc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return getClass().getSimpleName()+"("+calculator+")";
|
||||
return getClass().getSimpleName()+"("+provider+", "+from+")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
|
||||
final ShapeFieldCache<Point> cache =
|
||||
provider.getCache(readerContext.reader());
|
||||
|
||||
public FunctionValues getValues(Map context, final AtomicReaderContext readerContext) throws IOException {
|
||||
return new FunctionValues() {
|
||||
private final ShapeFieldCache<Point> cache =
|
||||
provider.getCache(readerContext.reader());
|
||||
private final Point from = ShapeFieldCacheDistanceValueSource.this.from;
|
||||
private final DistanceCalculator calculator = ctx.getDistCalc();
|
||||
private final double nullValue = (ctx.isGeo() ? 180 : Double.MAX_VALUE);
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) {
|
||||
return (float) doubleVal(doc);
|
||||
|
@ -73,7 +77,7 @@ public class ShapeFieldCacheDistanceValueSource extends ValueSource {
|
|||
}
|
||||
return v;
|
||||
}
|
||||
return Double.NaN; // ?? maybe max?
|
||||
return nullValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,16 +94,15 @@ public class ShapeFieldCacheDistanceValueSource extends ValueSource {
|
|||
|
||||
ShapeFieldCacheDistanceValueSource that = (ShapeFieldCacheDistanceValueSource) o;
|
||||
|
||||
if (calculator != null ? !calculator.equals(that.calculator) : that.calculator != null) return false;
|
||||
if (from != null ? !from.equals(that.from) : that.from != null) return false;
|
||||
if (!ctx.equals(that.ctx)) return false;
|
||||
if (!from.equals(that.from)) return false;
|
||||
if (!provider.equals(that.provider)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = calculator != null ? calculator.hashCode() : 0;
|
||||
result = 31 * result + (from != null ? from.hashCode() : 0);
|
||||
return result;
|
||||
return from.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.io.IOException;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of the Lucene ValueSource model to support spatial relevance ranking.
|
||||
* An implementation of the Lucene ValueSource model that returns the distance.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
|
@ -38,15 +38,13 @@ public class DistanceValueSource extends ValueSource {
|
|||
|
||||
private TwoDoublesStrategy strategy;
|
||||
private final Point from;
|
||||
private final DistanceCalculator calculator;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public DistanceValueSource(TwoDoublesStrategy strategy, Point from, DistanceCalculator calc) {
|
||||
public DistanceValueSource(TwoDoublesStrategy strategy, Point from) {
|
||||
this.strategy = strategy;
|
||||
this.from = from;
|
||||
this.calculator = calc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,10 +52,9 @@ public class DistanceValueSource extends ValueSource {
|
|||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
return "DistanceValueSource("+calculator+")";
|
||||
return "DistanceValueSource("+strategy+", "+from+")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the FunctionValues used by the function query.
|
||||
*/
|
||||
|
@ -71,6 +68,11 @@ public class DistanceValueSource extends ValueSource {
|
|||
final Bits validY = FieldCache.DEFAULT.getDocsWithField(reader, strategy.getFieldNameY());
|
||||
|
||||
return new FunctionValues() {
|
||||
|
||||
private final Point from = DistanceValueSource.this.from;
|
||||
private final DistanceCalculator calculator = strategy.getSpatialContext().getDistCalc();
|
||||
private final double nullValue = (strategy.getSpatialContext().isGeo() ? 180 : Double.MAX_VALUE);
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) {
|
||||
return (float) doubleVal(doc);
|
||||
|
@ -79,10 +81,11 @@ public class DistanceValueSource extends ValueSource {
|
|||
@Override
|
||||
public double doubleVal(int doc) {
|
||||
// make sure it has minX and area
|
||||
if (validX.get(doc) && validY.get(doc)) {
|
||||
if (validX.get(doc)) {
|
||||
assert validY.get(doc);
|
||||
return calculator.distance(from, ptX[doc], ptY[doc]);
|
||||
}
|
||||
return 0;
|
||||
return nullValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,11 +95,6 @@ public class DistanceValueSource extends ValueSource {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this ValueSource is equal to another.
|
||||
* @param o the ValueSource to compare
|
||||
* @return <code>true</code> if the two objects are based upon the same query envelope
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -104,18 +102,14 @@ public class DistanceValueSource extends ValueSource {
|
|||
|
||||
DistanceValueSource that = (DistanceValueSource) o;
|
||||
|
||||
if (calculator != null ? !calculator.equals(that.calculator) : that.calculator != null) return false;
|
||||
if (strategy != null ? !strategy.equals(that.strategy) : that.strategy != null) return false;
|
||||
if (from != null ? !from.equals(that.from) : that.from != null) return false;
|
||||
if (!from.equals(that.from)) return false;
|
||||
if (!strategy.equals(that.strategy)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = strategy != null ? strategy.hashCode() : 0;
|
||||
result = 31 * result + (calculator != null ? calculator.hashCode() : 0);
|
||||
result = 31 * result + (from != null ? from.hashCode() : 0);
|
||||
return result;
|
||||
return from.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.lucene.queries.function.FunctionQuery;
|
|||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.FilteredQuery;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
|
@ -94,32 +95,32 @@ public class TwoDoublesStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeValueSource(SpatialArgs args) {
|
||||
Point p = args.getShape().getCenter();
|
||||
return new DistanceValueSource(this, p, ctx.getDistCalc());
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint) {
|
||||
return new DistanceValueSource(this, queryPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter makeFilter(SpatialArgs args) {
|
||||
if( args.getShape() instanceof Circle) {
|
||||
if( SpatialOperation.is( args.getOperation(),
|
||||
SpatialOperation.Intersects,
|
||||
SpatialOperation.IsWithin )) {
|
||||
Circle circle = (Circle)args.getShape();
|
||||
Query bbox = makeWithin(circle.getBoundingBox());
|
||||
|
||||
// Make the ValueSource
|
||||
ValueSource valueSource = makeValueSource(args);
|
||||
|
||||
return new ValueSourceFilter(
|
||||
new QueryWrapperFilter( bbox ), valueSource, 0, circle.getRadius() );
|
||||
}
|
||||
}
|
||||
return new QueryWrapperFilter( makeQuery(args) );
|
||||
return new QueryWrapperFilter(makeQuery(args).getQuery());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query makeQuery(SpatialArgs args) {
|
||||
public ConstantScoreQuery makeQuery(SpatialArgs args) {
|
||||
if(! SpatialOperation.is( args.getOperation(),
|
||||
SpatialOperation.Intersects,
|
||||
SpatialOperation.IsWithin ))
|
||||
throw new UnsupportedSpatialOperation(args.getOperation());
|
||||
Shape shape = args.getShape();
|
||||
if (!(shape instanceof Rectangle))
|
||||
throw new InvalidShapeException("Only Rectangle is currently supported, got "+shape.getClass());
|
||||
Rectangle bbox = (Rectangle) shape;
|
||||
if (bbox.getCrossesDateLine()) {
|
||||
throw new UnsupportedOperationException( "Crossing dateline not yet supported" );
|
||||
}
|
||||
return new ConstantScoreQuery(makeWithin(bbox));
|
||||
}
|
||||
|
||||
public Query makeQueryDistanceScore(SpatialArgs args) {
|
||||
// For starters, just limit the bbox
|
||||
Shape shape = args.getShape();
|
||||
if (!(shape instanceof Rectangle || shape instanceof Circle)) {
|
||||
|
@ -151,7 +152,7 @@ public class TwoDoublesStrategy extends SpatialStrategy {
|
|||
Circle circle = (Circle)args.getShape();
|
||||
|
||||
// Make the ValueSource
|
||||
valueSource = makeValueSource(args);
|
||||
valueSource = makeDistanceValueSource(shape.getCenter());
|
||||
|
||||
ValueSourceFilter vsf = new ValueSourceFilter(
|
||||
new QueryWrapperFilter( spatial ), valueSource, 0, circle.getRadius() );
|
||||
|
@ -171,7 +172,7 @@ public class TwoDoublesStrategy extends SpatialStrategy {
|
|||
valueSource = new CachingDoubleValueSource(valueSource);
|
||||
}
|
||||
else {
|
||||
valueSource = makeValueSource(args);
|
||||
valueSource = makeDistanceValueSource(shape.getCenter());
|
||||
}
|
||||
Query spatialRankingQuery = new FunctionQuery(valueSource);
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
|
@ -212,19 +213,19 @@ public class TwoDoublesStrategy extends SpatialStrategy {
|
|||
*/
|
||||
Query makeDisjoint(Rectangle bbox) {
|
||||
Query qX = NumericRangeQuery.newDoubleRange(
|
||||
fieldNameX,
|
||||
precisionStep,
|
||||
bbox.getMinX(),
|
||||
bbox.getMaxX(),
|
||||
true,
|
||||
true);
|
||||
fieldNameX,
|
||||
precisionStep,
|
||||
bbox.getMinX(),
|
||||
bbox.getMaxX(),
|
||||
true,
|
||||
true);
|
||||
Query qY = NumericRangeQuery.newDoubleRange(
|
||||
fieldNameY,
|
||||
precisionStep,
|
||||
bbox.getMinY(),
|
||||
bbox.getMaxY(),
|
||||
true,
|
||||
true);
|
||||
fieldNameY,
|
||||
precisionStep,
|
||||
bbox.getMinY(),
|
||||
bbox.getMaxY(),
|
||||
true,
|
||||
true);
|
||||
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
bq.add(qX,BooleanClause.Occur.MUST_NOT);
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package org.apache.lucene.spatial;
|
||||
|
||||
/*
|
||||
* 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.carrotsearch.randomizedtesting.annotations.Name;
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.spatial.bbox.BBoxStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.spatial.vector.TwoDoublesStrategy;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author David Smiley - dsmiley@mitre.org
|
||||
*/
|
||||
public class DistanceStrategyTest extends StrategyTestCase {
|
||||
|
||||
@ParametersFactory
|
||||
public static Iterable<Object[]> parameters() {
|
||||
List<Object[]> ctorArgs = new ArrayList<Object[]>();
|
||||
|
||||
SpatialContext ctx = SpatialContext.GEO;
|
||||
SpatialPrefixTree grid;
|
||||
SpatialStrategy strategy;
|
||||
|
||||
grid = new QuadPrefixTree(ctx,25);
|
||||
strategy = new RecursivePrefixTreeStrategy(grid, "recursive_quad");
|
||||
ctorArgs.add(new Object[]{new Param(strategy)});
|
||||
|
||||
grid = new GeohashPrefixTree(ctx,12);
|
||||
strategy = new TermQueryPrefixTreeStrategy(grid, "termquery_geohash");
|
||||
ctorArgs.add(new Object[]{new Param(strategy)});
|
||||
|
||||
strategy = new TwoDoublesStrategy(ctx, "twodoubles");
|
||||
ctorArgs.add(new Object[]{new Param(strategy)});
|
||||
|
||||
strategy = new BBoxStrategy(ctx, "bbox");
|
||||
ctorArgs.add(new Object[]{new Param(strategy)});
|
||||
|
||||
return ctorArgs;
|
||||
}
|
||||
|
||||
// this is a hack for clover!
|
||||
static class Param {
|
||||
SpatialStrategy strategy;
|
||||
|
||||
Param(SpatialStrategy strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return strategy.getFieldName();
|
||||
}
|
||||
}
|
||||
|
||||
// private String fieldName;
|
||||
|
||||
public DistanceStrategyTest(@Name("strategy") Param param) {
|
||||
SpatialStrategy strategy = param.strategy;
|
||||
this.ctx = strategy.getSpatialContext();
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistanceOrder() throws IOException {
|
||||
adoc("100", ctx.makePoint(2,1));
|
||||
adoc("101", ctx.makePoint(-1,4));
|
||||
adoc("103", (Shape)null);//test score for nothing
|
||||
commit();
|
||||
//FYI distances are in docid order
|
||||
checkDistValueSource("3,4", 2.8274937f, 5.0898066f, 180f);
|
||||
checkDistValueSource("4,0", 3.6043684f, 0.9975641f, 180f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecipScore() throws IOException {
|
||||
Point p100 = ctx.makePoint(2, 1);
|
||||
adoc("100", p100);
|
||||
Point p101 = ctx.makePoint(-1, 4);
|
||||
adoc("101", p101);
|
||||
adoc("103", (Shape)null);//test score for nothing
|
||||
commit();
|
||||
|
||||
double dist = ctx.getDistCalc().distance(p100, p101);
|
||||
Shape queryShape = ctx.makeCircle(2.01, 0.99, dist);
|
||||
checkValueSource(strategy.makeRecipDistanceValueSource(queryShape),
|
||||
new float[]{1.00f, 0.10f, 0f}, 0.09f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Document newDoc(String id, Shape shape) {
|
||||
//called by adoc(). Make compatible with BBoxStrategy.
|
||||
if (shape != null && strategy instanceof BBoxStrategy)
|
||||
shape = ctx.makeRectangle(shape.getCenter(), shape.getCenter());
|
||||
return super.newDoc(id, shape);
|
||||
}
|
||||
|
||||
void checkDistValueSource(String ptStr, float... distances) throws IOException {
|
||||
Point pt = (Point) ctx.readShape(ptStr);
|
||||
checkValueSource(strategy.makeDistanceValueSource(pt), distances, 1.0e-4f);
|
||||
}
|
||||
}
|
|
@ -24,10 +24,6 @@ import com.spatial4j.core.distance.DistanceUtils;
|
|||
import com.spatial4j.core.io.ShapeReadWriter;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.search.FilteredQuery;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
@ -157,51 +153,7 @@ public class PortedSolr3Test extends StrategyTestCase {
|
|||
checkHitsBBox("43.517030,-96.789603", 110, 1, 17);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test is similar to a Solr 3 spatial test.
|
||||
*/
|
||||
@Test
|
||||
public void testDistanceOrder() throws IOException {
|
||||
adoc("100","1,2");
|
||||
adoc("101","4,-1");
|
||||
commit();
|
||||
double km1000inDeg = DistanceUtils.dist2Degrees(1000, DistanceUtils.EARTH_MEAN_RADIUS_KM);
|
||||
|
||||
//query closer to #100
|
||||
checkHitsOrdered("Intersects(Circle(3,4 d="+km1000inDeg+"))", "101", "100");
|
||||
//query closer to #101
|
||||
checkHitsOrdered("Intersects(Circle(4,0 d="+km1000inDeg+"))", "100", "101");
|
||||
}
|
||||
|
||||
private void checkHitsOrdered(String spatialQ, String... ids) {
|
||||
SpatialArgs args = this.argsParser.parse(spatialQ,ctx);
|
||||
Query query = strategy.makeQuery(args);
|
||||
SearchResults results = executeQuery(query, 100);
|
||||
String[] resultIds = new String[results.numFound];
|
||||
int i = 0;
|
||||
for (SearchResult result : results.results) {
|
||||
resultIds[i++] = result.document.get("id");
|
||||
}
|
||||
assertArrayEquals("order matters",ids, resultIds);
|
||||
}
|
||||
|
||||
//---- these are similar to Solr test methods
|
||||
|
||||
private void adoc(String idStr, String shapeStr) throws IOException {
|
||||
Shape shape = new ShapeReadWriter(ctx).readShape(shapeStr);
|
||||
addDocument(newDoc(idStr,shape));
|
||||
}
|
||||
|
||||
private Document newDoc(String id, Shape shape) {
|
||||
Document doc = new Document();
|
||||
doc.add(new StringField("id", id, Field.Store.YES));
|
||||
for (Field f : strategy.createIndexableFields(shape)) {
|
||||
doc.add(f);
|
||||
}
|
||||
if (storeShape)
|
||||
doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape)));
|
||||
return doc;
|
||||
}
|
||||
|
||||
private void checkHitsCircle(String ptStr, double distKM, int assertNumFound, int... assertIds) {
|
||||
_checkHits(false, ptStr, distKM, assertNumFound, assertIds);
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.lucene.spatial;
|
|||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.distance.DistanceUtils;
|
||||
import com.spatial4j.core.io.ShapeReadWriter;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
@ -45,7 +46,6 @@ import org.apache.lucene.spatial.query.SpatialOperation;
|
|||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -75,8 +75,9 @@ public class SpatialExample extends LuceneTestCase {
|
|||
|
||||
/**
|
||||
* The Lucene spatial {@link SpatialStrategy} encapsulates an approach to
|
||||
* indexing and searching shapes, and providing relevancy scores for them.
|
||||
* It's a simple API to unify different approaches.
|
||||
* indexing and searching shapes, and providing distance values for them.
|
||||
* It's a simple API to unify different approaches. You might use more than
|
||||
* one strategy for a shape as each strategy has its strengths and weaknesses.
|
||||
* <p />
|
||||
* Note that these are initialized with a field name.
|
||||
*/
|
||||
|
@ -85,13 +86,13 @@ public class SpatialExample extends LuceneTestCase {
|
|||
private Directory directory;
|
||||
|
||||
protected void init() {
|
||||
//Typical geospatial context with kilometer units.
|
||||
// These can also be constructed from a factory: SpatialContextFactory
|
||||
//Typical geospatial context
|
||||
// These can also be constructed from SpatialContextFactory
|
||||
this.ctx = SpatialContext.GEO;
|
||||
|
||||
int maxLevels = 10;//results in sub-meter precision for geohash
|
||||
int maxLevels = 11;//results in sub-meter precision for geohash
|
||||
//TODO demo lookup by detail distance
|
||||
// This can also be constructed from a factory: SpatialPrefixTreeFactory
|
||||
// This can also be constructed from SpatialPrefixTreeFactory
|
||||
SpatialPrefixTree grid = new GeohashPrefixTree(ctx, maxLevels);
|
||||
|
||||
this.strategy = new RecursivePrefixTreeStrategy(grid, "myGeoField");
|
||||
|
@ -151,9 +152,8 @@ public class SpatialExample extends LuceneTestCase {
|
|||
}
|
||||
//--Match all, order by distance
|
||||
{
|
||||
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects,//doesn't matter
|
||||
ctx.makePoint(60, -50));
|
||||
ValueSource valueSource = strategy.makeValueSource(args);//the distance (in degrees)
|
||||
Point pt = ctx.makePoint(60, -50);
|
||||
ValueSource valueSource = strategy.makeDistanceValueSource(pt);//the distance (in degrees)
|
||||
Sort reverseDistSort = new Sort(valueSource.getSortField(false)).rewrite(indexSearcher);//true=asc dist
|
||||
TopDocs docs = indexSearcher.search(new MatchAllDocsQuery(), 10, reverseDistSort);
|
||||
assertDocMatchedIds(indexSearcher, docs, 4, 20, 2);
|
||||
|
|
|
@ -32,14 +32,15 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class SpatialTestCase extends LuceneTestCase {
|
||||
|
||||
private DirectoryReader indexReader;
|
||||
private RandomIndexWriter indexWriter;
|
||||
private Directory directory;
|
||||
private IndexSearcher indexSearcher;
|
||||
protected IndexSearcher indexSearcher;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
|
|
|
@ -27,6 +27,11 @@ import org.apache.lucene.document.Document;
|
|||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.queries.function.FunctionQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.CheckHits;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.spatial.query.SpatialArgsParser;
|
||||
import org.junit.Assert;
|
||||
|
||||
|
@ -167,4 +172,47 @@ public abstract class StrategyTestCase extends SpatialTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void adoc(String id, String shapeStr) throws IOException {
|
||||
Shape shape = shapeStr==null ? null : new ShapeReadWriter(ctx).readShape(shapeStr);
|
||||
addDocument(newDoc(id, shape));
|
||||
}
|
||||
protected void adoc(String id, Shape shape) throws IOException {
|
||||
addDocument(newDoc(id, shape));
|
||||
}
|
||||
|
||||
protected Document newDoc(String id, Shape shape) {
|
||||
Document doc = new Document();
|
||||
doc.add(new StringField("id", id, Field.Store.YES));
|
||||
if (shape != null) {
|
||||
for (Field f : strategy.createIndexableFields(shape)) {
|
||||
doc.add(f);
|
||||
}
|
||||
if (storeShape)
|
||||
doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape)));
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/** scores[] are in docId order */
|
||||
protected void checkValueSource(ValueSource vs, float scores[], float delta) throws IOException {
|
||||
FunctionQuery q = new FunctionQuery(vs);
|
||||
|
||||
// //TODO is there any point to this check?
|
||||
// int expectedDocs[] = new int[scores.length];//fill with ascending 0....length-1
|
||||
// for (int i = 0; i < expectedDocs.length; i++) {
|
||||
// expectedDocs[i] = i;
|
||||
// }
|
||||
// CheckHits.checkHits(random(), q, "", indexSearcher, expectedDocs);
|
||||
|
||||
TopDocs docs = indexSearcher.search(q, 1000);//calculates the score
|
||||
for (int i = 0; i < docs.scoreDocs.length; i++) {
|
||||
ScoreDoc gotSD = docs.scoreDocs[i];
|
||||
float expectedScore = scores[gotSD.doc];
|
||||
assertEquals("Not equal for doc "+gotSD.doc, expectedScore, gotSD.score, delta);
|
||||
}
|
||||
|
||||
CheckHits.checkExplanations(q, "", indexSearcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,10 +23,6 @@ import com.spatial4j.core.io.GeohashUtils;
|
|||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.spatial.SpatialMatchConcern;
|
||||
import org.apache.lucene.spatial.StrategyTestCase;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
|
@ -198,17 +194,6 @@ public class TestRecursivePrefixTreeStrategy extends StrategyTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private Document newDoc(String id, Shape shape) {
|
||||
Document doc = new Document();
|
||||
doc.add(new StringField("id", id, Field.Store.YES));
|
||||
for (Field f : strategy.createIndexableFields(shape)) {
|
||||
doc.add(f);
|
||||
}
|
||||
if (storeShape)
|
||||
doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape)));
|
||||
return doc;
|
||||
}
|
||||
|
||||
/** NGeohash round-trip for given precision. */
|
||||
private Point alignGeohash(Point p) {
|
||||
return GeohashUtils.decode(GeohashUtils.encodeLatLon(p.getY(), p.getX(), maxLength), ctx);
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.lucene.spatial.StrategyTestCase;
|
|||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -41,7 +42,7 @@ public class TestTwoDoublesStrategy extends StrategyTestCase {
|
|||
this.strategy = new TwoDoublesStrategy(ctx, getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test @Ignore
|
||||
public void testCircleShapeSupport() {
|
||||
Circle circle = ctx.makeCircle(ctx.makePoint(0, 0), 10);
|
||||
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, circle);
|
||||
|
|
Loading…
Reference in New Issue