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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -15,11 +17,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import com.spatial4j.core.distance.DistanceCalculator;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.simple.PointImpl;
|
||||
import org.apache.lucene.index.AtomicReader;
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
|
@ -84,8 +83,7 @@ public class DistanceValueSource extends ValueSource {
|
|||
public double doubleVal(int doc) {
|
||||
// make sure it has minX and area
|
||||
if (validX.get(doc) && validY.get(doc)) {
|
||||
PointImpl pt = new PointImpl( ptX[doc], ptY[doc] );
|
||||
return calculator.distance(from, pt);
|
||||
return calculator.distance(from, ptX[doc], ptY[doc]);
|
||||
}
|
||||
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