LUCENE-5408: Spatial SerializedDVStrategy

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1568807 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2014-02-16 18:58:42 +00:00
parent 87f987082b
commit 4c60821ef4
9 changed files with 684 additions and 37 deletions

View File

@ -159,7 +159,11 @@ New Features
* LUCENE-5437: ASCIIFoldingFilter now has an option to preserve the original token
and emit it on the same position as the folded token only if the actual token was
folded. (Simon Willnauer, Nik Everett)
folded. (Simon Willnauer, Nik Everett)
* LUCENE-5408: Add spatial SerializedDVStrategy that serializes a binary
representations of a shape into BinaryDocValues. It supports exact geometry
relationship calculations. (David Smiley)
Build

View File

@ -0,0 +1,291 @@
package org.apache.lucene.spatial.serialized;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.io.BinaryCodec;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.BinaryDocValuesField;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
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.util.Bits;
import org.apache.lucene.util.BytesRef;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.util.Map;
/**
* A SpatialStrategy based on serializing a Shape stored into BinaryDocValues.
* This is not at all fast; it's designed to be used in conjuction with another index based
* SpatialStrategy that is approximated (like {@link org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy})
* to add precision or eventually make more specific / advanced calculations on the per-document
* geometry.
* The serialization uses Spatial4j's {@link com.spatial4j.core.io.BinaryCodec}.
*
* @lucene.experimental
*/
public class SerializedDVStrategy extends SpatialStrategy {
/**
* A cache heuristic for the buf size based on the last shape size.
*/
//TODO do we make this non-volatile since it's merely a heuristic?
private volatile int indexLastBufSize = 8 * 1024;//8KB default on first run
/**
* Constructs the spatial strategy with its mandatory arguments.
*/
public SerializedDVStrategy(SpatialContext ctx, String fieldName) {
super(ctx, fieldName);
}
@Override
public Field[] createIndexableFields(Shape shape) {
int bufSize = Math.max(128, (int) (this.indexLastBufSize * 1.5));//50% headroom over last
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(bufSize);
final BytesRef bytesRef = new BytesRef();//receiver of byteStream's bytes
try {
ctx.getBinaryCodec().writeShape(new DataOutputStream(byteStream), shape);
//this is a hack to avoid redundant byte array copying by byteStream.toByteArray()
byteStream.writeTo(new FilterOutputStream(null/*not used*/) {
@Override
public void write(byte[] b, int off, int len) throws IOException {
bytesRef.bytes = b;
bytesRef.offset = off;
bytesRef.length = len;
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
this.indexLastBufSize = bytesRef.length;//cache heuristic
return new Field[]{new BinaryDocValuesField(getFieldName(), bytesRef)};
}
@Override
public ValueSource 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);
}
@Override
public Query makeQuery(SpatialArgs args) {
throw new UnsupportedOperationException("This strategy can't return a query that operates" +
" efficiently. Instead try a Filter or ValueSource.");
}
/**
* Returns a Filter that should be used with {@link org.apache.lucene.search.FilteredQuery#QUERY_FIRST_FILTER_STRATEGY}.
* Use in another manner is likely to result in an {@link java.lang.UnsupportedOperationException}
* to prevent misuse because the filter can't efficiently work via iteration.
*/
@Override
public Filter makeFilter(final SpatialArgs args) {
ValueSource shapeValueSource = makeShapeValueSource();
ShapePredicateValueSource predicateValueSource = new ShapePredicateValueSource(
shapeValueSource, args.getOperation(), args.getShape());
return new PredicateValueSourceFilter(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}.
*/ //TODO raise to SpatialStrategy
public ValueSource makeShapeValueSource() {
return new ShapeDocValueSource(getFieldName(), ctx.getBinaryCodec());
}
/** This filter only supports returning a DocSet with a bits(). If you try to grab the
* iterator then you'll get an UnsupportedOperationException.
*/
static class PredicateValueSourceFilter extends Filter {
private final ValueSource predicateValueSource;//we call boolVal(doc)
public PredicateValueSourceFilter(ValueSource predicateValueSource) {
this.predicateValueSource = predicateValueSource;
}
@Override
public DocIdSet getDocIdSet(final AtomicReaderContext context, final Bits acceptDocs) throws IOException {
return new DocIdSet() {
@Override
public DocIdSetIterator iterator() throws IOException {
throw new UnsupportedOperationException(
"Iteration is too slow; instead try FilteredQuery.QUERY_FIRST_FILTER_STRATEGY");
//Note that if you're truly bent on doing this, then see FunctionValues.getRangeScorer
}
@Override
public Bits bits() throws IOException {
//null Map context -- we simply don't have one. That's ok.
final FunctionValues predFuncValues = predicateValueSource.getValues(null, context);
return new Bits() {
@Override
public boolean get(int index) {
if (acceptDocs != null && !acceptDocs.get(index))
return false;
return predFuncValues.boolVal(index);
}
@Override
public int length() {
return context.reader().maxDoc();
}
};
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PredicateValueSourceFilter that = (PredicateValueSourceFilter) o;
if (!predicateValueSource.equals(that.predicateValueSource)) return false;
return true;
}
@Override
public int hashCode() {
return predicateValueSource.hashCode();
}
}//PredicateValueSourceFilter
/**
* Implements a ValueSource by deserializing a Shape in from BinaryDocValues using BinaryCodec.
* @see #makeShapeValueSource()
*/
static class ShapeDocValueSource extends ValueSource {
private final String fieldName;
private final BinaryCodec binaryCodec;//spatial4j
private ShapeDocValueSource(String fieldName, BinaryCodec binaryCodec) {
this.fieldName = fieldName;
this.binaryCodec = binaryCodec;
}
@Override
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
final BinaryDocValues docValues = readerContext.reader().getBinaryDocValues(fieldName);
return new FunctionValues() {
int bytesRefDoc = -1;
BytesRef bytesRef = new BytesRef();//scratch
boolean fillBytes(int doc) {
if (bytesRefDoc != doc) {
docValues.get(doc, bytesRef);
bytesRefDoc = doc;
}
return bytesRef.length != 0;
}
@Override
public boolean exists(int doc) {
return fillBytes(doc);
}
@Override
public boolean bytesVal(int doc, BytesRef target) {
if (fillBytes(doc)) {
target.bytes = bytesRef.bytes;
target.offset = bytesRef.offset;
target.length = bytesRef.length;
return true;
} else {
target.length = 0;
return false;
}
}
@Override
public Object objectVal(int docId) {
if (!fillBytes(docId))
return null;
DataInputStream dataInput = new DataInputStream(
new ByteArrayInputStream(bytesRef.bytes, bytesRef.offset, bytesRef.length));
try {
return binaryCodec.readShape(dataInput);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public Explanation explain(int doc) {
return new Explanation(Float.NaN, toString(doc));
}
@Override
public String toString(int doc) {
return description() + "=" + objectVal(doc);//TODO truncate?
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShapeDocValueSource that = (ShapeDocValueSource) o;
if (!fieldName.equals(that.fieldName)) return false;
return true;
}
@Override
public int hashCode() {
int result = fieldName.hashCode();
return result;
}
@Override
public String description() {
return "shapeDocVal(" + fieldName + ")";
}
}//ShapeDocValueSource
}

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<!--
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.
-->
<html>
<body>
Strategies that serialize the shape (non-indexed).
</body>
</html>

View File

@ -0,0 +1,118 @@
package org.apache.lucene.spatial.util;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceCalculator;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import java.io.IOException;
import java.util.Map;
/**
* 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
* is calculated via a {@link com.spatial4j.core.distance.DistanceCalculator}.
*
* @lucene.experimental
*/
public class DistanceToShapeValueSource extends ValueSource {
private final ValueSource 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
public DistanceToShapeValueSource(ValueSource 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);
}
@Override
public String description() {
return "distance(" + queryPoint + " to " + shapeValueSource.description() + ")*" + multiplier + ")";
}
@Override
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
shapeValueSource.createWeight(context, searcher);
}
@Override
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
final FunctionValues shapeValues = shapeValueSource.getValues(context, readerContext);
return new DoubleDocValues(this) {
@Override
public double doubleVal(int doc) {
Shape shape = (Shape) shapeValues.objectVal(doc);
if (shape == null || shape.isEmpty())
return nullValue;
Point pt = shape.getCenter();
return distCalc.distance(queryPoint, pt) * multiplier;
}
@Override
public Explanation explain(int doc) {
Explanation exp = super.explain(doc);
exp.addDetail(shapeValues.explain(doc));
return exp;
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DistanceToShapeValueSource that = (DistanceToShapeValueSource) o;
if (!queryPoint.equals(that.queryPoint)) return false;
if (Double.compare(that.multiplier, multiplier) != 0) return false;
if (!shapeValueSource.equals(that.shapeValueSource)) return false;
if (!distCalc.equals(that.distCalc)) return false;
return true;
}
@Override
public int hashCode() {
int result;
long temp;
result = shapeValueSource.hashCode();
result = 31 * result + queryPoint.hashCode();
temp = Double.doubleToLongBits(multiplier);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}

View File

@ -0,0 +1,109 @@
package org.apache.lucene.spatial.util;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.docvalues.BoolDocValues;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.spatial.query.SpatialOperation;
import java.io.IOException;
import java.util.Map;
/**
* A boolean ValueSource that compares a shape from a provided ValueSource with a given Shape and sees
* if it matches a given {@link SpatialOperation} (the predicate).
*
* @lucene.experimental
*/
public class ShapePredicateValueSource extends ValueSource {
private final ValueSource shapeValuesource;//the left hand side
private final SpatialOperation op;
private final Shape queryShape;//the right hand side (constant)
/**
*
* @param shapeValuesource Must yield {@link Shape} instances from it's objectVal(doc). If null
* then the result is false. This is the left-hand (indexed) side.
* @param op the predicate
* @param queryShape The shape on the right-hand (query) side.
*/
public ShapePredicateValueSource(ValueSource shapeValuesource, SpatialOperation op, Shape queryShape) {
this.shapeValuesource = shapeValuesource;
this.op = op;
this.queryShape = queryShape;
}
@Override
public String description() {
return shapeValuesource + " " + op + " " + queryShape;
}
@Override
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
shapeValuesource.createWeight(context, searcher);
}
@Override
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
final FunctionValues shapeValues = shapeValuesource.getValues(context, readerContext);
return new BoolDocValues(this) {
@Override
public boolean boolVal(int doc) {
Shape indexedShape = (Shape) shapeValues.objectVal(doc);
if (indexedShape == null)
return false;
return op.evaluate(indexedShape, queryShape);
}
@Override
public Explanation explain(int doc) {
Explanation exp = super.explain(doc);
exp.addDetail(shapeValues.explain(doc));
return exp;
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShapePredicateValueSource that = (ShapePredicateValueSource) o;
if (!shapeValuesource.equals(that.shapeValuesource)) return false;
if (!op.equals(that.op)) return false;
if (!queryShape.equals(that.queryShape)) return false;
return true;
}
@Override
public int hashCode() {
int result = shapeValuesource.hashCode();
result = 31 * result + op.hashCode();
result = 31 * result + queryShape.hashCode();
return result;
}
}

View File

@ -29,6 +29,7 @@ import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
import org.apache.lucene.spatial.vector.PointVectorStrategy;
import org.junit.Test;
@ -61,6 +62,9 @@ public class DistanceStrategyTest extends StrategyTestCase {
strategy = new BBoxStrategy(ctx, "bbox");
ctorArgs.add(new Object[]{new Param(strategy)});
strategy = new SerializedDVStrategy(ctx, "serialized");
ctorArgs.add(new Object[]{new Param(strategy)});
return ctorArgs;
}
@ -88,9 +92,12 @@ public class DistanceStrategyTest extends StrategyTestCase {
@Test
public void testDistanceOrder() throws IOException {
adoc("100", ctx.makePoint(2,1));
adoc("101", ctx.makePoint(-1,4));
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);
@ -104,6 +111,9 @@ public class DistanceStrategyTest extends StrategyTestCase {
Point p101 = ctx.makePoint(-1, 4);
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);

View File

@ -22,10 +22,12 @@ import com.spatial4j.core.shape.Shape;
import org.apache.lucene.spatial.bbox.BBoxStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
import org.apache.lucene.spatial.vector.PointVectorStrategy;
import org.apache.lucene.util.LuceneTestCase;
import org.junit.Test;
@ -40,43 +42,52 @@ public class QueryEqualsHashCodeTest extends LuceneTestCase {
@Test
public void testEqualsHashCode() {
final SpatialPrefixTree grid = new QuadPrefixTree(ctx,10);
final SpatialArgs args1 = makeArgs1();
final SpatialArgs args2 = makeArgs2();
final SpatialPrefixTree gridQuad = new QuadPrefixTree(ctx,10);
final SpatialPrefixTree gridGeohash = new GeohashPrefixTree(ctx,10);
Collection<ObjGenerator> generators = new ArrayList<ObjGenerator>();
generators.add(new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return new RecursivePrefixTreeStrategy(grid, "recursive_quad").makeQuery(args);
}
});
generators.add(new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return new TermQueryPrefixTreeStrategy(grid, "termquery_quad").makeQuery(args);
}
});
generators.add(new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return new PointVectorStrategy(ctx, "pointvector").makeQuery(args);
}
});
generators.add(new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return new BBoxStrategy(ctx, "bbox").makeQuery(args);
}
});
for (ObjGenerator generator : generators) {
testStratQueryEqualsHashcode(args1, args2, generator);
Collection<SpatialStrategy> strategies = new ArrayList<SpatialStrategy>();
strategies.add(new RecursivePrefixTreeStrategy(gridGeohash, "recursive_geohash"));
strategies.add(new TermQueryPrefixTreeStrategy(gridQuad, "termquery_quad"));
strategies.add(new PointVectorStrategy(ctx, "pointvector"));
strategies.add(new BBoxStrategy(ctx, "bbox"));
strategies.add(new SerializedDVStrategy(ctx, "serialized"));
for (SpatialStrategy strategy : strategies) {
testEqualsHashcode(strategy);
}
}
private void testStratQueryEqualsHashcode(SpatialArgs args1, SpatialArgs args2, ObjGenerator generator) {
Object first = generator.gen(args1);
private void testEqualsHashcode(final SpatialStrategy strategy) {
final SpatialArgs args1 = makeArgs1();
final SpatialArgs args2 = makeArgs2();
testEqualsHashcode(args1, args2, new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return strategy.makeQuery(args);
}
});
testEqualsHashcode(args1, args2, new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return strategy.makeFilter(args);
}
});
testEqualsHashcode(args1, args2, new ObjGenerator() {
@Override
public Object gen(SpatialArgs args) {
return strategy.makeDistanceValueSource(args.getShape().getCenter());
}
});
}
private void testEqualsHashcode(SpatialArgs args1, SpatialArgs args2, ObjGenerator generator) {
Object first;
try {
first = generator.gen(args1);
} catch (UnsupportedOperationException e) {
return;
}
if (first == null)
return;//unsupported op?
Object second = generator.gen(args1);//should be the same
assertEquals(first, second);
assertEquals(first.hashCode(), second.hashCode());

View File

@ -28,6 +28,7 @@ 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.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
@ -139,7 +140,7 @@ public abstract class StrategyTestCase extends SpatialTestCase {
public void runTestQuery(SpatialMatchConcern concern, SpatialTestQuery q) {
String msg = q.toString(); //"Query: " + q.args.toString(ctx);
SearchResults got = executeQuery(strategy.makeQuery(q.args), Math.max(100, q.ids.size()+1));
SearchResults got = executeQuery(makeQuery(q), Math.max(100, q.ids.size()+1));
if (storeShape && got.numFound > 0) {
//check stored value is there
assertNotNull(got.results.get(0).document.get(strategy.getFieldName()));
@ -183,6 +184,10 @@ public abstract class StrategyTestCase extends SpatialTestCase {
}
}
protected Query makeQuery(SpatialTestQuery q) {
return strategy.makeQuery(q.args);
}
protected void adoc(String id, String shapeStr) throws IOException, ParseException {
Shape shape = shapeStr==null ? null : ctx.readShapeFromWkt(shapeStr);
addDocument(newDoc(id, shape));

View File

@ -0,0 +1,76 @@
package org.apache.lucene.spatial.serialized;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.spatial.SpatialMatchConcern;
import org.apache.lucene.spatial.SpatialTestQuery;
import org.apache.lucene.spatial.StrategyTestCase;
import org.apache.lucene.spatial.bbox.BBoxStrategy;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
public class SerializedStrategyTest extends StrategyTestCase {
@Before
@Override
public void setUp() throws Exception {
super.setUp();
this.ctx = SpatialContext.GEO;
this.strategy = new SerializedDVStrategy(ctx, "serialized");
}
//called by StrategyTestCase; we can't let it call our makeQuery which will UOE ex.
@Override
protected Query makeQuery(SpatialTestQuery q) {
return new FilteredQuery(new MatchAllDocsQuery(), strategy.makeFilter(q.args),
FilteredQuery.QUERY_FIRST_FILTER_STRATEGY);
}
@Test
public void testBasicOperaions() throws IOException {
getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);
executeQueries(SpatialMatchConcern.EXACT, QTEST_Simple_Queries_BBox);
}
@Test
public void testStatesBBox() throws IOException {
getAddAndVerifyIndexedDocuments(DATA_STATES_BBOX);
executeQueries(SpatialMatchConcern.FILTER, QTEST_States_IsWithin_BBox);
executeQueries(SpatialMatchConcern.FILTER, QTEST_States_Intersects_BBox);
}
@Test
public void testCitiesIntersectsBBox() throws IOException {
getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_Intersects_BBox);
}
//sorting is tested in DistanceStrategyTest
}