mirror of https://github.com/apache/lucene.git
LUCENE-7737: Remove spatial-extras dependency on queries module
This commit is contained in:
parent
1a278ae89b
commit
2f2e00ffe2
|
@ -24,10 +24,7 @@
|
|||
<orderEntry type="library" scope="TEST" name="JUnit" level="project" />
|
||||
<orderEntry type="module" scope="TEST" module-name="lucene-test-framework" />
|
||||
<orderEntry type="module" module-name="lucene-core" />
|
||||
<orderEntry type="module" module-name="queries" />
|
||||
<orderEntry type="module" module-name="misc" />
|
||||
<orderEntry type="module" module-name="spatial3d" />
|
||||
<orderEntry type="module" module-name="backward-codecs" />
|
||||
<orderEntry type="module" module-name="analysis-common" scope="TEST"/>
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
|
|
|
@ -92,6 +92,11 @@ API Changes
|
|||
* LUCENE-7723: DoubleValuesSource enforces implementation of equals() and
|
||||
hashCode() (Alan Woodward)
|
||||
|
||||
* LUCENE-7737: The spatial-extras module no longer has a dependency on the
|
||||
queries module. All uses of ValueSource are either replaced with core
|
||||
DoubleValuesSource extensions, or with the new ShapeValuesSource and
|
||||
ShapeValuesPredicate classes (Alan Woodward, David Smiley)
|
||||
|
||||
Bug Fixes
|
||||
|
||||
* LUCENE-7626: IndexWriter will no longer accept broken token offsets
|
||||
|
|
|
@ -21,16 +21,14 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.benchmark.byTask.utils.Config;
|
||||
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.queries.function.FunctionScoreQuery;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* Reads spatial data from the body field docs from an internally created {@link LineDocSource}.
|
||||
|
@ -102,11 +100,8 @@ public class SpatialFileQueryMaker extends AbstractQueryMaker {
|
|||
Query filterQuery = strategy.makeQuery(args);
|
||||
if (score) {
|
||||
//wrap with distance computing query
|
||||
ValueSource valueSource = strategy.makeDistanceValueSource(shape.getCenter());
|
||||
return new BooleanQuery.Builder()
|
||||
.add(new FunctionQuery(valueSource), BooleanClause.Occur.MUST)//matches everything and provides score
|
||||
.add(filterQuery, BooleanClause.Occur.FILTER)//filters (score isn't used)
|
||||
.build();
|
||||
DoubleValuesSource valueSource = strategy.makeDistanceValueSource(shape.getCenter());
|
||||
return new FunctionScoreQuery(filterQuery, valueSource);
|
||||
} else {
|
||||
return filterQuery; // assume constant scoring
|
||||
}
|
||||
|
|
|
@ -35,4 +35,25 @@ public abstract class DoubleValues {
|
|||
*/
|
||||
public abstract boolean advanceExact(int doc) throws IOException;
|
||||
|
||||
/**
|
||||
* Wrap a DoubleValues instance, returning a default if the wrapped instance has no value
|
||||
*/
|
||||
public static DoubleValues withDefault(DoubleValues in, double missingValue) {
|
||||
return new DoubleValues() {
|
||||
|
||||
boolean hasValue = false;
|
||||
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
return hasValue ? in.doubleValue() : missingValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
hasValue = in.advanceExact(doc);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,12 @@ public abstract class DoubleValuesSource {
|
|||
* @return an Explanation for the value
|
||||
* @throws IOException if an {@link IOException} occurs
|
||||
*/
|
||||
public abstract Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException;
|
||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||
DoubleValues dv = getValues(ctx, DoubleValuesSource.constant(scoreExplanation.getValue()).getValues(ctx, null));
|
||||
if (dv.advanceExact(docId))
|
||||
return Explanation.match((float) dv.doubleValue(), this.toString());
|
||||
return Explanation.noMatch(this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sort field based on the value of this producer
|
||||
|
|
|
@ -258,6 +258,69 @@ public abstract class ValueSource {
|
|||
|
||||
}
|
||||
|
||||
public static ValueSource fromDoubleValuesSource(DoubleValuesSource in) {
|
||||
return new FromDoubleValuesSource(in);
|
||||
}
|
||||
|
||||
private static class FromDoubleValuesSource extends ValueSource {
|
||||
|
||||
final DoubleValuesSource in;
|
||||
|
||||
private FromDoubleValuesSource(DoubleValuesSource in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
Scorer scorer = (Scorer) context.get("scorer");
|
||||
DoubleValues scores = scorer == null ? null : DoubleValuesSource.fromScorer(scorer);
|
||||
DoubleValues inner = in.getValues(readerContext, scores);
|
||||
return new FunctionValues() {
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return in.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) throws IOException {
|
||||
if (inner.advanceExact(doc) == false)
|
||||
return 0;
|
||||
return (float) inner.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
if (inner.advanceExact(doc) == false)
|
||||
return 0;
|
||||
return inner.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
return inner.advanceExact(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FromDoubleValuesSource that = (FromDoubleValuesSource) o;
|
||||
return Objects.equals(in, that.in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return in.toString();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Sorting by function
|
||||
//
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
<path id="classpath">
|
||||
<path refid="base.classpath"/>
|
||||
<path refid="spatialjar"/>
|
||||
<pathelement path="${queries.jar}" />
|
||||
<pathelement path="${spatial3d.jar}" />
|
||||
</path>
|
||||
|
||||
|
@ -41,15 +40,12 @@
|
|||
<pathelement path="src/test-files" />
|
||||
</path>
|
||||
|
||||
<target name="compile-core" depends="jar-backward-codecs,jar-queries,jar-misc,jar-spatial3d,common.compile-core" />
|
||||
<target name="compile-core" depends="jar-spatial3d,common.compile-core" />
|
||||
|
||||
<target name="javadocs" depends="javadocs-backward-codecs,javadocs-queries,javadocs-misc,javadocs-spatial3d,compile-core,check-javadocs-uptodate"
|
||||
<target name="javadocs" depends="javadocs-spatial3d,compile-core,check-javadocs-uptodate"
|
||||
unless="javadocs-uptodate-${name}">
|
||||
<invoke-module-javadoc>
|
||||
<links>
|
||||
<link href="../backward-codecs"/>
|
||||
<link href="../queries"/>
|
||||
<link href="../misc"/>
|
||||
<link href="../spatial3d"/>
|
||||
</links>
|
||||
</invoke-module-javadoc>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.lucene.spatial;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* Iterator over {@link Shape} objects for an index segment
|
||||
*/
|
||||
public abstract class ShapeValues {
|
||||
|
||||
/**
|
||||
* Advance the iterator to the given document
|
||||
* @param doc the document to advance to
|
||||
* @return {@code true} if there is a value for this document
|
||||
*/
|
||||
public abstract boolean advanceExact(int doc) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a {@link Shape} for the current document
|
||||
*/
|
||||
public abstract Shape value() throws IOException;
|
||||
|
||||
}
|
|
@ -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.lucene.spatial;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
|
||||
/**
|
||||
* Produces {@link ShapeValues} per-segment
|
||||
*/
|
||||
public abstract class ShapeValuesSource {
|
||||
|
||||
/**
|
||||
* Get a {@link ShapeValues} instance for the given leaf reader context
|
||||
*/
|
||||
public abstract ShapeValues getValues(LeafReaderContext ctx) throws IOException;
|
||||
|
||||
}
|
|
@ -16,15 +16,15 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial;
|
||||
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.util.ReciprocalDoubleValuesSource;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.queries.function.valuesource.ReciprocalFloatFunction;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
|
||||
/**
|
||||
* The SpatialStrategy encapsulates an approach to indexing and searching based
|
||||
|
@ -103,7 +103,7 @@ public abstract class SpatialStrategy {
|
|||
* See {@link #makeDistanceValueSource(org.locationtech.spatial4j.shape.Point, double)} called with
|
||||
* a multiplier of 1.0 (i.e. units of degrees).
|
||||
*/
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint) {
|
||||
return makeDistanceValueSource(queryPoint, 1.0);
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ public abstract class SpatialStrategy {
|
|||
* then the closest one is chosen. The result is multiplied by {@code multiplier}, which
|
||||
* conveniently is used to get the desired units.
|
||||
*/
|
||||
public abstract ValueSource makeDistanceValueSource(Point queryPoint, double multiplier);
|
||||
public abstract DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier);
|
||||
|
||||
/**
|
||||
* Make a Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
|
||||
|
@ -133,13 +133,14 @@ public abstract class SpatialStrategy {
|
|||
* 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) {
|
||||
public final DoubleValuesSource 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(), 1.0), 1f, c, c);
|
||||
DoubleValuesSource distance = makeDistanceValueSource(queryShape.getCenter(), 1.0);
|
||||
return new ReciprocalDoubleValuesSource(c, distance);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,9 +18,8 @@ package org.apache.lucene.spatial.bbox;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
/**
|
||||
|
@ -79,7 +78,7 @@ public class BBoxOverlapRatioValueSource extends BBoxSimilarityValueSource {
|
|||
* @param queryTargetProportion see class javadocs. Between 0 and 1.
|
||||
* @param minSideLength see class javadocs. 0.0 will effectively disable.
|
||||
*/
|
||||
public BBoxOverlapRatioValueSource(ValueSource rectValueSource, boolean isGeo, Rectangle queryExtent,
|
||||
public BBoxOverlapRatioValueSource(ShapeValuesSource rectValueSource, boolean isGeo, Rectangle queryExtent,
|
||||
double queryTargetProportion, double minSideLength) {
|
||||
super(rectValueSource);
|
||||
this.isGeo = isGeo;
|
||||
|
@ -94,7 +93,7 @@ public class BBoxOverlapRatioValueSource extends BBoxSimilarityValueSource {
|
|||
|
||||
/** Construct with 75% weighting towards target (roughly GeoPortal's default), geo degrees assumed, no
|
||||
* minimum side length. */
|
||||
public BBoxOverlapRatioValueSource(ValueSource rectValueSource, Rectangle queryExtent) {
|
||||
public BBoxOverlapRatioValueSource(ShapeValuesSource rectValueSource, Rectangle queryExtent) {
|
||||
this(rectValueSource, true, queryExtent, 0.25, 0.0);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,78 +17,58 @@
|
|||
package org.apache.lucene.spatial.bbox;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
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.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
/**
|
||||
* A base class for calculating a spatial relevance rank per document from a provided
|
||||
* {@link ValueSource} in which {@link FunctionValues#objectVal(int)} returns a {@link
|
||||
* org.locationtech.spatial4j.shape.Rectangle}.
|
||||
* {@link ShapeValuesSource} returning a {@link
|
||||
* org.locationtech.spatial4j.shape.Rectangle} per-document.
|
||||
* <p>
|
||||
* Implementers: remember to implement equals and hashCode if you have
|
||||
* fields!
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public abstract class BBoxSimilarityValueSource extends ValueSource {
|
||||
public abstract class BBoxSimilarityValueSource extends DoubleValuesSource {
|
||||
|
||||
private final ValueSource bboxValueSource;
|
||||
private final ShapeValuesSource bboxValueSource;
|
||||
|
||||
public BBoxSimilarityValueSource(ValueSource bboxValueSource) {
|
||||
public BBoxSimilarityValueSource(ShapeValuesSource bboxValueSource) {
|
||||
this.bboxValueSource = bboxValueSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||
bboxValueSource.createWeight(context, searcher);
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+"(" + bboxValueSource.toString() + "," + similarityDescription() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return getClass().getSimpleName()+"(" + bboxValueSource.description() + "," + similarityDescription() + ")";
|
||||
}
|
||||
|
||||
/** A comma-separated list of configurable items of the subclass to put into {@link #description()}. */
|
||||
/** A comma-separated list of configurable items of the subclass to put into {@link #toString()}. */
|
||||
protected abstract String similarityDescription();
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
|
||||
final FunctionValues shapeValues = bboxValueSource.getValues(context, readerContext);
|
||||
|
||||
return new DoubleDocValues(this) {
|
||||
final ShapeValues shapeValues = bboxValueSource.getValues(readerContext);
|
||||
return DoubleValues.withDefault(new DoubleValues() {
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
//? limit to Rect or call getBoundingBox()? latter would encourage bad practice
|
||||
final Rectangle rect = (Rectangle) shapeValues.objectVal(doc);
|
||||
return rect==null ? 0 : score(rect, null);
|
||||
public double doubleValue() throws IOException {
|
||||
return score((Rectangle) shapeValues.value(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
return shapeValues.exists(doc);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return shapeValues.advanceExact(doc);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
final Rectangle rect = (Rectangle) shapeValues.objectVal(doc);
|
||||
if (rect == null) {
|
||||
return Explanation.noMatch("no rect");
|
||||
}
|
||||
AtomicReference<Explanation> explanation = new AtomicReference<>();
|
||||
score(rect, explanation);
|
||||
return explanation.get();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,4 +95,23 @@ public abstract class BBoxSimilarityValueSource extends ValueSource {
|
|||
public int hashCode() {
|
||||
return bboxValueSource.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||
DoubleValues dv = getValues(ctx, DoubleValuesSource.constant(scoreExplanation.getValue()).getValues(ctx, null));
|
||||
if (dv.advanceExact(docId)) {
|
||||
AtomicReference<Explanation> explanation = new AtomicReference<>();
|
||||
final ShapeValues shapeValues = bboxValueSource.getValues(ctx);
|
||||
if (shapeValues.advanceExact(docId)) {
|
||||
score((Rectangle) shapeValues.value(), explanation);
|
||||
return explanation.get();
|
||||
}
|
||||
}
|
||||
return Explanation.noMatch(this.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,13 @@ import org.apache.lucene.document.StringField;
|
|||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.Term;
|
||||
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.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
|
@ -211,23 +212,21 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
//---------------------------------
|
||||
|
||||
/**
|
||||
* Provides access to each rectangle per document as a ValueSource in which
|
||||
* {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)} returns a {@link
|
||||
* Shape}.
|
||||
* Provides access to each rectangle per document as a {@link ShapeValuesSource}
|
||||
*/ //TODO raise to SpatialStrategy
|
||||
public ValueSource makeShapeValueSource() {
|
||||
public ShapeValuesSource makeShapeValueSource() {
|
||||
return new BBoxValueSource(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
//TODO if makeShapeValueSource gets lifted to the top; this could become a generic impl.
|
||||
return new DistanceToShapeValueSource(makeShapeValueSource(), queryPoint, multiplier, ctx);
|
||||
}
|
||||
|
||||
/** Returns a similarity based on {@link BBoxOverlapRatioValueSource}. This is just a
|
||||
* convenience method. */
|
||||
public ValueSource makeOverlapRatioValueSource(Rectangle queryBox, double queryTargetProportion) {
|
||||
public DoubleValuesSource makeOverlapRatioValueSource(Rectangle queryBox, double queryTargetProportion) {
|
||||
return new BBoxOverlapRatioValueSource(
|
||||
makeShapeValueSource(), ctx.isGeo(), queryBox, queryTargetProportion, 0.0);
|
||||
}
|
||||
|
|
|
@ -17,24 +17,22 @@
|
|||
package org.apache.lucene.spatial.bbox;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* A ValueSource in which the indexed Rectangle is returned from
|
||||
* {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}.
|
||||
* A ShapeValuesSource returning a Rectangle from each document derived from four numeric fields
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
class BBoxValueSource extends ValueSource {
|
||||
class BBoxValueSource extends ShapeValuesSource {
|
||||
|
||||
private final BBoxStrategy strategy;
|
||||
|
||||
|
@ -43,12 +41,12 @@ class BBoxValueSource extends ValueSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return "bboxShape(" + strategy.getFieldName() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
public ShapeValues getValues(LeafReaderContext readerContext) throws IOException {
|
||||
LeafReader reader = readerContext.reader();
|
||||
final NumericDocValues minX = DocValues.getNumeric(reader, strategy.field_minX);
|
||||
final NumericDocValues minY = DocValues.getNumeric(reader, strategy.field_minY);
|
||||
|
@ -58,61 +56,23 @@ class BBoxValueSource extends ValueSource {
|
|||
//reused
|
||||
final Rectangle rect = strategy.getSpatialContext().makeRectangle(0,0,0,0);
|
||||
|
||||
return new FunctionValues() {
|
||||
private int lastDocID = -1;
|
||||
return new ShapeValues() {
|
||||
|
||||
private double getDocValue(NumericDocValues values, int doc) throws IOException {
|
||||
int curDocID = values.docID();
|
||||
if (doc > curDocID) {
|
||||
curDocID = values.advance(doc);
|
||||
}
|
||||
if (doc == curDocID) {
|
||||
return Double.longBitsToDouble(values.longValue());
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return minX.advanceExact(doc) && minY.advanceExact(doc) && maxX.advanceExact(doc) && maxY.advanceExact(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object objectVal(int doc) throws IOException {
|
||||
if (doc < lastDocID) {
|
||||
throw new AssertionError("docs were sent out-of-order: lastDocID=" + lastDocID + " vs doc=" + doc);
|
||||
}
|
||||
lastDocID = doc;
|
||||
|
||||
double minXValue = getDocValue(minX, doc);
|
||||
if (minX.docID() != doc) {
|
||||
return null;
|
||||
} else {
|
||||
double minYValue = getDocValue(minY, doc);
|
||||
double maxXValue = getDocValue(maxX, doc);
|
||||
double maxYValue = getDocValue(maxY, doc);
|
||||
rect.reset(minXValue, maxXValue, minYValue, maxYValue);
|
||||
return rect;
|
||||
}
|
||||
public Shape value() throws IOException {
|
||||
double minXValue = Double.longBitsToDouble(minX.longValue());
|
||||
double minYValue = Double.longBitsToDouble(minY.longValue());
|
||||
double maxXValue = Double.longBitsToDouble(maxX.longValue());
|
||||
double maxYValue = Double.longBitsToDouble(maxY.longValue());
|
||||
rect.reset(minXValue, maxXValue, minYValue, maxYValue);
|
||||
return rect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String strVal(int doc) throws IOException {//TODO support WKT output once Spatial4j does
|
||||
Object v = objectVal(doc);
|
||||
return v == null ? null : v.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
getDocValue(minX, doc);
|
||||
return minX.docID() == doc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
return Explanation.match(Float.NaN, toString(doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return description() + '=' + strVal(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,10 +20,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
|
@ -32,7 +30,9 @@ import org.apache.lucene.spatial.query.SpatialArgs;
|
|||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.apache.lucene.spatial.util.ShapePredicateValueSource;
|
||||
import org.apache.lucene.spatial.util.ShapeValuesPredicate;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* A composite {@link SpatialStrategy} based on {@link RecursivePrefixTreeStrategy} (RPT) and
|
||||
|
@ -86,7 +86,7 @@ public class CompositeSpatialStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
//TODO consider indexing center-point in DV? Guarantee contained by the shape, which could then be used for
|
||||
// other purposes like faster WITHIN predicate?
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -108,8 +108,8 @@ public class CompositeSpatialStrategy extends SpatialStrategy {
|
|||
throw new UnsupportedSpatialOperation(pred);
|
||||
}
|
||||
|
||||
final ShapePredicateValueSource predicateValueSource =
|
||||
new ShapePredicateValueSource(geometryStrategy.makeShapeValueSource(), pred, args.getShape());
|
||||
final ShapeValuesPredicate predicateValueSource =
|
||||
new ShapeValuesPredicate(geometryStrategy.makeShapeValueSource(), pred, args.getShape());
|
||||
//System.out.println("PredOpt: " + optimizePredicates);
|
||||
if (pred == SpatialOperation.Intersects && optimizePredicates) {
|
||||
// We have a smart Intersects impl
|
||||
|
|
|
@ -17,12 +17,9 @@
|
|||
package org.apache.lucene.spatial.composite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.ConstantScoreScorer;
|
||||
import org.apache.lucene.search.ConstantScoreWeight;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
|
@ -30,19 +27,20 @@ import org.apache.lucene.search.Query;
|
|||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.TwoPhaseIterator;
|
||||
import org.apache.lucene.search.Weight;
|
||||
import org.apache.lucene.spatial.util.ShapeValuesPredicate;
|
||||
|
||||
/**
|
||||
* A Query that considers an "indexQuery" to have approximate results, and a follow-on
|
||||
* {@link ValueSource}/{@link FunctionValues#boolVal(int)} is called to verify each hit
|
||||
* from {@link TwoPhaseIterator#matches()}.
|
||||
* ShapeValuesSource is called to verify each hit from {@link TwoPhaseIterator#matches()}.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class CompositeVerifyQuery extends Query {
|
||||
final Query indexQuery;//approximation (matches more than needed)
|
||||
final ValueSource predicateValueSource;//we call boolVal(doc)
|
||||
|
||||
public CompositeVerifyQuery(Query indexQuery, ValueSource predicateValueSource) {
|
||||
private final Query indexQuery;//approximation (matches more than needed)
|
||||
private final ShapeValuesPredicate predicateValueSource;
|
||||
|
||||
public CompositeVerifyQuery(Query indexQuery, ShapeValuesPredicate predicateValueSource) {
|
||||
this.indexQuery = indexQuery;
|
||||
this.predicateValueSource = predicateValueSource;
|
||||
}
|
||||
|
@ -84,7 +82,6 @@ public class CompositeVerifyQuery extends Query {
|
|||
@Override
|
||||
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
|
||||
final Weight indexQueryWeight = indexQuery.createWeight(searcher, false, boost);//scores aren't unsupported
|
||||
final Map valueSourceContext = ValueSource.newContext(searcher);
|
||||
|
||||
return new ConstantScoreWeight(this, boost) {
|
||||
|
||||
|
@ -96,21 +93,8 @@ public class CompositeVerifyQuery extends Query {
|
|||
return null;
|
||||
}
|
||||
|
||||
final FunctionValues predFuncValues = predicateValueSource.getValues(valueSourceContext, context);
|
||||
|
||||
final TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(indexQueryScorer.iterator()) {
|
||||
@Override
|
||||
public boolean matches() throws IOException {
|
||||
return predFuncValues.boolVal(indexQueryScorer.docID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float matchCost() {
|
||||
return 100; // TODO: use cost of predFuncValues.boolVal()
|
||||
}
|
||||
};
|
||||
|
||||
return new ConstantScoreScorer(this, score(), twoPhaseIterator);
|
||||
final TwoPhaseIterator predFuncValues = predicateValueSource.iterator(context, indexQueryScorer.iterator());
|
||||
return new ConstantScoreScorer(this, score(), predFuncValues);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,11 +17,8 @@
|
|||
package org.apache.lucene.spatial.composite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.ConstantScoreScorer;
|
||||
import org.apache.lucene.search.ConstantScoreWeight;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
|
@ -34,6 +31,7 @@ import org.apache.lucene.search.Weight;
|
|||
import org.apache.lucene.spatial.prefix.AbstractVisitingPrefixTreeQuery;
|
||||
import org.apache.lucene.spatial.prefix.tree.Cell;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.spatial.util.ShapeValuesPredicate;
|
||||
import org.apache.lucene.util.DocIdSetBuilder;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
|
@ -41,17 +39,17 @@ import org.locationtech.spatial4j.shape.SpatialRelation;
|
|||
/**
|
||||
* A spatial Intersects predicate that distinguishes an approximated match from an exact match based on which cells
|
||||
* are within the query shape. It exposes a {@link TwoPhaseIterator} that will verify a match with a provided
|
||||
* predicate in the form of a {@link ValueSource} by calling {@link FunctionValues#boolVal(int)}.
|
||||
* predicate in the form of an ShapeValuesPredicate.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public class IntersectsRPTVerifyQuery extends Query {
|
||||
|
||||
private final IntersectsDifferentiatingQuery intersectsDiffQuery;
|
||||
private final ValueSource predicateValueSource; // we call FunctionValues.boolVal(doc)
|
||||
private final ShapeValuesPredicate predicateValueSource;
|
||||
|
||||
public IntersectsRPTVerifyQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel,
|
||||
int prefixGridScanLevel, ValueSource predicateValueSource) {
|
||||
int prefixGridScanLevel, ShapeValuesPredicate predicateValueSource) {
|
||||
this.predicateValueSource = predicateValueSource;
|
||||
this.intersectsDiffQuery = new IntersectsDifferentiatingQuery(queryShape, fieldName, grid, detailLevel,
|
||||
prefixGridScanLevel);
|
||||
|
@ -83,7 +81,6 @@ public class IntersectsRPTVerifyQuery extends Query {
|
|||
|
||||
@Override
|
||||
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
|
||||
final Map valueSourceContext = ValueSource.newContext(searcher);
|
||||
|
||||
return new ConstantScoreWeight(this, boost) {
|
||||
@Override
|
||||
|
@ -110,9 +107,10 @@ public class IntersectsRPTVerifyQuery extends Query {
|
|||
exactIterator = null;
|
||||
}
|
||||
|
||||
final FunctionValues predFuncValues = predicateValueSource.getValues(valueSourceContext, context);
|
||||
|
||||
final TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(approxDISI) {
|
||||
|
||||
final TwoPhaseIterator predFuncValues = predicateValueSource.iterator(context, approxDISI);
|
||||
|
||||
@Override
|
||||
public boolean matches() throws IOException {
|
||||
final int doc = approxDISI.docID();
|
||||
|
@ -124,13 +122,12 @@ public class IntersectsRPTVerifyQuery extends Query {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return predFuncValues.boolVal(doc);
|
||||
return predFuncValues.matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float matchCost() {
|
||||
return 100; // TODO: use cost of exactIterator.advance() and predFuncValues.boolVal()
|
||||
return 100; // TODO: use cost of exactIterator.advance() and predFuncValues.cost()
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.SortedMap;
|
|||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.lucene.index.IndexReaderContext;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.spatial.prefix.tree.Cell;
|
||||
import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree;
|
||||
import org.apache.lucene.util.Bits;
|
||||
|
@ -76,7 +76,7 @@ public class NumberRangePrefixTreeStrategy extends RecursivePrefixTreeStrategy {
|
|||
|
||||
/** Unsupported. */
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.apache.lucene.document.Field;
|
|||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.IndexReaderContext;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.Cell;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
|
@ -180,7 +180,7 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
PointPrefixTreeFieldCacheProvider p = provider.get( getFieldName() );
|
||||
if( p == null ) {
|
||||
synchronized (this) {//double checked locking idiom is okay since provider is threadsafe
|
||||
|
|
|
@ -22,29 +22,27 @@ import java.io.DataInputStream;
|
|||
import java.io.DataOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.document.BinaryDocValuesField;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.index.BinaryDocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.ConstantScoreScorer;
|
||||
import org.apache.lucene.search.ConstantScoreWeight;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.TwoPhaseIterator;
|
||||
import org.apache.lucene.search.Weight;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.util.DistanceToShapeValueSource;
|
||||
import org.apache.lucene.spatial.util.ShapePredicateValueSource;
|
||||
import org.apache.lucene.spatial.util.ShapeValuesPredicate;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.io.BinaryCodec;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
|
@ -100,7 +98,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
//TODO if makeShapeValueSource gets lifted to the top; this could become a generic impl.
|
||||
return new DistanceToShapeValueSource(makeShapeValueSource(), queryPoint, multiplier, ctx);
|
||||
}
|
||||
|
@ -111,18 +109,15 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
*/
|
||||
@Override
|
||||
public Query makeQuery(SpatialArgs args) {
|
||||
ValueSource shapeValueSource = makeShapeValueSource();
|
||||
ShapePredicateValueSource predicateValueSource = new ShapePredicateValueSource(
|
||||
shapeValueSource, args.getOperation(), args.getShape());
|
||||
ShapeValuesSource shapeValueSource = makeShapeValueSource();
|
||||
ShapeValuesPredicate predicateValueSource = new ShapeValuesPredicate(shapeValueSource, args.getOperation(), args.getShape());
|
||||
return new PredicateValueSourceQuery(predicateValueSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to each shape per document as a ValueSource in which
|
||||
* {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)} returns a {@link
|
||||
* Shape}.
|
||||
* Provides access to each shape per document
|
||||
*/ //TODO raise to SpatialStrategy
|
||||
public ValueSource makeShapeValueSource() {
|
||||
public ShapeValuesSource makeShapeValueSource() {
|
||||
return new ShapeDocValueSource(getFieldName(), ctx.getBinaryCodec());
|
||||
}
|
||||
|
||||
|
@ -130,9 +125,9 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
* by {@link TwoPhaseIterator}.
|
||||
*/
|
||||
static class PredicateValueSourceQuery extends Query {
|
||||
private final ValueSource predicateValueSource;//we call boolVal(doc)
|
||||
private final ShapeValuesPredicate predicateValueSource;
|
||||
|
||||
public PredicateValueSourceQuery(ValueSource predicateValueSource) {
|
||||
public PredicateValueSourceQuery(ShapeValuesPredicate predicateValueSource) {
|
||||
this.predicateValueSource = predicateValueSource;
|
||||
}
|
||||
|
||||
|
@ -142,21 +137,8 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
@Override
|
||||
public Scorer scorer(LeafReaderContext context) throws IOException {
|
||||
DocIdSetIterator approximation = DocIdSetIterator.all(context.reader().maxDoc());
|
||||
final FunctionValues predFuncValues = predicateValueSource.getValues(null, context);
|
||||
return new ConstantScoreScorer(this, score(), new TwoPhaseIterator(approximation) {
|
||||
|
||||
@Override
|
||||
public boolean matches() throws IOException {
|
||||
final int docID = approximation.docID();
|
||||
return predFuncValues.boolVal(docID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float matchCost() {
|
||||
// TODO: what is the cost of the predicateValueSource
|
||||
return 100f;
|
||||
}
|
||||
});
|
||||
TwoPhaseIterator it = predicateValueSource.iterator(context, approximation);
|
||||
return new ConstantScoreScorer(this, score(), it);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -184,7 +166,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
* Implements a ValueSource by deserializing a Shape in from BinaryDocValues using BinaryCodec.
|
||||
* @see #makeShapeValueSource()
|
||||
*/
|
||||
static class ShapeDocValueSource extends ValueSource {
|
||||
static class ShapeDocValueSource extends ShapeValuesSource {
|
||||
|
||||
private final String fieldName;
|
||||
private final BinaryCodec binaryCodec;//spatial4j
|
||||
|
@ -195,65 +177,21 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
public ShapeValues getValues(LeafReaderContext readerContext) throws IOException {
|
||||
final BinaryDocValues docValues = readerContext.reader().getBinaryDocValues(fieldName);
|
||||
|
||||
return new FunctionValues() {
|
||||
int bytesRefDoc = -1;
|
||||
BytesRefBuilder bytesRef = new BytesRefBuilder();
|
||||
|
||||
boolean fillBytes(int doc) throws IOException {
|
||||
if (bytesRefDoc != doc) {
|
||||
if (docValues.docID() < doc) {
|
||||
docValues.advance(doc);
|
||||
}
|
||||
if (docValues.docID() == doc) {
|
||||
bytesRef.copyBytes(docValues.binaryValue());
|
||||
} else {
|
||||
bytesRef.clear();
|
||||
}
|
||||
bytesRefDoc = doc;
|
||||
}
|
||||
return bytesRef.length() != 0;
|
||||
return new ShapeValues() {
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return docValues.advanceExact(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
return fillBytes(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bytesVal(int doc, BytesRefBuilder target) throws IOException {
|
||||
target.clear();
|
||||
if (fillBytes(doc)) {
|
||||
target.copyBytes(bytesRef);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object objectVal(int docId) throws IOException {
|
||||
if (!fillBytes(docId))
|
||||
return null;
|
||||
DataInputStream dataInput = new DataInputStream(
|
||||
new ByteArrayInputStream(bytesRef.bytes(), 0, bytesRef.length()));
|
||||
try {
|
||||
return binaryCodec.readShape(dataInput);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
return Explanation.match(Float.NaN, toString(doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return description() + "=" + objectVal(doc);//TODO truncate?
|
||||
public Shape value() throws IOException {
|
||||
BytesRef bytesRef = docValues.binaryValue();
|
||||
DataInputStream dataInput
|
||||
= new DataInputStream(new ByteArrayInputStream(bytesRef.bytes, bytesRef.offset, bytesRef.length));
|
||||
return binaryCodec.readShape(dataInput);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -278,7 +216,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return "shapeDocVal(" + fieldName + ")";
|
||||
}
|
||||
}//ShapeDocValueSource
|
||||
|
|
|
@ -16,64 +16,73 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
|
||||
/**
|
||||
* Caches the doubleVal of another value source in a HashMap
|
||||
* so that it is computed only once.
|
||||
* @lucene.internal
|
||||
*/
|
||||
public class CachingDoubleValueSource extends ValueSource {
|
||||
public class CachingDoubleValueSource extends DoubleValuesSource {
|
||||
|
||||
final ValueSource source;
|
||||
final DoubleValuesSource source;
|
||||
final Map<Integer, Double> cache;
|
||||
|
||||
public CachingDoubleValueSource( ValueSource source )
|
||||
{
|
||||
public CachingDoubleValueSource(DoubleValuesSource source) {
|
||||
this.source = source;
|
||||
cache = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Cached["+source.description()+"]";
|
||||
public String toString() {
|
||||
return "Cached["+source.toString()+"]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
final int base = readerContext.docBase;
|
||||
final FunctionValues vals = source.getValues(context,readerContext);
|
||||
return new FunctionValues() {
|
||||
final DoubleValues vals = source.getValues(readerContext, scores);
|
||||
return new DoubleValues() {
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
Integer key = Integer.valueOf( base+doc );
|
||||
Double v = cache.get( key );
|
||||
if( v == null ) {
|
||||
v = Double.valueOf( vals.doubleVal(doc) );
|
||||
cache.put( key, v );
|
||||
public double doubleValue() throws IOException {
|
||||
int key = base + doc;
|
||||
Double v = cache.get(key);
|
||||
if (v == null) {
|
||||
v = vals.doubleValue();
|
||||
cache.put(key, v);
|
||||
}
|
||||
return v.doubleValue();
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) throws IOException {
|
||||
return (float)doubleVal(doc);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
this.doc = doc;
|
||||
return vals.advanceExact(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return doubleVal(doc)+"";
|
||||
}
|
||||
int doc = -1;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||
return source.explain(ctx, docId, scoreExplanation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -17,81 +17,67 @@
|
|||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
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.search.Explanation;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceCalculator;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* The distance from a provided Point to a Point retrieved from a ValueSource via
|
||||
* {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}. The distance
|
||||
* The distance from a provided Point to a Point retrieved from an ShapeValuesSource. The distance
|
||||
* is calculated via a {@link org.locationtech.spatial4j.distance.DistanceCalculator}.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class DistanceToShapeValueSource extends ValueSource {
|
||||
private final ValueSource shapeValueSource;
|
||||
public class DistanceToShapeValueSource extends DoubleValuesSource {
|
||||
|
||||
private final ShapeValuesSource shapeValueSource;
|
||||
private final Point queryPoint;
|
||||
private final double multiplier;
|
||||
private final DistanceCalculator distCalc;
|
||||
|
||||
//TODO if FunctionValues returns NaN; will things be ok?
|
||||
private final double nullValue;//computed
|
||||
//TODO if DoubleValues returns NaN; will things be ok?
|
||||
private final double nullValue;
|
||||
|
||||
public DistanceToShapeValueSource(ValueSource shapeValueSource, Point queryPoint,
|
||||
public DistanceToShapeValueSource(ShapeValuesSource shapeValueSource, Point queryPoint,
|
||||
double multiplier, SpatialContext ctx) {
|
||||
this.shapeValueSource = shapeValueSource;
|
||||
this.queryPoint = queryPoint;
|
||||
this.multiplier = multiplier;
|
||||
this.distCalc = ctx.getDistCalc();
|
||||
this.nullValue =
|
||||
(ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
|
||||
this.nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "distance(" + queryPoint + " to " + shapeValueSource.description() + ")*" + multiplier + ")";
|
||||
public String toString() {
|
||||
return "distance(" + queryPoint + " to " + shapeValueSource.toString() + ")*" + multiplier + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||
shapeValueSource.createWeight(context, searcher);
|
||||
}
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
final FunctionValues shapeValues = shapeValueSource.getValues(context, readerContext);
|
||||
final ShapeValues shapeValues = shapeValueSource.getValues(readerContext);
|
||||
|
||||
return new DoubleDocValues(this) {
|
||||
return DoubleValues.withDefault(new DoubleValues() {
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
Shape shape = (Shape) shapeValues.objectVal(doc);
|
||||
if (shape == null || shape.isEmpty())
|
||||
return nullValue;
|
||||
Point pt = shape.getCenter();
|
||||
return distCalc.distance(queryPoint, pt) * multiplier;
|
||||
public double doubleValue() throws IOException {
|
||||
return distCalc.distance(queryPoint, shapeValues.value().getCenter()) * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
Explanation exp = super.explain(doc);
|
||||
List<Explanation> details = new ArrayList<>(Arrays.asList(exp.getDetails()));
|
||||
details.add(shapeValues.explain(doc));
|
||||
return Explanation.match(exp.getValue(), exp.getDescription(), details);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return shapeValues.advanceExact(doc);
|
||||
}
|
||||
};
|
||||
}, nullValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.lucene.spatial.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
|
||||
/**
|
||||
* Transforms a DoubleValuesSource using the formula v = k / (v + k)
|
||||
*/
|
||||
public class ReciprocalDoubleValuesSource extends DoubleValuesSource {
|
||||
|
||||
private final double distToEdge;
|
||||
private final DoubleValuesSource input;
|
||||
|
||||
/**
|
||||
* Creates a ReciprocalDoubleValuesSource
|
||||
* @param distToEdge the value k in v = k / (v + k)
|
||||
* @param input the input DoubleValuesSource to transform
|
||||
*/
|
||||
public ReciprocalDoubleValuesSource(double distToEdge, DoubleValuesSource input) {
|
||||
this.distToEdge = distToEdge;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||
DoubleValues in = input.getValues(ctx, scores);
|
||||
return new DoubleValues() {
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
return recip(in.doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return in.advanceExact(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private double recip(double in) {
|
||||
return distToEdge / (in + distToEdge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return input.needsScores();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||
Explanation expl = input.explain(ctx, docId, scoreExplanation);
|
||||
return Explanation.match((float)recip(expl.getValue()),
|
||||
distToEdge + " / (v + " + distToEdge + "), computed from:", expl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ReciprocalDoubleValuesSource that = (ReciprocalDoubleValuesSource) o;
|
||||
return Double.compare(that.distToEdge, distToEdge) == 0 &&
|
||||
Objects.equals(input, that.input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(distToEdge, input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "recip(" + distToEdge + ", " + input.toString() + ")";
|
||||
}
|
||||
}
|
|
@ -17,36 +17,29 @@
|
|||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
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.search.Explanation;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
|
||||
/**
|
||||
* The area of a Shape retrieved from a ValueSource via
|
||||
* {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}.
|
||||
* The area of a Shape retrieved from an ShapeValuesSource
|
||||
*
|
||||
* @see Shape#getArea(org.locationtech.spatial4j.context.SpatialContext)
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class ShapeAreaValueSource extends ValueSource {
|
||||
private final ValueSource shapeValueSource;
|
||||
public class ShapeAreaValueSource extends DoubleValuesSource {
|
||||
private final ShapeValuesSource shapeValueSource;
|
||||
private final SpatialContext ctx;//not part of identity; should be associated with shapeValueSource indirectly
|
||||
private final boolean geoArea;
|
||||
private double multiplier;
|
||||
|
||||
public ShapeAreaValueSource(ValueSource shapeValueSource, SpatialContext ctx, boolean geoArea, double multiplier) {
|
||||
public ShapeAreaValueSource(ShapeValuesSource shapeValueSource, SpatialContext ctx, boolean geoArea, double multiplier) {
|
||||
this.shapeValueSource = shapeValueSource;
|
||||
this.ctx = ctx;
|
||||
this.geoArea = geoArea;
|
||||
|
@ -54,43 +47,29 @@ public class ShapeAreaValueSource extends ValueSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "area(" + shapeValueSource.description() + ",geo=" + geoArea + ")";
|
||||
public String toString() {
|
||||
return "area(" + shapeValueSource.toString() + ",geo=" + geoArea + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||
shapeValueSource.createWeight(context, searcher);
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
final ShapeValues shapeValues = shapeValueSource.getValues(readerContext);
|
||||
return DoubleValues.withDefault(new DoubleValues() {
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
return shapeValues.value().getArea(geoArea ? ctx : null) * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return shapeValues.advanceExact(doc);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
final FunctionValues shapeValues = shapeValueSource.getValues(context, readerContext);
|
||||
|
||||
return new DoubleDocValues(this) {
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
Shape shape = (Shape) shapeValues.objectVal(doc);
|
||||
if (shape == null || shape.isEmpty())
|
||||
return 0;//or NaN?
|
||||
//This part of Spatial4j API is kinda weird. Passing null means 2D area, otherwise geo
|
||||
// assuming ctx.isGeo()
|
||||
return shape.getArea( geoArea ? ctx : null ) * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
return shapeValues.exists(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
Explanation exp = super.explain(doc);
|
||||
List<Explanation> details = new ArrayList<>(Arrays.asList(exp.getDetails()));
|
||||
details.add(shapeValues.explain(doc));
|
||||
return Explanation.match(exp.getValue(), exp.getDescription(), details);
|
||||
}
|
||||
};
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,26 +16,25 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceCalculator;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of the Lucene ValueSource that returns the spatial distance
|
||||
* A DoubleValuesSource that returns the spatial distance
|
||||
* between an input point and a document's points in
|
||||
* {@link ShapeFieldCacheProvider}. The shortest distance is returned if a
|
||||
* document has more than one point.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public class ShapeFieldCacheDistanceValueSource extends ValueSource {
|
||||
public class ShapeFieldCacheDistanceValueSource extends DoubleValuesSource {
|
||||
|
||||
private final SpatialContext ctx;
|
||||
private final Point from;
|
||||
|
@ -51,43 +50,43 @@ public class ShapeFieldCacheDistanceValueSource extends ValueSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+"("+provider+", "+from+")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, final LeafReaderContext readerContext) throws IOException {
|
||||
return new FunctionValues() {
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
|
||||
final double nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
|
||||
|
||||
return DoubleValues.withDefault(new DoubleValues() {
|
||||
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 * multiplier : Double.MAX_VALUE);
|
||||
|
||||
private List<Point> currentVals;
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) {
|
||||
return (float) doubleVal(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) {
|
||||
|
||||
List<Point> vals = cache.getShapes( doc );
|
||||
if( vals != null ) {
|
||||
double v = calculator.distance(from, vals.get(0));
|
||||
for( int i=1; i<vals.size(); i++ ) {
|
||||
v = Math.min(v, calculator.distance(from, vals.get(i)));
|
||||
}
|
||||
return v * multiplier;
|
||||
public double doubleValue() throws IOException {
|
||||
double v = calculator.distance(from, currentVals.get(0));
|
||||
for (int i = 1; i < currentVals.size(); i++) {
|
||||
v = Math.min(v, calculator.distance(from, currentVals.get(i)));
|
||||
}
|
||||
return nullValue;
|
||||
return v * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) {
|
||||
return description() + "=" + floatVal(doc);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
currentVals = cache.getShapes(doc);
|
||||
return currentVals != null;
|
||||
}
|
||||
};
|
||||
}, nullValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,30 +16,29 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.queries.function.docvalues.BoolDocValues;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.search.TwoPhaseIterator;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* A boolean ValueSource that compares a shape from a provided ValueSource with a given Shape and sees
|
||||
* Compares a shape from a provided {@link ShapeValuesSource} with a given Shape and sees
|
||||
* if it matches a given {@link SpatialOperation} (the predicate).
|
||||
*
|
||||
* Consumers should call {@link #iterator(LeafReaderContext, DocIdSetIterator)} to obtain a
|
||||
* {@link TwoPhaseIterator} over a particular {@link DocIdSetIterator}. The initial DocIdSetIterator
|
||||
* will be used as the approximation, and the {@link SpatialOperation} comparison will only be
|
||||
* performed in {@link TwoPhaseIterator#matches()}
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class ShapePredicateValueSource extends ValueSource {
|
||||
private final ValueSource shapeValuesource;//the left hand side
|
||||
public class ShapeValuesPredicate {
|
||||
private final ShapeValuesSource shapeValuesource;//the left hand side
|
||||
private final SpatialOperation op;
|
||||
private final Shape queryShape;//the right hand side (constant)
|
||||
|
||||
|
@ -50,41 +49,28 @@ public class ShapePredicateValueSource extends ValueSource {
|
|||
* @param op the predicate
|
||||
* @param queryShape The shape on the right-hand (query) side.
|
||||
*/
|
||||
public ShapePredicateValueSource(ValueSource shapeValuesource, SpatialOperation op, Shape queryShape) {
|
||||
public ShapeValuesPredicate(ShapeValuesSource shapeValuesource, SpatialOperation op, Shape queryShape) {
|
||||
this.shapeValuesource = shapeValuesource;
|
||||
this.op = op;
|
||||
this.queryShape = queryShape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return shapeValuesource + " " + op + " " + queryShape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
|
||||
shapeValuesource.createWeight(context, searcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
final FunctionValues shapeValues = shapeValuesource.getValues(context, readerContext);
|
||||
|
||||
return new BoolDocValues(this) {
|
||||
public TwoPhaseIterator iterator(LeafReaderContext ctx, DocIdSetIterator approximation) throws IOException {
|
||||
final ShapeValues shapeValues = shapeValuesource.getValues(ctx);
|
||||
return new TwoPhaseIterator(approximation) {
|
||||
@Override
|
||||
public boolean boolVal(int doc) throws IOException {
|
||||
Shape indexedShape = (Shape) shapeValues.objectVal(doc);
|
||||
if (indexedShape == null)
|
||||
return false;
|
||||
return op.evaluate(indexedShape, queryShape);
|
||||
public boolean matches() throws IOException {
|
||||
return shapeValues.advanceExact(approximation.docID()) && op.evaluate(shapeValues.value(), queryShape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
Explanation exp = super.explain(doc);
|
||||
List<Explanation> details = new ArrayList<>(Arrays.asList(exp.getDetails()));
|
||||
details.add(shapeValues.explain(doc));
|
||||
return Explanation.match(exp.getValue(), exp.getDescription(), details);
|
||||
public float matchCost() {
|
||||
return 100; // is this necessary?
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -94,7 +80,7 @@ public class ShapePredicateValueSource extends ValueSource {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ShapePredicateValueSource that = (ShapePredicateValueSource) o;
|
||||
ShapeValuesPredicate that = (ShapeValuesPredicate) o;
|
||||
|
||||
if (!shapeValuesource.equals(that.shapeValuesource)) return false;
|
||||
if (!op.equals(that.op)) return false;
|
|
@ -16,29 +16,28 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
import org.locationtech.spatial4j.distance.DistanceCalculator;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.locationtech.spatial4j.distance.DistanceCalculator;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
|
||||
/**
|
||||
* An implementation of the Lucene ValueSource model that returns the distance
|
||||
* for a {@link PointVectorStrategy}.
|
||||
* A DoubleValuesSource that returns the distance for a {@link PointVectorStrategy}.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public class DistanceValueSource extends ValueSource {
|
||||
public class DistanceValueSource extends DoubleValuesSource {
|
||||
|
||||
private PointVectorStrategy strategy;
|
||||
private final Point from;
|
||||
private final double multiplier;
|
||||
private final double nullValue;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -47,13 +46,14 @@ public class DistanceValueSource extends ValueSource {
|
|||
this.strategy = strategy;
|
||||
this.from = from;
|
||||
this.multiplier = multiplier;
|
||||
this.nullValue = 180 * multiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ValueSource description.
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return "DistanceValueSource("+strategy+", "+from+")";
|
||||
}
|
||||
|
||||
|
@ -61,55 +61,35 @@ public class DistanceValueSource extends ValueSource {
|
|||
* Returns the FunctionValues used by the function query.
|
||||
*/
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
LeafReader reader = readerContext.reader();
|
||||
|
||||
final NumericDocValues ptX = DocValues.getNumeric(reader, strategy.getFieldNameX());
|
||||
final NumericDocValues ptY = DocValues.getNumeric(reader, strategy.getFieldNameY());
|
||||
|
||||
return new FunctionValues() {
|
||||
|
||||
private int lastDocID = -1;
|
||||
return DoubleValues.withDefault(new DoubleValues() {
|
||||
|
||||
private final Point from = DistanceValueSource.this.from;
|
||||
private final DistanceCalculator calculator = strategy.getSpatialContext().getDistCalc();
|
||||
private final double nullValue =
|
||||
(strategy.getSpatialContext().isGeo() ? 180 * multiplier : Double.MAX_VALUE);
|
||||
|
||||
private double getDocValue(NumericDocValues values, int doc) throws IOException {
|
||||
int curDocID = values.docID();
|
||||
if (doc > curDocID) {
|
||||
curDocID = values.advance(doc);
|
||||
}
|
||||
if (doc == curDocID) {
|
||||
return Double.longBitsToDouble(values.longValue());
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
double x = Double.longBitsToDouble(ptX.longValue());
|
||||
double y = Double.longBitsToDouble(ptY.longValue());
|
||||
return calculator.distance(from, x, y) * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) throws IOException {
|
||||
return (float) doubleVal(doc);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return ptX.advanceExact(doc) && ptY.advanceExact(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
// make sure it has minX and area
|
||||
double x = getDocValue(ptX, doc);
|
||||
if (ptX.docID() == doc) {
|
||||
double y = getDocValue(ptY, doc);
|
||||
assert ptY.docID() == doc;
|
||||
return calculator.distance(from, x, y) * multiplier;
|
||||
}
|
||||
return nullValue;
|
||||
}
|
||||
}, nullValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return description() + "=" + floatVal(doc);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,18 +16,30 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.document.DoubleDocValuesField;
|
||||
import org.apache.lucene.document.DoublePoint;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.queries.function.FunctionRangeQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.ConstantScoreScorer;
|
||||
import org.apache.lucene.search.ConstantScoreWeight;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.TwoPhaseIterator;
|
||||
import org.apache.lucene.search.Weight;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
|
@ -169,12 +181,12 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
return new DistanceValueSource(this, queryPoint, multiplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantScoreQuery makeQuery(SpatialArgs args) {
|
||||
public Query makeQuery(SpatialArgs args) {
|
||||
if(! SpatialOperation.is( args.getOperation(),
|
||||
SpatialOperation.Intersects,
|
||||
SpatialOperation.IsWithin ))
|
||||
|
@ -186,13 +198,7 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
} else if (shape instanceof Circle) {
|
||||
Circle circle = (Circle)shape;
|
||||
Rectangle bbox = circle.getBoundingBox();
|
||||
Query approxQuery = makeWithin(bbox);
|
||||
BooleanQuery.Builder bqBuilder = new BooleanQuery.Builder();
|
||||
FunctionRangeQuery vsRangeQuery =
|
||||
new FunctionRangeQuery(makeDistanceValueSource(circle.getCenter()), 0.0, circle.getRadius(), true, true);
|
||||
bqBuilder.add(approxQuery, BooleanClause.Occur.FILTER);//should have lowest "cost" value; will drive iteration
|
||||
bqBuilder.add(vsRangeQuery, BooleanClause.Occur.FILTER);
|
||||
return new ConstantScoreQuery(bqBuilder.build());
|
||||
return new DistanceRangeQuery(makeWithin(bbox), makeDistanceValueSource(circle.getCenter()), circle.getRadius());
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Only Rectangles and Circles are currently supported, " +
|
||||
"found [" + shape.getClass() + "]");//TODO
|
||||
|
@ -237,4 +243,71 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
//TODO try doc-value range query?
|
||||
throw new UnsupportedOperationException("An index is required for this operation.");
|
||||
}
|
||||
|
||||
private static class DistanceRangeQuery extends Query {
|
||||
|
||||
final Query inner;
|
||||
final DoubleValuesSource distanceSource;
|
||||
final double limit;
|
||||
|
||||
private DistanceRangeQuery(Query inner, DoubleValuesSource distanceSource, double limit) {
|
||||
this.inner = inner;
|
||||
this.distanceSource = distanceSource;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
Query rewritten = inner.rewrite(reader);
|
||||
if (rewritten == inner)
|
||||
return this;
|
||||
return new DistanceRangeQuery(rewritten, distanceSource, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
|
||||
Weight w = inner.createWeight(searcher, needsScores, 1f);
|
||||
return new ConstantScoreWeight(this, boost) {
|
||||
@Override
|
||||
public Scorer scorer(LeafReaderContext context) throws IOException {
|
||||
Scorer in = w.scorer(context);
|
||||
if (in == null)
|
||||
return null;
|
||||
DoubleValues v = distanceSource.getValues(context, DoubleValuesSource.fromScorer(in));
|
||||
DocIdSetIterator approximation = in.iterator();
|
||||
TwoPhaseIterator twoPhase = new TwoPhaseIterator(approximation) {
|
||||
@Override
|
||||
public boolean matches() throws IOException {
|
||||
return v.advanceExact(approximation.docID()) && v.doubleValue() <= limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float matchCost() {
|
||||
return 100; // distance calculation can be heavy!
|
||||
}
|
||||
};
|
||||
return new ConstantScoreScorer(this, score(), twoPhase);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String field) {
|
||||
return "DistanceRangeQuery(" + inner.toString(field) + "; " + distanceSource.toString() + " < " + limit + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DistanceRangeQuery that = (DistanceRangeQuery) o;
|
||||
return Objects.equals(inner, that.inner) &&
|
||||
Objects.equals(distanceSource, that.distanceSource) && limit == that.limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(inner, distanceSource, limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,9 +84,6 @@ public class DistanceStrategyTest extends StrategyTestCase {
|
|||
adoc("100", ctx.makePoint(2, 1));
|
||||
adoc("101", ctx.makePoint(-1, 4));
|
||||
adoc("103", (Shape)null);//test score for nothing
|
||||
adoc("999", ctx.makePoint(2, 1));//test deleted
|
||||
commit();
|
||||
deleteDoc("999");
|
||||
commit();
|
||||
//FYI distances are in docid order
|
||||
checkDistValueSource(ctx.makePoint(4, 3), 2.8274937f, 5.0898066f, 180f);
|
||||
|
@ -100,9 +97,6 @@ public class DistanceStrategyTest extends StrategyTestCase {
|
|||
Point p101 = ctx.makePoint(-1.001, 4.001);
|
||||
adoc("101", p101);
|
||||
adoc("103", (Shape)null);//test score for nothing
|
||||
adoc("999", ctx.makePoint(2, 1));//test deleted
|
||||
commit();
|
||||
deleteDoc("999");
|
||||
commit();
|
||||
|
||||
double dist = ctx.getDistCalc().distance(p100, p101);
|
||||
|
|
|
@ -18,10 +18,6 @@ package org.apache.lucene.spatial;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
|
@ -30,7 +26,7 @@ import org.apache.lucene.index.DirectoryReader;
|
|||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
@ -46,6 +42,10 @@ 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.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* This class serves as example code to show how to use the Lucene spatial
|
||||
|
@ -167,7 +167,7 @@ public class SpatialExample extends LuceneTestCase {
|
|||
//--Match all, order by distance ascending
|
||||
{
|
||||
Point pt = ctx.makePoint(60, -50);
|
||||
ValueSource valueSource = strategy.makeDistanceValueSource(pt, DistanceUtils.DEG_TO_KM);//the distance (in km)
|
||||
DoubleValuesSource valueSource = strategy.makeDistanceValueSource(pt, DistanceUtils.DEG_TO_KM);//the distance (in km)
|
||||
Sort distSort = new Sort(valueSource.getSortField(false)).rewrite(indexSearcher);//false=asc dist
|
||||
TopDocs docs = indexSearcher.search(new MatchAllDocsQuery(), 10, distSort);
|
||||
assertDocMatchedIds(indexSearcher, docs, 4, 20, 2);
|
||||
|
|
|
@ -29,23 +29,21 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.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.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.Term;
|
||||
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.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.query.SpatialArgsParser;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
public abstract class StrategyTestCase extends SpatialTestCase {
|
||||
|
||||
|
@ -212,25 +210,18 @@ public abstract class StrategyTestCase extends SpatialTestCase {
|
|||
}
|
||||
|
||||
/** scores[] are in docId order */
|
||||
protected void checkValueSource(ValueSource vs, float scores[], float delta) throws IOException {
|
||||
FunctionQuery q = new FunctionQuery(vs);
|
||||
protected void checkValueSource(DoubleValuesSource vs, float scores[], float delta) throws IOException {
|
||||
|
||||
// //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 is sorted but we actually don't care about the order
|
||||
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);
|
||||
for (LeafReaderContext ctx : indexSearcher.getTopReaderContext().leaves()) {
|
||||
DoubleValues v = vs.getValues(ctx, null);
|
||||
int count = ctx.reader().maxDoc();
|
||||
for (int i = 0; i < count; i++) {
|
||||
assertTrue(v.advanceExact(i));
|
||||
int doc = i + ctx.docBase;
|
||||
assertEquals("Not equal for doc " + doc, v.doubleValue(), (double) scores[doc], delta);
|
||||
}
|
||||
}
|
||||
|
||||
CheckHits.checkExplanations(q, "", indexSearcher);
|
||||
}
|
||||
|
||||
protected void testOperation(Shape indexedShape, SpatialOperation operation,
|
||||
|
|
|
@ -29,11 +29,11 @@ import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
|||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
|
||||
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPathFactory;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPoint;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
|
||||
import org.apache.lucene.spatial3d.geom.GeoShape;
|
||||
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
|
||||
import org.apache.lucene.spatial3d.geom.PlanetModel;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
|
|
|
@ -81,7 +81,7 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
|
|||
return GeoBBoxFactory.makeGeoBBox(planetModel, maxLat, minLat, leftLon, rightLon);
|
||||
}
|
||||
|
||||
abstract class Geo3dRectIntersectionTestHelper extends RectIntersectionTestHelper<Geo3dShape> {
|
||||
public abstract class Geo3dRectIntersectionTestHelper extends RectIntersectionTestHelper<Geo3dShape> {
|
||||
|
||||
public Geo3dRectIntersectionTestHelper(SpatialContext ctx) {
|
||||
super(ctx);
|
||||
|
|
|
@ -25,12 +25,13 @@ import org.apache.lucene.document.StringField;
|
|||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.Term;
|
||||
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.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.bbox.BBoxOverlapRatioValueSource;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
|
@ -257,23 +258,21 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
//---------------------------------
|
||||
|
||||
/**
|
||||
* Provides access to each rectangle per document as a ValueSource in which
|
||||
* {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)} returns a {@link
|
||||
* Shape}.
|
||||
* Provides access to each rectangle per document
|
||||
*/ //TODO raise to SpatialStrategy
|
||||
public ValueSource makeShapeValueSource() {
|
||||
public ShapeValuesSource makeShapeValueSource() {
|
||||
return new BBoxValueSource(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
//TODO if makeShapeValueSource gets lifted to the top; this could become a generic impl.
|
||||
return new DistanceToShapeValueSource(makeShapeValueSource(), queryPoint, multiplier, ctx);
|
||||
}
|
||||
|
||||
/** Returns a similarity based on {@link BBoxOverlapRatioValueSource}. This is just a
|
||||
* convenience method. */
|
||||
public ValueSource makeOverlapRatioValueSource(Rectangle queryBox, double queryTargetProportion) {
|
||||
public DoubleValuesSource makeOverlapRatioValueSource(Rectangle queryBox, double queryTargetProportion) {
|
||||
return new BBoxOverlapRatioValueSource(
|
||||
makeShapeValueSource(), ctx.isGeo(), queryBox, queryTargetProportion, 0.0);
|
||||
}
|
||||
|
|
|
@ -17,16 +17,14 @@
|
|||
package org.apache.solr.legacy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* A ValueSource in which the indexed Rectangle is returned from
|
||||
|
@ -34,7 +32,7 @@ import org.locationtech.spatial4j.shape.Rectangle;
|
|||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
class BBoxValueSource extends ValueSource {
|
||||
class BBoxValueSource extends ShapeValuesSource {
|
||||
|
||||
private final BBoxStrategy strategy;
|
||||
|
||||
|
@ -43,76 +41,34 @@ class BBoxValueSource extends ValueSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return "bboxShape(" + strategy.getFieldName() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
LeafReader reader = readerContext.reader();
|
||||
final NumericDocValues minX = DocValues.getNumeric(reader, strategy.field_minX);
|
||||
final NumericDocValues minY = DocValues.getNumeric(reader, strategy.field_minY);
|
||||
final NumericDocValues maxX = DocValues.getNumeric(reader, strategy.field_maxX);
|
||||
final NumericDocValues maxY = DocValues.getNumeric(reader, strategy.field_maxY);
|
||||
public ShapeValues getValues(LeafReaderContext readerContext) throws IOException {
|
||||
|
||||
final DoubleValues minX = DoubleValuesSource.fromDoubleField(strategy.field_minX).getValues(readerContext, null);
|
||||
final DoubleValues minY = DoubleValuesSource.fromDoubleField(strategy.field_minY).getValues(readerContext, null);
|
||||
final DoubleValues maxX = DoubleValuesSource.fromDoubleField(strategy.field_maxX).getValues(readerContext, null);
|
||||
final DoubleValues maxY = DoubleValuesSource.fromDoubleField(strategy.field_maxY).getValues(readerContext, null);
|
||||
|
||||
//reused
|
||||
final Rectangle rect = strategy.getSpatialContext().makeRectangle(0,0,0,0);
|
||||
|
||||
return new FunctionValues() {
|
||||
private int lastDocID = -1;
|
||||
return new ShapeValues() {
|
||||
|
||||
private double getDocValue(NumericDocValues values, int doc) throws IOException {
|
||||
int curDocID = values.docID();
|
||||
if (doc > curDocID) {
|
||||
curDocID = values.advance(doc);
|
||||
}
|
||||
if (doc == curDocID) {
|
||||
return Double.longBitsToDouble(values.longValue());
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
@Override
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return minX.advanceExact(doc) && maxX.advanceExact(doc) && minY.advanceExact(doc) && maxY.advanceExact(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object objectVal(int doc) throws IOException {
|
||||
if (doc < lastDocID) {
|
||||
throw new AssertionError("docs were sent out-of-order: lastDocID=" + lastDocID + " vs doc=" + doc);
|
||||
}
|
||||
lastDocID = doc;
|
||||
|
||||
double minXValue = getDocValue(minX, doc);
|
||||
if (minX.docID() != doc) {
|
||||
return null;
|
||||
} else {
|
||||
double minYValue = getDocValue(minY, doc);
|
||||
double maxXValue = getDocValue(maxX, doc);
|
||||
double maxYValue = getDocValue(maxY, doc);
|
||||
rect.reset(minXValue, maxXValue, minYValue, maxYValue);
|
||||
return rect;
|
||||
}
|
||||
public Shape value() throws IOException {
|
||||
rect.reset(minX.doubleValue(), maxX.doubleValue(), minY.doubleValue(), maxY.doubleValue());
|
||||
return rect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String strVal(int doc) throws IOException {//TODO support WKT output once Spatial4j does
|
||||
Object v = objectVal(doc);
|
||||
return v == null ? null : v.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
getDocValue(minX, doc);
|
||||
return minX.docID() == doc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
return Explanation.match(Float.NaN, toString(doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return description() + '=' + strVal(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,17 +16,13 @@
|
|||
*/
|
||||
package org.apache.solr.legacy;
|
||||
|
||||
import org.apache.lucene.index.NumericDocValues;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.locationtech.spatial4j.distance.DistanceCalculator;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of the Lucene ValueSource model that returns the distance
|
||||
|
@ -34,11 +30,13 @@ import java.util.Map;
|
|||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public class DistanceValueSource extends ValueSource {
|
||||
public class DistanceValueSource extends DoubleValuesSource {
|
||||
|
||||
private PointVectorStrategy strategy;
|
||||
private final Point from;
|
||||
private final double multiplier;
|
||||
private final double nullValue;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -47,13 +45,15 @@ public class DistanceValueSource extends ValueSource {
|
|||
this.strategy = strategy;
|
||||
this.from = from;
|
||||
this.multiplier = multiplier;
|
||||
this.nullValue =
|
||||
(strategy.getSpatialContext().isGeo() ? 180 * multiplier : Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ValueSource description.
|
||||
*/
|
||||
@Override
|
||||
public String description() {
|
||||
public String toString() {
|
||||
return "DistanceValueSource("+strategy+", "+from+")";
|
||||
}
|
||||
|
||||
|
@ -61,55 +61,30 @@ public class DistanceValueSource extends ValueSource {
|
|||
* Returns the FunctionValues used by the function query.
|
||||
*/
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
LeafReader reader = readerContext.reader();
|
||||
public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
|
||||
|
||||
final NumericDocValues ptX = DocValues.getNumeric(reader, strategy.getFieldNameX());
|
||||
final NumericDocValues ptY = DocValues.getNumeric(reader, strategy.getFieldNameY());
|
||||
final DoubleValues ptX = DoubleValuesSource.fromDoubleField(strategy.getFieldNameX()).getValues(readerContext, null);
|
||||
final DoubleValues ptY = DoubleValuesSource.fromDoubleField(strategy.getFieldNameY()).getValues(readerContext, null);
|
||||
final DistanceCalculator calculator = strategy.getSpatialContext().getDistCalc();
|
||||
|
||||
return new FunctionValues() {
|
||||
return DoubleValues.withDefault(new DoubleValues() {
|
||||
|
||||
private int lastDocID = -1;
|
||||
|
||||
private final Point from = DistanceValueSource.this.from;
|
||||
private final DistanceCalculator calculator = strategy.getSpatialContext().getDistCalc();
|
||||
private final double nullValue =
|
||||
(strategy.getSpatialContext().isGeo() ? 180 * multiplier : Double.MAX_VALUE);
|
||||
|
||||
private double getDocValue(NumericDocValues values, int doc) throws IOException {
|
||||
int curDocID = values.docID();
|
||||
if (doc > curDocID) {
|
||||
curDocID = values.advance(doc);
|
||||
}
|
||||
if (doc == curDocID) {
|
||||
return Double.longBitsToDouble(values.longValue());
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
@Override
|
||||
public double doubleValue() throws IOException {
|
||||
return calculator.distance(from, ptX.doubleValue(), ptY.doubleValue()) * multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatVal(int doc) throws IOException {
|
||||
return (float) doubleVal(doc);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
return ptX.advanceExact(doc) && ptY.advanceExact(doc);
|
||||
}
|
||||
}, nullValue);
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) throws IOException {
|
||||
// make sure it has minX and area
|
||||
double x = getDocValue(ptX, doc);
|
||||
if (ptX.docID() == doc) {
|
||||
double y = getDocValue(ptY, doc);
|
||||
assert ptY.docID() == doc;
|
||||
return calculator.distance(from, x, y) * multiplier;
|
||||
}
|
||||
return nullValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return description() + "=" + floatVal(doc);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,15 +23,11 @@ import org.apache.lucene.document.FieldType;
|
|||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.solr.legacy.LegacyDoubleField;
|
||||
import org.apache.solr.legacy.LegacyFieldType;
|
||||
import org.apache.solr.legacy.LegacyNumericRangeQuery;
|
||||
import org.apache.solr.legacy.LegacyNumericType;
|
||||
import org.apache.lucene.queries.function.FunctionRangeQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.queries.function.FunctionMatchQuery;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
|
@ -218,7 +214,7 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
return new DistanceValueSource(this, queryPoint, multiplier);
|
||||
}
|
||||
|
||||
|
@ -237,10 +233,11 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
Rectangle bbox = circle.getBoundingBox();
|
||||
Query approxQuery = makeWithin(bbox);
|
||||
BooleanQuery.Builder bqBuilder = new BooleanQuery.Builder();
|
||||
FunctionRangeQuery vsRangeQuery =
|
||||
new FunctionRangeQuery(makeDistanceValueSource(circle.getCenter()), 0.0, circle.getRadius(), true, true);
|
||||
double r = circle.getRadius();
|
||||
FunctionMatchQuery vsMatchQuery = new FunctionMatchQuery(makeDistanceValueSource(circle.getCenter()),
|
||||
v -> 0 <= v && v <= r);
|
||||
bqBuilder.add(approxQuery, BooleanClause.Occur.FILTER);//should have lowest "cost" value; will drive iteration
|
||||
bqBuilder.add(vsRangeQuery, BooleanClause.Occur.FILTER);
|
||||
bqBuilder.add(vsMatchQuery, BooleanClause.Occur.FILTER);
|
||||
return new ConstantScoreQuery(bqBuilder.build());
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Only Rectangles and Circles are currently supported, " +
|
||||
|
|
|
@ -20,9 +20,11 @@ import java.io.IOException;
|
|||
import java.util.Iterator;
|
||||
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.ReaderUtil;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
|
@ -35,8 +37,6 @@ import org.apache.solr.response.JSONResponseWriter;
|
|||
import org.apache.solr.response.QueryResponseWriter;
|
||||
import org.apache.solr.schema.AbstractSpatialFieldType;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
import org.apache.solr.search.QParser;
|
||||
import org.apache.solr.search.SyntaxError;
|
||||
import org.locationtech.spatial4j.io.GeoJSONWriter;
|
||||
import org.locationtech.spatial4j.io.ShapeWriter;
|
||||
import org.locationtech.spatial4j.io.SupportedFormats;
|
||||
|
@ -87,7 +87,7 @@ public class GeoTransformerFactory extends TransformerFactory
|
|||
updater.display = display;
|
||||
updater.display_error = display+"_error";
|
||||
|
||||
ValueSource shapes = null;
|
||||
final ShapeValuesSource shapes;
|
||||
AbstractSpatialFieldType<?> sdv = (AbstractSpatialFieldType<?>)sf.getType();
|
||||
SpatialStrategy strategy = sdv.getStrategy(fname);
|
||||
if(strategy instanceof CompositeSpatialStrategy) {
|
||||
|
@ -98,6 +98,8 @@ public class GeoTransformerFactory extends TransformerFactory
|
|||
shapes = ((SerializedDVStrategy)strategy)
|
||||
.makeShapeValueSource();
|
||||
}
|
||||
else
|
||||
shapes = null;
|
||||
|
||||
|
||||
String writerName = params.get("w", "GeoJSON");
|
||||
|
@ -122,20 +124,24 @@ public class GeoTransformerFactory extends TransformerFactory
|
|||
|
||||
// Using ValueSource
|
||||
if(shapes!=null) {
|
||||
// we don't really need the qparser... just so we can reuse valueSource
|
||||
QParser parser = new QParser(null,null,params, req) {
|
||||
return new DocTransformer() {
|
||||
@Override
|
||||
public Query parse() throws SyntaxError {
|
||||
return new MatchAllDocsQuery();
|
||||
public String getName() {
|
||||
return display;
|
||||
}
|
||||
};
|
||||
|
||||
return new ValueSourceAugmenter(display, parser, shapes) {
|
||||
@Override
|
||||
protected void setValue(SolrDocument doc, Object val) {
|
||||
updater.setValue(doc, val);
|
||||
public void transform(SolrDocument doc, int docid, float score) throws IOException {
|
||||
int leafOrd = ReaderUtil.subIndex(docid, context.getSearcher().getTopReaderContext().leaves());
|
||||
LeafReaderContext ctx = context.getSearcher().getTopReaderContext().leaves().get(leafOrd);
|
||||
ShapeValues values = shapes.getValues(ctx);
|
||||
int segmentDoc = docid - ctx.docBase;
|
||||
if (values.advanceExact(segmentDoc)) {
|
||||
updater.setValue(doc, values.value());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Using the raw stored values
|
||||
|
@ -160,6 +166,7 @@ public class GeoTransformerFactory extends TransformerFactory
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GeoFieldUpdater {
|
||||
|
|
|
@ -34,10 +34,12 @@ import com.google.common.cache.CacheBuilder;
|
|||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.queries.function.FunctionQuery;
|
||||
import org.apache.lucene.queries.function.FunctionScoreQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.spatial.SpatialStrategy;
|
||||
|
@ -356,12 +358,12 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
|
|||
String scoreParam = (localParams == null ? null : localParams.get(SCORE_PARAM));
|
||||
|
||||
//We get the valueSource for the score then the filter and combine them.
|
||||
ValueSource valueSource = getValueSourceFromSpatialArgs(parser, field, spatialArgs, scoreParam, strategy);
|
||||
DoubleValuesSource valueSource = getValueSourceFromSpatialArgs(parser, field, spatialArgs, scoreParam, strategy);
|
||||
if (valueSource == null) {
|
||||
return strategy.makeQuery(spatialArgs); //assumed constant scoring
|
||||
}
|
||||
|
||||
FunctionQuery functionQuery = new FunctionQuery(valueSource);
|
||||
FunctionScoreQuery functionQuery = new FunctionScoreQuery(new MatchAllDocsQuery(), valueSource);
|
||||
|
||||
if (localParams != null && !localParams.getBool(FILTER_PARAM, true))
|
||||
return functionQuery;
|
||||
|
@ -383,7 +385,7 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
|
|||
return supportedScoreModes;
|
||||
}
|
||||
|
||||
protected ValueSource getValueSourceFromSpatialArgs(QParser parser, SchemaField field, SpatialArgs spatialArgs, String score, T strategy) {
|
||||
protected DoubleValuesSource getValueSourceFromSpatialArgs(QParser parser, SchemaField field, SpatialArgs spatialArgs, String score, T strategy) {
|
||||
if (score == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -23,13 +23,13 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.solr.legacy.LegacyFieldType;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.spatial.bbox.BBoxOverlapRatioValueSource;
|
||||
import org.apache.solr.legacy.BBoxStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.util.ShapeAreaValueSource;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.legacy.BBoxStrategy;
|
||||
import org.apache.solr.legacy.LegacyFieldType;
|
||||
import org.apache.solr.search.QParser;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
|
@ -156,7 +156,7 @@ public class BBoxField extends AbstractSpatialFieldType<BBoxStrategy> implements
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ValueSource getValueSourceFromSpatialArgs(QParser parser, SchemaField field, SpatialArgs spatialArgs, String scoreParam, BBoxStrategy strategy) {
|
||||
protected DoubleValuesSource getValueSourceFromSpatialArgs(QParser parser, SchemaField field, SpatialArgs spatialArgs, String scoreParam, BBoxStrategy strategy) {
|
||||
if (scoreParam == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -18,16 +18,15 @@
|
|||
package org.apache.solr.schema;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.LatLonDocValuesField;
|
||||
import org.apache.lucene.document.LatLonPoint;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
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.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.IndexOrDocValuesQuery;
|
||||
import org.apache.lucene.search.LeafFieldComparator;
|
||||
|
@ -177,7 +176,7 @@ public class LatLonPointSpatialField extends AbstractSpatialFieldType implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
|
||||
if (!docValues) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||
getFieldName() + " must have docValues enabled to support this feature");
|
||||
|
@ -191,7 +190,7 @@ public class LatLonPointSpatialField extends AbstractSpatialFieldType implements
|
|||
/**
|
||||
* A {@link ValueSource} based around {@code LatLonDocValuesField#newDistanceSort(String, double, double)}.
|
||||
*/
|
||||
private static class DistanceSortValueSource extends ValueSource {
|
||||
private static class DistanceSortValueSource extends DoubleValuesSource {
|
||||
private final String fieldName;
|
||||
private final Point queryPoint;
|
||||
private final double multiplier;
|
||||
|
@ -218,41 +217,38 @@ public class LatLonPointSpatialField extends AbstractSpatialFieldType implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
return new DoubleDocValues(this) {
|
||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||
return new DoubleValues() {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final FieldComparator<Double> comparator =
|
||||
(FieldComparator<Double>) getSortField(false).getComparator(1, 1);
|
||||
final LeafFieldComparator leafComparator = comparator.getLeafComparator(readerContext);
|
||||
final LeafFieldComparator leafComparator = comparator.getLeafComparator(ctx);
|
||||
final double mult = multiplier; // so it's a local field
|
||||
|
||||
// Since this computation is expensive, it's worth caching it just in case.
|
||||
double cacheDoc = -1;
|
||||
double cacheVal = Double.POSITIVE_INFINITY;
|
||||
double value = Double.POSITIVE_INFINITY;
|
||||
|
||||
@Override
|
||||
public double doubleVal(int doc) {
|
||||
if (cacheDoc != doc) {
|
||||
try {
|
||||
leafComparator.copy(0, doc);
|
||||
cacheVal = comparator.value(0) * mult;
|
||||
cacheDoc = doc;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return cacheVal;
|
||||
public double doubleValue() throws IOException {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) {
|
||||
return !Double.isInfinite(doubleVal(doc));
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
leafComparator.copy(0, doc);
|
||||
value = comparator.value(0) * mult;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
public boolean needsScores() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "distSort(" + fieldName + ", " + queryPoint + ", mult:" + multiplier + ")";
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,8 @@ import java.util.Map;
|
|||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.spatial.ShapeValues;
|
||||
import org.apache.lucene.spatial.ShapeValuesSource;
|
||||
import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.query.SpatialArgsParser;
|
||||
|
@ -103,24 +102,24 @@ public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<Compos
|
|||
}
|
||||
|
||||
@Override
|
||||
public ValueSource makeShapeValueSource() {
|
||||
public ShapeValuesSource makeShapeValueSource() {
|
||||
return new CachingShapeValuesource(super.makeShapeValueSource(), getFieldName());
|
||||
}
|
||||
}
|
||||
|
||||
private static class CachingShapeValuesource extends ValueSource {
|
||||
private static class CachingShapeValuesource extends ShapeValuesSource {
|
||||
|
||||
private final ValueSource targetValueSource;
|
||||
private final ShapeValuesSource targetValueSource;
|
||||
private final String fieldName;
|
||||
|
||||
private CachingShapeValuesource(ValueSource targetValueSource, String fieldName) {
|
||||
private CachingShapeValuesource(ShapeValuesSource targetValueSource, String fieldName) {
|
||||
this.targetValueSource = targetValueSource;
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "cache(" + targetValueSource.description() + ")";
|
||||
public String toString() {
|
||||
return "cache(" + targetValueSource.toString() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,10 +141,9 @@ public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<Compos
|
|||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
|
||||
final FunctionValues targetFuncValues = targetValueSource.getValues(context, readerContext);
|
||||
public ShapeValues getValues(LeafReaderContext readerContext) throws IOException {
|
||||
final ShapeValues targetFuncValues = targetValueSource.getValues(readerContext);
|
||||
// The key is a pair of leaf reader with a docId relative to that reader. The value is a Map from field to Shape.
|
||||
final SolrCache<PerSegCacheKey,Shape> cache =
|
||||
SolrRequestInfo.getRequestInfo().getReq().getSearcher().getCache(CACHE_KEY_PREFIX + fieldName);
|
||||
|
@ -153,24 +151,20 @@ public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<Compos
|
|||
return targetFuncValues; // no caching; no configured cache
|
||||
}
|
||||
|
||||
return new FunctionValues() {
|
||||
return new ShapeValues() {
|
||||
int docId = -1;
|
||||
Shape shape = null;
|
||||
|
||||
private void setShapeFromDoc(int doc) throws IOException {
|
||||
if (docId == doc) {
|
||||
return;
|
||||
}
|
||||
docId = doc;
|
||||
@Override
|
||||
public Shape value() throws IOException {
|
||||
//lookup in cache
|
||||
IndexReader.CacheHelper cacheHelper = readerContext.reader().getCoreCacheHelper();
|
||||
if (cacheHelper == null) {
|
||||
throw new IllegalStateException("Leaf " + readerContext.reader() + " is not suited for caching");
|
||||
}
|
||||
PerSegCacheKey key = new PerSegCacheKey(cacheHelper.getKey(), doc);
|
||||
shape = cache.get(key);
|
||||
PerSegCacheKey key = new PerSegCacheKey(cacheHelper.getKey(), docId);
|
||||
Shape shape = cache.get(key);
|
||||
if (shape == null) {
|
||||
shape = (Shape) targetFuncValues.objectVal(doc);
|
||||
shape = targetFuncValues.value();
|
||||
if (shape != null) {
|
||||
cache.put(key, shape);
|
||||
}
|
||||
|
@ -180,31 +174,15 @@ public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<Compos
|
|||
((JtsGeometry) shape).index(); // TODO would be nice if some day we didn't have to cast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the cache for exists & objectVal;
|
||||
|
||||
@Override
|
||||
public boolean exists(int doc) throws IOException {
|
||||
setShapeFromDoc(doc);
|
||||
return shape != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object objectVal(int doc) throws IOException {
|
||||
setShapeFromDoc(doc);
|
||||
return shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int doc) throws IOException {
|
||||
return targetFuncValues.explain(doc);
|
||||
public boolean advanceExact(int doc) throws IOException {
|
||||
this.docId = doc;
|
||||
return targetFuncValues.advanceExact(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int doc) throws IOException {
|
||||
return targetFuncValues.toString(doc);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.queries.function.valuesource.ConstNumberSource;
|
||||
import org.apache.lucene.queries.function.valuesource.DoubleConstValueSource;
|
||||
|
@ -38,6 +35,9 @@ import org.apache.solr.search.SyntaxError;
|
|||
import org.apache.solr.search.ValueSourceParser;
|
||||
import org.apache.solr.util.DistanceUnits;
|
||||
import org.apache.solr.util.SpatialUtils;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
|
||||
/**
|
||||
* Parses "geodist" creating {@link HaversineConstFunction} or {@link HaversineFunction}
|
||||
|
@ -135,7 +135,7 @@ public class GeoDistValueSourceParser extends ValueSourceParser {
|
|||
SpatialStrategy strategy = ((SpatialStrategyMultiValueSource) mv2).strategy;
|
||||
DistanceUnits distanceUnits = ((SpatialStrategyMultiValueSource) mv2).distanceUnits;
|
||||
Point queryPoint = strategy.getSpatialContext().makePoint(constants[1], constants[0]);
|
||||
return strategy.makeDistanceValueSource(queryPoint, distanceUnits.multiplierFromDegreesToThisUnit());
|
||||
return ValueSource.fromDoubleValuesSource(strategy.makeDistanceValueSource(queryPoint, distanceUnits.multiplierFromDegreesToThisUnit()));
|
||||
}
|
||||
|
||||
if (constants != null && other instanceof VectorValueSource) {
|
||||
|
|
|
@ -20,14 +20,10 @@ import java.text.ParseException;
|
|||
import java.util.Arrays;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.apache.solr.legacy.BBoxStrategy;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.legacy.BBoxStrategy;
|
||||
import org.apache.solr.schema.BBoxField;
|
||||
import org.apache.solr.schema.IndexSchema;
|
||||
import org.apache.solr.schema.SchemaField;
|
||||
|
@ -35,6 +31,10 @@ import org.apache.solr.util.SpatialUtils;
|
|||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
/**
|
||||
* Test Solr 4's new spatial capabilities from the new Lucene spatial module. Don't thoroughly test it here because
|
||||
|
|
Loading…
Reference in New Issue