mirror of https://github.com/apache/lucene.git
LUCENE-4157 Ported the Solr 3 spatial test to Lucene spatial. Did a few minor other things too.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1356081 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8356f38ad5
commit
132175856f
|
@ -25,6 +25,11 @@ import java.util.*;
|
||||||
/**
|
/**
|
||||||
* A clause that compares a stored geometry to a supplied geometry.
|
* A clause that compares a stored geometry to a supplied geometry.
|
||||||
*
|
*
|
||||||
|
* @see <a href="http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm">
|
||||||
|
* ESRI's docs on spatial relations</a>
|
||||||
|
* @see <a href="http://docs.geoserver.org/latest/en/user/filter/ecql_reference.html#spatial-predicate">
|
||||||
|
* GeoServer ECQL Spatial Predicates</a>
|
||||||
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public class SpatialOperation implements Serializable {
|
public class SpatialOperation implements Serializable {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
package org.apache.lucene.spatial.vector;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
@ -15,11 +17,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.spatial.vector;
|
|
||||||
|
|
||||||
import com.spatial4j.core.distance.DistanceCalculator;
|
import com.spatial4j.core.distance.DistanceCalculator;
|
||||||
import com.spatial4j.core.shape.Point;
|
import com.spatial4j.core.shape.Point;
|
||||||
import com.spatial4j.core.shape.simple.PointImpl;
|
|
||||||
import org.apache.lucene.index.AtomicReader;
|
import org.apache.lucene.index.AtomicReader;
|
||||||
import org.apache.lucene.index.AtomicReaderContext;
|
import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.queries.function.FunctionValues;
|
import org.apache.lucene.queries.function.FunctionValues;
|
||||||
|
@ -84,8 +83,7 @@ public class DistanceValueSource extends ValueSource {
|
||||||
public double doubleVal(int doc) {
|
public double doubleVal(int doc) {
|
||||||
// make sure it has minX and area
|
// make sure it has minX and area
|
||||||
if (validX.get(doc) && validY.get(doc)) {
|
if (validX.get(doc) && validY.get(doc)) {
|
||||||
PointImpl pt = new PointImpl( ptX[doc], ptY[doc] );
|
return calculator.distance(from, ptX[doc], ptY[doc]);
|
||||||
return calculator.distance(from, pt);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
package org.apache.lucene.spatial;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||||
|
import com.spatial4j.core.context.SpatialContext;
|
||||||
|
import com.spatial4j.core.context.simple.SimpleSpatialContext;
|
||||||
|
import com.spatial4j.core.shape.Point;
|
||||||
|
import com.spatial4j.core.shape.Shape;
|
||||||
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.document.Field;
|
||||||
|
import org.apache.lucene.document.StringField;
|
||||||
|
import org.apache.lucene.index.IndexableField;
|
||||||
|
import org.apache.lucene.search.FilteredQuery;
|
||||||
|
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||||
|
import org.apache.lucene.search.Query;
|
||||||
|
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.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based off of Solr 3's SpatialFilterTest.
|
||||||
|
* @author dsmiley
|
||||||
|
*/
|
||||||
|
public class PortedSolr3Test extends StrategyTestCase {
|
||||||
|
|
||||||
|
@ParametersFactory
|
||||||
|
public static Iterable<Object[]> parameters() {
|
||||||
|
List<Object[]> ctorArgs = new ArrayList<Object[]>();
|
||||||
|
|
||||||
|
SpatialContext ctx = SimpleSpatialContext.GEO_KM;
|
||||||
|
SpatialPrefixTree grid;
|
||||||
|
SpatialStrategy strategy;
|
||||||
|
|
||||||
|
grid = new GeohashPrefixTree(ctx,12);
|
||||||
|
strategy = new RecursivePrefixTreeStrategy(grid);
|
||||||
|
ctorArgs.add(new Object[]{"recursive_geohash",strategy});
|
||||||
|
|
||||||
|
grid = new QuadPrefixTree(ctx,25);
|
||||||
|
strategy = new RecursivePrefixTreeStrategy(grid);
|
||||||
|
ctorArgs.add(new Object[]{"recursive_quad",strategy});
|
||||||
|
|
||||||
|
grid = new GeohashPrefixTree(ctx,12);
|
||||||
|
strategy = new TermQueryPrefixTreeStrategy(grid);
|
||||||
|
ctorArgs.add(new Object[]{"termquery_geohash",strategy});
|
||||||
|
|
||||||
|
return ctorArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private String fieldName;
|
||||||
|
|
||||||
|
public PortedSolr3Test(String fieldName, SpatialStrategy strategy) {
|
||||||
|
ctx = strategy.getSpatialContext();
|
||||||
|
this.strategy = strategy;
|
||||||
|
// this.fieldName = fieldName;
|
||||||
|
fieldInfo = new SimpleSpatialFieldInfo( fieldName );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupDocs() throws IOException {
|
||||||
|
super.deleteAll();
|
||||||
|
adoc("1", "32.7693246, -79.9289094");
|
||||||
|
adoc("2", "33.7693246, -80.9289094");
|
||||||
|
adoc("3", "-32.7693246, 50.9289094");
|
||||||
|
adoc("4", "-50.7693246, 60.9289094");
|
||||||
|
adoc("5", "0,0");
|
||||||
|
adoc("6", "0.1,0.1");
|
||||||
|
adoc("7", "-0.1,-0.1");
|
||||||
|
adoc("8", "0,179.9");
|
||||||
|
adoc("9", "0,-179.9");
|
||||||
|
adoc("10", "89.9,50");
|
||||||
|
adoc("11", "89.9,-130");
|
||||||
|
adoc("12", "-89.9,50");
|
||||||
|
adoc("13", "-89.9,-130");
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntersections() throws Exception {
|
||||||
|
setupDocs();
|
||||||
|
//Try some edge cases
|
||||||
|
checkHitsCircle("1,1", 175, 3, 5, 6, 7);
|
||||||
|
checkHitsCircle("0,179.8", 200, 2, 8, 9);
|
||||||
|
checkHitsCircle("89.8, 50", 200, 2, 10, 11);//this goes over the north pole
|
||||||
|
checkHitsCircle("-89.8, 50", 200, 2, 12, 13);//this goes over the south pole
|
||||||
|
//try some normal cases
|
||||||
|
checkHitsCircle("33.0,-80.0", 300, 2);
|
||||||
|
//large distance
|
||||||
|
checkHitsCircle("1,1", 5000, 3, 5, 6, 7);
|
||||||
|
//Because we are generating a box based on the west/east longitudes and the south/north latitudes, which then
|
||||||
|
//translates to a range query, which is slightly more inclusive. Thus, even though 0.0 is 15.725 kms away,
|
||||||
|
//it will be included, b/c of the box calculation.
|
||||||
|
checkHitsBBox("0.1,0.1", 15, 2, 5, 6);
|
||||||
|
//try some more
|
||||||
|
deleteAll();
|
||||||
|
adoc("14", "0,5");
|
||||||
|
adoc("15", "0,15");
|
||||||
|
//3000KM from 0,0, see http://www.movable-type.co.uk/scripts/latlong.html
|
||||||
|
adoc("16", "18.71111,19.79750");
|
||||||
|
adoc("17", "44.043900,-95.436643");
|
||||||
|
commit();
|
||||||
|
|
||||||
|
checkHitsCircle("0,0", 1000, 1, 14);
|
||||||
|
checkHitsCircle("0,0", 2000, 2, 14, 15);
|
||||||
|
checkHitsBBox("0,0", 3000, 3, 14, 15, 16);
|
||||||
|
checkHitsCircle("0,0", 3001, 3, 14, 15, 16);
|
||||||
|
checkHitsCircle("0,0", 3000.1, 3, 14, 15, 16);
|
||||||
|
|
||||||
|
//really fine grained distance and reflects some of the vagaries of how we are calculating the box
|
||||||
|
checkHitsCircle("43.517030,-96.789603", 109, 0);
|
||||||
|
|
||||||
|
// falls outside of the real distance, but inside the bounding box
|
||||||
|
checkHitsCircle("43.517030,-96.789603", 110, 0);
|
||||||
|
checkHitsBBox("43.517030,-96.789603", 110, 1, 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test is similar to a Solr 3 spatial test.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDistanceOrder() throws IOException {
|
||||||
|
adoc("100","1,2");
|
||||||
|
adoc("101","4,-1");
|
||||||
|
commit();
|
||||||
|
|
||||||
|
//query closer to #100
|
||||||
|
checkHitsOrdered("Intersects(Circle(3,4 d=1000))", "101", "100");
|
||||||
|
//query closer to #101
|
||||||
|
checkHitsOrdered("Intersects(Circle(4,0 d=1000))", "100", "101");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkHitsOrdered(String spatialQ, String... ids) {
|
||||||
|
SpatialArgs args = this.argsParser.parse(spatialQ,ctx);
|
||||||
|
Query query = strategy.makeQuery(args, fieldInfo);
|
||||||
|
SearchResults results = executeQuery(query, 100);
|
||||||
|
String[] resultIds = new String[results.numFound];
|
||||||
|
int i = 0;
|
||||||
|
for (SearchResult result : results.results) {
|
||||||
|
resultIds[i++] = result.document.get("id");
|
||||||
|
}
|
||||||
|
assertArrayEquals("order matters",ids, resultIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- these are similar to Solr test methods
|
||||||
|
|
||||||
|
private void adoc(String idStr, String shapeStr) throws IOException {
|
||||||
|
Shape shape = ctx.readShape(shapeStr);
|
||||||
|
addDocument(newDoc(idStr,shape));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Document newDoc(String id, Shape shape) {
|
||||||
|
Document doc = new Document();
|
||||||
|
doc.add(new StringField("id", id, Field.Store.YES));
|
||||||
|
for (IndexableField f : strategy.createFields(fieldInfo, shape, true, storeShape)) {
|
||||||
|
doc.add(f);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkHitsCircle(String ptStr, double dist, int assertNumFound, int... assertIds) {
|
||||||
|
_checkHits(SpatialOperation.Intersects, ptStr, dist, assertNumFound, assertIds);
|
||||||
|
}
|
||||||
|
private void checkHitsBBox(String ptStr, double dist, int assertNumFound, int... assertIds) {
|
||||||
|
_checkHits(SpatialOperation.BBoxIntersects, ptStr, dist, assertNumFound, assertIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void _checkHits(SpatialOperation op, String ptStr, double dist, int assertNumFound, int... assertIds) {
|
||||||
|
Point pt = (Point) ctx.readShape(ptStr);
|
||||||
|
Shape shape = ctx.makeCircle(pt,dist);
|
||||||
|
|
||||||
|
SpatialArgs args = new SpatialArgs(op,shape);
|
||||||
|
//args.setDistPrecision(0.025);
|
||||||
|
Query query;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
query = strategy.makeQuery(args, fieldInfo);
|
||||||
|
} else {
|
||||||
|
query = new FilteredQuery(new MatchAllDocsQuery(),strategy.makeFilter(args, fieldInfo));
|
||||||
|
}
|
||||||
|
SearchResults results = executeQuery(query, 100);
|
||||||
|
assertEquals(""+shape,assertNumFound,results.numFound);
|
||||||
|
if (assertIds != null) {
|
||||||
|
Set<Integer> resultIds = new HashSet<Integer>();
|
||||||
|
for (SearchResult result : results.results) {
|
||||||
|
resultIds.add(Integer.valueOf(result.document.get("id")));
|
||||||
|
}
|
||||||
|
for (int assertId : assertIds) {
|
||||||
|
assertTrue("has " + assertId, resultIds.contains(assertId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.prefix;
|
|
||||||
|
|
||||||
import org.apache.lucene.util.LuceneTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is just a quick idea for *simple* tests
|
|
||||||
*/
|
|
||||||
public class TestSpatialPrefixField extends LuceneTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRawTokens() {
|
|
||||||
// Ignoring geometry for now, and focus on what tokens need to match
|
|
||||||
|
|
||||||
List<String> docA = Arrays.asList(
|
|
||||||
"AAAAAA*",
|
|
||||||
"AAAAAB+"
|
|
||||||
);
|
|
||||||
|
|
||||||
List<String> docB = Arrays.asList(
|
|
||||||
"A*",
|
|
||||||
"BB*"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Assumptions:
|
|
||||||
checkQuery("AAAAA", "docA", "docB");
|
|
||||||
checkQuery("AAAAA*", "docA", "docB"); // for now * and + are essentially identical
|
|
||||||
checkQuery("AAAAA+", "docA", "docB"); // down the road, there may be a difference between 'covers' and an edge
|
|
||||||
|
|
||||||
checkQuery("AA*", "docB", "docA"); // Bigger input query
|
|
||||||
|
|
||||||
checkQuery("AAAAAAAAAAAA*", "docA", "docB"); // small
|
|
||||||
|
|
||||||
checkQuery("BC"); // nothing
|
|
||||||
checkQuery("XX"); // nothing
|
|
||||||
|
|
||||||
// match only B
|
|
||||||
checkQuery("B", "docB");
|
|
||||||
checkQuery("BBBB", "docB");
|
|
||||||
checkQuery("B*", "docB");
|
|
||||||
checkQuery("BBBB*", "docB");
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkQuery(String query, String... expect) {
|
|
||||||
// TODO, check that the query returns the docs in order
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue