blob: 2b23a656eaecc96fce6088926a2fbe99fd476831 [file] [log] [blame]
/*
* 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.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
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.search.DoubleValues;
import org.apache.lucene.search.DoubleValuesSource;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
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 {
public static final String DATA_SIMPLE_BBOX = "simple-bbox.txt";
public static final String DATA_STATES_POLY = "states-poly.txt";
public static final String DATA_STATES_BBOX = "states-bbox.txt";
public static final String DATA_COUNTRIES_POLY = "countries-poly.txt";
public static final String DATA_COUNTRIES_BBOX = "countries-bbox.txt";
public static final String DATA_WORLD_CITIES_POINTS = "world-cities-points.txt";
public static final String QTEST_States_IsWithin_BBox = "states-IsWithin-BBox.txt";
public static final String QTEST_States_Intersects_BBox = "states-Intersects-BBox.txt";
public static final String QTEST_Cities_Intersects_BBox = "cities-Intersects-BBox.txt";
public static final String QTEST_Simple_Queries_BBox = "simple-Queries-BBox.txt";
protected Logger log = Logger.getLogger(getClass().getName());
protected final SpatialArgsParser argsParser = new SpatialArgsParser();
protected SpatialStrategy strategy;
protected boolean storeShape = true;
protected void executeQueries(SpatialMatchConcern concern, String... testQueryFile) throws IOException {
log.info("testing queried for strategy "+strategy); // nowarn
for( String path : testQueryFile ) {
Iterator<SpatialTestQuery> testQueryIterator = getTestQueries(path, ctx);
runTestQueries(testQueryIterator, concern);
}
}
protected void getAddAndVerifyIndexedDocuments(String testDataFile) throws IOException {
List<Document> testDocuments = getDocuments(testDataFile);
addDocumentsAndCommit(testDocuments);
verifyDocumentsIndexed(testDocuments.size());
}
protected List<Document> getDocuments(String testDataFile) throws IOException {
return getDocuments(getSampleData(testDataFile));
}
protected List<Document> getDocuments(Iterator<SpatialTestData> sampleData) {
List<Document> documents = new ArrayList<>();
while (sampleData.hasNext()) {
SpatialTestData data = sampleData.next();
Document document = new Document();
document.add(new StringField("id", data.id, Field.Store.YES));
document.add(new StringField("name", data.name, Field.Store.YES));
Shape shape = data.shape;
shape = convertShapeFromGetDocuments(shape);
if (shape != null) {
for (Field f : strategy.createIndexableFields(shape)) {
document.add(f);
}
if (storeShape)//just for diagnostics
document.add(new StoredField(strategy.getFieldName(), shape.toString()));
}
documents.add(document);
}
return documents;
}
/** Subclasses may override to transform or remove a shape for indexing */
protected Shape convertShapeFromGetDocuments(Shape shape) {
return shape;
}
protected Iterator<SpatialTestData> getSampleData(String testDataFile) throws IOException {
String path = "data/" + testDataFile;
InputStream stream = getClass().getClassLoader().getResourceAsStream(path);
if (stream == null)
throw new FileNotFoundException("classpath resource not found: "+path);
return SpatialTestData.getTestData(stream, ctx);//closes the InputStream
}
protected Iterator<SpatialTestQuery> getTestQueries(String testQueryFile, SpatialContext ctx) throws IOException {
InputStream in = getClass().getClassLoader().getResourceAsStream(testQueryFile);
return SpatialTestQuery.getTestQueries(
argsParser, ctx, testQueryFile, in );//closes the InputStream
}
public void runTestQueries(
Iterator<SpatialTestQuery> queries,
SpatialMatchConcern concern) {
while (queries.hasNext()) {
SpatialTestQuery q = queries.next();
runTestQuery(concern, q);
}
}
public void runTestQuery(SpatialMatchConcern concern, SpatialTestQuery q) {
String msg = q.toString(); //"Query: " + q.args.toString(ctx);
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()));
}
if (concern.orderIsImportant) {
Iterator<String> ids = q.ids.iterator();
for (SearchResult r : got.results) {
String id = r.document.get("id");
if (!ids.hasNext()) {
fail(msg + " :: Did not get enough results. Expect" + q.ids + ", got: " + got.toDebugString());
}
assertEquals("out of order: " + msg, ids.next(), id);
}
if (ids.hasNext()) {
fail(msg + " :: expect more results then we got: " + ids.next());
}
} else {
// We are looking at how the results overlap
if (concern.resultsAreSuperset) {
Set<String> found = new HashSet<>();
for (SearchResult r : got.results) {
found.add(r.document.get("id"));
}
for (String s : q.ids) {
if (!found.contains(s)) {
fail("Results are mising id: " + s + " :: " + found);
}
}
} else {
List<String> found = new ArrayList<>();
for (SearchResult r : got.results) {
found.add(r.document.get("id"));
}
// sort both so that the order is not important
Collections.sort(q.ids);
Collections.sort(found);
assertEquals(msg, q.ids.toString(), found.toString());
}
}
}
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));
}
protected void adoc(String id, Shape shape) throws IOException {
addDocument(newDoc(id, shape));
}
protected Document newDoc(String id, Shape shape) {
Document doc = new Document();
doc.add(new StringField("id", id, Field.Store.YES));
if (shape != null) {
for (Field f : strategy.createIndexableFields(shape)) {
doc.add(f);
}
if (storeShape)
doc.add(new StoredField(strategy.getFieldName(), shape.toString()));//not to be parsed; just for debug
}
return doc;
}
protected void deleteDoc(String id) throws IOException {
indexWriter.deleteDocuments(new TermQuery(new Term("id", id)));
}
/** scores[] are in docId order */
protected void checkValueSource(DoubleValuesSource vs, float scores[], float delta) throws IOException {
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);
}
}
}
protected void testOperation(Shape indexedShape, SpatialOperation operation,
Shape queryShape, boolean match) throws IOException {
assertTrue("Faulty test",
operation.evaluate(indexedShape, queryShape) == match ||
indexedShape.equals(queryShape) &&
(operation == SpatialOperation.Contains || operation == SpatialOperation.IsWithin));
adoc("0", indexedShape);
commit();
Query query = strategy.makeQuery(new SpatialArgs(operation, queryShape));
SearchResults got = executeQuery(query, 1);
assert got.numFound <= 1 : "unclean test env";
if ((got.numFound == 1) != match)
fail(operation+" I:" + indexedShape + " Q:" + queryShape);
deleteAll();//clean up after ourselves
}
}