blob: 537f62c677a0b8c2967404d3154da05000718624 [file] [log] [blame]
Index: lucene/spatial/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java (revision )
+++ lucene/spatial/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java (revision )
@@ -0,0 +1,213 @@
+package org.apache.lucene.spatial.composite;
+
+/*
+ * 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 java.io.IOException;
+import java.util.Map;
+
+import com.spatial4j.core.shape.Shape;
+import com.spatial4j.core.shape.SpatialRelation;
+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.ConstantScoreWeight;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+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.prefix.AbstractVisitingPrefixTreeFilter;
+import org.apache.lucene.spatial.prefix.tree.Cell;
+import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
+import org.apache.lucene.util.BitDocIdSet;
+import org.apache.lucene.util.Bits;
+
+/**
+ * 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)}.
+ *
+ * @lucene.internal
+ */
+public class IntersectsRPTVerifyQuery extends Query {
+
+ private final IntersectsDifferentiatingFilter intersectsDiffFilter;
+ private final ValueSource predicateValueSource; // we call FunctionValues.boolVal(doc)
+
+ public IntersectsRPTVerifyQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel,
+ int prefixGridScanLevel, ValueSource predicateValueSource) {
+ this.predicateValueSource = predicateValueSource;
+ this.intersectsDiffFilter = new IntersectsDifferentiatingFilter(queryShape, fieldName, grid, detailLevel,
+ prefixGridScanLevel);
+ }
+
+ @Override
+ public String toString(String field) {
+ return "IntersectsVerified(fieldName=" + field + ")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof IntersectsRPTVerifyQuery)) return false;
+ if (!super.equals(o)) return false;
+
+ IntersectsRPTVerifyQuery that = (IntersectsRPTVerifyQuery) o;
+
+ if (!intersectsDiffFilter.equals(that.intersectsDiffFilter)) return false;
+ return predicateValueSource.equals(that.predicateValueSource);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + intersectsDiffFilter.hashCode();
+ result = 31 * result + predicateValueSource.hashCode();
+ return result;
+ }
+
+ @Override
+ public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+ final Map valueSourceContext = ValueSource.newContext(searcher);
+
+ return new ConstantScoreWeight(this) {
+ @Override
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ // Compute approx & exact
+ final IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor result =
+ intersectsDiffFilter.compute(context, acceptDocs);
+ if (result.approxDocIdSet == null) {
+ return null;
+ }
+ final DocIdSetIterator approxDISI = result.approxDocIdSet.iterator();
+ if (approxDISI == null) {
+ return null;
+ }
+ final Bits exactDocBits;
+ if (result.exactDocIdSet != null) {
+ // If both sets are the same, there's nothing to verify; we needn't return a TwoPhaseIterator
+ if (result.approxDocIdSet.equals(result.exactDocIdSet)) {
+ return new ConstantScoreScorer(this, score, approxDISI);
+ }
+ exactDocBits = result.exactDocIdSet.bits();
+ assert exactDocBits != null;
+ } else {
+ exactDocBits = null;
+ }
+
+ final FunctionValues predFuncValues = predicateValueSource.getValues(valueSourceContext, context);
+
+ final TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(approxDISI) {
+ @Override
+ public boolean matches() throws IOException {
+ if (exactDocBits != null && exactDocBits.get(approxDISI.docID())) {
+ return true;
+ }
+
+ return predFuncValues.boolVal(approxDISI.docID());
+ }
+ };
+
+ return new ConstantScoreScorer(this, score, twoPhaseIterator);
+ }
+ };
+ }
+
+ //This is a "Filter" but we don't use it as-such; the caller calls the constructor and then compute() and examines
+ // the results which consists of two parts -- the approximated results, and a subset of exact matches. The
+ // difference needs to be verified.
+ // TODO refactor AVPTF to not be a Query/Filter?
+ private static class IntersectsDifferentiatingFilter extends AbstractVisitingPrefixTreeFilter {
+
+ public IntersectsDifferentiatingFilter(Shape queryShape, String fieldName, SpatialPrefixTree grid,
+ int detailLevel, int prefixGridScanLevel) {
+ super(queryShape, fieldName, grid, detailLevel, prefixGridScanLevel);
+ }
+
+ IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor compute(LeafReaderContext context, Bits acceptDocs) throws IOException {
+ final IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor result = new IntersectsDifferentiatingFilter.IntersectsDifferentiatingVisitor(context, acceptDocs);
+ result.getDocIdSet();//computes
+ return result;
+ }
+
+ // TODO consider if IntersectsPrefixTreeFilter should simply do this and provide both sets
+
+ class IntersectsDifferentiatingVisitor extends VisitorTemplate {
+ BitDocIdSet.Builder approxBuilder = new BitDocIdSet.Builder(maxDoc);
+ BitDocIdSet.Builder exactBuilder = new BitDocIdSet.Builder(maxDoc);
+ BitDocIdSet exactDocIdSet;
+ BitDocIdSet approxDocIdSet;
+
+ public IntersectsDifferentiatingVisitor(LeafReaderContext context, Bits acceptDocs) throws IOException {
+ super(context, acceptDocs);
+ }
+
+ @Override
+ protected void start() throws IOException {
+ }
+
+ @Override
+ protected DocIdSet finish() throws IOException {
+ exactDocIdSet = exactBuilder.build();
+ if (approxBuilder.isDefinitelyEmpty()) {
+ approxDocIdSet = exactDocIdSet;//optimization
+ } else {
+ if (exactDocIdSet != null) {
+ approxBuilder.or(exactDocIdSet.iterator());
+ }
+ approxDocIdSet = approxBuilder.build();
+ }
+ return null;//unused in this weird re-use of AVPTF
+ }
+
+ @Override
+ protected boolean visitPrefix(Cell cell) throws IOException {
+ if (cell.getShapeRel() == SpatialRelation.WITHIN) {
+ collectDocs(exactBuilder);//note: we'll add exact to approx on finish()
+ return false;
+ } else if (cell.getLevel() == detailLevel) {
+ collectDocs(approxBuilder);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected void visitLeaf(Cell cell) throws IOException {
+ if (cell.getShapeRel() == SpatialRelation.WITHIN) {
+ collectDocs(exactBuilder);//note: we'll add exact to approx on finish()
+ } else {
+ collectDocs(approxBuilder);
+ }
+ }
+ }
+
+ @Override
+ public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public String toString(String field) {
+ throw new IllegalStateException();
+ }
+ }
+}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/composite/package-info.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/composite/package-info.java (revision )
+++ lucene/spatial/src/java/org/apache/lucene/spatial/composite/package-info.java (revision )
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/** Composite strategies. */
+package org.apache.lucene.spatial.composite;
\ No newline at end of file
Index: lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java (revision 1671637)
+++ lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java (revision )
@@ -33,6 +33,7 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
+import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.StringField;
@@ -52,8 +53,6 @@
import org.apache.lucene.util.RamUsageTester;
import org.apache.lucene.util.TestUtil;
-import com.carrotsearch.randomizedtesting.generators.RandomPicks;
-
public class TestLRUQueryCache extends LuceneTestCase {
private static final QueryCachingPolicy NEVER_CACHE = new QueryCachingPolicy() {
@@ -349,7 +348,7 @@
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
return new ConstantScoreWeight(this) {
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
return null;
}
};
@@ -925,7 +924,7 @@
return new ConstantScoreWeight(this) {
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
return null;
}
};
Index: lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/search/LRUQueryCache.java (revision )
@@ -547,7 +547,7 @@
}
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
if (context.ord == 0) {
policy.onUse(getQuery());
}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeFilter.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeFilter.java (revision 1671637)
+++ lucene/spatial/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeFilter.java (revision )
@@ -20,13 +20,14 @@
import java.io.IOException;
import com.spatial4j.core.shape.Shape;
-import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
+import org.apache.lucene.util.BitDocIdSet;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.Bits;
@@ -98,6 +99,11 @@
bitSet.or(postingsEnum);
}
+ protected void collectDocs(BitDocIdSet.Builder bitSetBuilder) throws IOException {
+ assert termsEnum != null;
+ postingsEnum = termsEnum.postings(acceptDocs, postingsEnum, PostingsEnum.NONE);
+ bitSetBuilder.or(postingsEnum);
+ }
}
}
Index: lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java (revision )
@@ -87,7 +87,7 @@
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
return new ConstantScoreWeight(this) {
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
final Terms terms = context.reader().terms(query.field);
if (terms == null) {
// field does not exist
Index: lucene/core/src/java/org/apache/lucene/util/BitDocIdSet.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/util/BitDocIdSet.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/util/BitDocIdSet.java (revision )
@@ -107,6 +107,15 @@
}
/**
+ * Is this builder definitely empty? If so, {@link #build()} will return null. This is usually the same as
+ * simply being empty but if this builder was constructed with the {@code full} option or if an iterator was passed
+ * that iterated over no documents, then we're not sure.
+ */
+ public boolean isDefinitelyEmpty() {
+ return sparseSet == null && denseSet == null;
+ }
+
+ /**
* Add the content of the provided {@link DocIdSetIterator} to this builder.
*/
public void or(DocIdSetIterator it) throws IOException {
Index: lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/search/ConstantScoreWeight.java (revision )
@@ -22,7 +22,10 @@
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.util.Bits;
-abstract class ConstantScoreWeight extends Weight {
+/**
+ * A Weight that has a constant score equal to the boost of the wrapped query.
+ */
+public abstract class ConstantScoreWeight extends Weight {
private float queryNorm;
private float queryWeight;
@@ -68,6 +71,6 @@
return scorer(context, acceptDocs, queryWeight);
}
- abstract Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException;
+ protected abstract Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException;
}
Index: lucene/spatial/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java (revision 1671637)
+++ lucene/spatial/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java (revision )
@@ -17,9 +17,13 @@
* limitations under the License.
*/
+import java.util.ArrayList;
+import java.util.Collection;
+
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.spatial.bbox.BBoxStrategy;
+import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
@@ -32,25 +36,33 @@
import org.apache.lucene.util.LuceneTestCase;
import org.junit.Test;
-import java.util.ArrayList;
-import java.util.Collection;
-
public class QueryEqualsHashCodeTest extends LuceneTestCase {
private final SpatialContext ctx = SpatialContext.GEO;
+ private SpatialOperation predicate;
+
@Test
public void testEqualsHashCode() {
+ switch(random().nextInt(4)) {//0-3
+ case 0: predicate = SpatialOperation.Contains; break;
+ case 1: predicate = SpatialOperation.IsWithin; break;
+
+ default: predicate = SpatialOperation.Intersects; break;
+ }
final SpatialPrefixTree gridQuad = new QuadPrefixTree(ctx,10);
final SpatialPrefixTree gridGeohash = new GeohashPrefixTree(ctx,10);
Collection<SpatialStrategy> strategies = new ArrayList<>();
- strategies.add(new RecursivePrefixTreeStrategy(gridGeohash, "recursive_geohash"));
+ RecursivePrefixTreeStrategy recursive_geohash = new RecursivePrefixTreeStrategy(gridGeohash, "recursive_geohash");
+ strategies.add(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"));
+ final SerializedDVStrategy serialized = new SerializedDVStrategy(ctx, "serialized");
+ strategies.add(serialized);
+ strategies.add(new CompositeSpatialStrategy("composite", recursive_geohash, serialized));
for (SpatialStrategy strategy : strategies) {
testEqualsHashcode(strategy);
}
@@ -91,20 +103,20 @@
Object second = generator.gen(args1);//should be the same
assertEquals(first, second);
assertEquals(first.hashCode(), second.hashCode());
- assertNotSame(args1, args2);
+ assertTrue(args1.equals(args2) == false);
second = generator.gen(args2);//now should be different
- assertNotSame(first, second);
- assertNotSame(first.hashCode(), second.hashCode());
+ assertTrue(first.equals(second) == false);
+ assertTrue(first.hashCode() != second.hashCode());
}
private SpatialArgs makeArgs1() {
final Shape shape1 = ctx.makeRectangle(0, 0, 10, 10);
- return new SpatialArgs(SpatialOperation.Intersects, shape1);
+ return new SpatialArgs(predicate, shape1);
}
private SpatialArgs makeArgs2() {
final Shape shape2 = ctx.makeRectangle(0, 0, 20, 20);
- return new SpatialArgs(SpatialOperation.Intersects, shape2);
+ return new SpatialArgs(predicate, shape2);
}
interface ObjGenerator {
Index: lucene/spatial/src/java/org/apache/lucene/spatial/composite/ConstantScoreScorer.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/composite/ConstantScoreScorer.java (revision )
+++ lucene/spatial/src/java/org/apache/lucene/spatial/composite/ConstantScoreScorer.java (revision )
@@ -0,0 +1,90 @@
+package org.apache.lucene.spatial.composite;
+
+/*
+ * 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 java.io.IOException;
+
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TwoPhaseIterator;
+import org.apache.lucene.search.Weight;
+
+/**
+ * A constant-scoring {@link Scorer}.
+ *
+ * @lucene.internal
+ */
+public final class ConstantScoreScorer extends Scorer {
+ // TODO refactor CSQ's Scorer to be re-usable and look like this
+
+ private final Weight weight;
+ private final float score;
+ private final TwoPhaseIterator twoPhaseIterator;
+ private final DocIdSetIterator disi;
+
+ public ConstantScoreScorer(Weight weight, float score, DocIdSetIterator disi) {
+ super(weight);
+ this.weight = weight;
+ this.score = score;
+ this.twoPhaseIterator = null;
+ this.disi = disi;
+ }
+
+ protected ConstantScoreScorer(Weight weight, float score, TwoPhaseIterator twoPhaseIterator) {
+ super(weight);
+ this.weight = weight;
+ this.score = score;
+ this.twoPhaseIterator = twoPhaseIterator;
+ this.disi = TwoPhaseIterator.asDocIdSetIterator(twoPhaseIterator);
+ }
+
+ @Override
+ public TwoPhaseIterator asTwoPhaseIterator() {
+ return twoPhaseIterator;
+ }
+
+ @Override
+ public float score() throws IOException {
+ return score;
+ }
+
+ @Override
+ public int freq() throws IOException {
+ return 1;
+ }
+
+ @Override
+ public int docID() {
+ return disi.docID();
+ }
+
+ @Override
+ public int nextDoc() throws IOException {
+ return disi.nextDoc();
+ }
+
+ @Override
+ public int advance(int target) throws IOException {
+ return disi.advance(target);
+ }
+
+ @Override
+ public long cost() {
+ return disi.cost();
+ }
+}
Index: lucene/spatial/src/test/org/apache/lucene/spatial/prefix/RandomSpatialOpStrategyTestCase.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/test/org/apache/lucene/spatial/prefix/RandomSpatialOpStrategyTestCase.java (revision 1671637)
+++ lucene/spatial/src/test/org/apache/lucene/spatial/prefix/RandomSpatialOpStrategyTestCase.java (revision )
@@ -17,18 +17,18 @@
* limitations under the License.
*/
-import com.spatial4j.core.shape.Shape;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.StrategyTestCase;
-import org.apache.lucene.spatial.query.SpatialArgs;
-import org.apache.lucene.spatial.query.SpatialOperation;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import com.spatial4j.core.shape.Shape;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.StrategyTestCase;
+import org.apache.lucene.spatial.query.SpatialArgs;
+import org.apache.lucene.spatial.query.SpatialOperation;
+
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomInt;
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween;
@@ -40,12 +40,6 @@
//Note: this is partially redundant with StrategyTestCase.runTestQuery & testOperation
protected void testOperationRandomShapes(final SpatialOperation operation) throws IOException {
- //first show that when there's no data, a query will result in no results
- {
- Query query = strategy.makeQuery(new SpatialArgs(operation, randomQueryShape()));
- SearchResults searchResults = executeQuery(query, 1);
- assertEquals(0, searchResults.numFound);
- }
final int numIndexedShapes = randomIntBetween(1, 6);
List<Shape> indexedShapes = new ArrayList<>(numIndexedShapes);
@@ -64,6 +58,13 @@
protected void testOperation(final SpatialOperation operation,
List<Shape> indexedShapes, List<Shape> queryShapes, boolean havoc) throws IOException {
+ //first show that when there's no data, a query will result in no results
+ {
+ Query query = strategy.makeQuery(new SpatialArgs(operation, randomQueryShape()));
+ SearchResults searchResults = executeQuery(query, 1);
+ assertEquals(0, searchResults.numFound);
+ }
+
//Main index loop:
for (int i = 0; i < indexedShapes.size(); i++) {
Shape shape = indexedShapes.get(i);
Index: lucene/core/src/java/org/apache/lucene/search/DocValuesTermsQuery.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocValuesTermsQuery.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/search/DocValuesTermsQuery.java (revision )
@@ -149,7 +149,7 @@
return new ConstantScoreWeight(this) {
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
final SortedSetDocValues values = DocValues.getSortedSet(context.reader(), field);
final LongBitSet bits = new LongBitSet(values.getValueCount());
for (BytesRef term : terms) {
Index: lucene/spatial/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java (revision )
+++ lucene/spatial/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java (revision )
@@ -0,0 +1,143 @@
+package org.apache.lucene.spatial.composite;
+
+/*
+ * 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 java.io.IOException;
+
+import com.carrotsearch.randomizedtesting.annotations.Repeat;
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.context.SpatialContextFactory;
+import com.spatial4j.core.shape.Point;
+import com.spatial4j.core.shape.Rectangle;
+import com.spatial4j.core.shape.Shape;
+import com.spatial4j.core.shape.impl.RectangleImpl;
+import org.apache.lucene.spatial.prefix.RandomSpatialOpStrategyTestCase;
+import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
+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.SpatialOperation;
+import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
+import org.junit.Test;
+
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean;
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomDouble;
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween;
+
+public class CompositeStrategyTest extends RandomSpatialOpStrategyTestCase {
+
+ private SpatialPrefixTree grid;
+ private RecursivePrefixTreeStrategy rptStrategy;
+
+ private void setupQuadGrid(int maxLevels) {
+ //non-geospatial makes this test a little easier (in gridSnap), and using boundary values 2^X raises
+ // the prospect of edge conditions we want to test, plus makes for simpler numbers (no decimals).
+ SpatialContextFactory factory = new SpatialContextFactory();
+ factory.geo = false;
+ factory.worldBounds = new RectangleImpl(0, 256, -128, 128, null);
+ this.ctx = factory.newSpatialContext();
+ //A fairly shallow grid
+ if (maxLevels == -1)
+ maxLevels = randomIntBetween(1, 8);//max 64k cells (4^8), also 256*256
+ this.grid = new QuadPrefixTree(ctx, maxLevels);
+ this.rptStrategy = newRPT();
+ }
+
+ private void setupGeohashGrid(int maxLevels) {
+ this.ctx = SpatialContext.GEO;
+ //A fairly shallow grid
+ if (maxLevels == -1)
+ maxLevels = randomIntBetween(1, 3);//max 16k cells (32^3)
+ this.grid = new GeohashPrefixTree(ctx, maxLevels);
+ this.rptStrategy = newRPT();
+ }
+
+ protected RecursivePrefixTreeStrategy newRPT() {
+ final RecursivePrefixTreeStrategy rpt = new RecursivePrefixTreeStrategy(this.grid,
+ getClass().getSimpleName() + "_rpt");
+ rpt.setDistErrPct(0.10);//not too many cells
+ return rpt;
+ }
+
+ @Test
+ @Repeat(iterations = 20)
+ public void testOperations() throws IOException {
+ //setup
+ if (randomBoolean()) {
+ setupQuadGrid(-1);
+ } else {
+ setupGeohashGrid(-1);
+ }
+ SerializedDVStrategy serializedDVStrategy = new SerializedDVStrategy(ctx, getClass().getSimpleName() + "_sdv");
+ this.strategy = new CompositeSpatialStrategy("composite_" + getClass().getSimpleName(),
+ rptStrategy, serializedDVStrategy);
+
+ //Do it!
+
+ for (SpatialOperation pred : SpatialOperation.values()) {
+ if (pred == SpatialOperation.BBoxIntersects || pred == SpatialOperation.BBoxWithin) {
+ continue;
+ }
+ if (pred == SpatialOperation.IsDisjointTo) {//TODO
+ continue;
+ }
+ testOperationRandomShapes(pred);
+ deleteAll();
+ commit();
+ }
+ }
+
+ @Override
+ protected boolean needsDocValues() {
+ return true;//due to SerializedDVStrategy
+ }
+
+ @Override
+ protected Shape randomIndexedShape() {
+ return randomShape();
+ }
+
+ @Override
+ protected Shape randomQueryShape() {
+ return randomShape();
+ }
+
+ private Shape randomShape() {
+ return random().nextBoolean() ? randomCircle() : randomRectangle();
+ }
+
+ //TODO move up
+ private Shape randomCircle() {
+ final Point point = randomPoint();
+ //TODO pick using gaussian
+ double radius;
+ if (ctx.isGeo()) {
+ radius = randomDouble() * 100;
+ } else {
+ //find distance to closest edge
+ final Rectangle worldBounds = ctx.getWorldBounds();
+ double maxRad = point.getX() - worldBounds.getMinX();
+ maxRad = Math.min(maxRad, worldBounds.getMaxX() - point.getX());
+ maxRad = Math.min(maxRad, point.getY() - worldBounds.getMinY());
+ maxRad = Math.min(maxRad, worldBounds.getMaxY() - point.getY());
+ radius = randomDouble() * maxRad;
+ }
+
+ return ctx.makeCircle(point, radius);
+ }
+}
Index: lucene/core/src/java/org/apache/lucene/search/CachingWrapperQuery.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/CachingWrapperQuery.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/search/CachingWrapperQuery.java (revision )
@@ -116,7 +116,7 @@
policy.onUse(weight.getQuery());
return new ConstantScoreWeight(weight.getQuery()) {
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
final LeafReader reader = context.reader();
final Object key = reader.getCoreCacheKey();
Index: lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java (revision )
+++ lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java (revision )
@@ -0,0 +1,139 @@
+package org.apache.lucene.spatial.composite;
+
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.spatial4j.core.shape.Point;
+import com.spatial4j.core.shape.Shape;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryWrapperFilter;
+import org.apache.lucene.spatial.SpatialStrategy;
+import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
+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.query.UnsupportedSpatialOperation;
+import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
+import org.apache.lucene.spatial.util.ShapePredicateValueSource;
+
+/**
+ * A composite {@link SpatialStrategy} based on {@link RecursivePrefixTreeStrategy} (RPT) and
+ * {@link SerializedDVStrategy} (SDV).
+ * RPT acts as an index to the precision available in SDV, and in some circumstances can avoid geometry lookups based
+ * on where a cell is in relation to the query shape. Currently the only predicate optimized like this is Intersects.
+ * All predicates are supported except for the BBox* ones, and Disjoint.
+ */
+public class CompositeSpatialStrategy extends SpatialStrategy {
+
+ //TODO support others? (BBox)
+ private final RecursivePrefixTreeStrategy indexStrategy;
+
+ /** Has the geometry. */ // TODO support others?
+ private final SerializedDVStrategy geometryStrategy;
+ private boolean optimizePredicates = true;
+
+ public CompositeSpatialStrategy(String fieldName,
+ RecursivePrefixTreeStrategy indexStrategy, SerializedDVStrategy geometryStrategy) {
+ super(indexStrategy.getSpatialContext(), fieldName);//field name; unused
+ this.indexStrategy = indexStrategy;
+ this.geometryStrategy = geometryStrategy;
+ }
+
+ /** Set to false to NOT use optimized search predicates that avoid checking the geometry sometimes. Only useful for
+ * benchmarking. */
+ public void setOptimizePredicates(boolean optimizePredicates) {
+ this.optimizePredicates = optimizePredicates;
+ }
+
+ @Override
+ public Field[] createIndexableFields(Shape shape) {
+ List<Field> fields = new ArrayList<>();
+ Collections.addAll(fields, indexStrategy.createIndexableFields(shape));
+ Collections.addAll(fields, geometryStrategy.createIndexableFields(shape));
+ return fields.toArray(new Field[fields.size()]);
+ }
+
+ @Override
+ public ValueSource 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();
+ }
+
+ @Override
+ public Query makeQuery(SpatialArgs args) {
+ final SpatialOperation pred = args.getOperation();
+
+ if (pred == SpatialOperation.BBoxIntersects || pred == SpatialOperation.BBoxWithin) {
+ throw new UnsupportedSpatialOperation(pred);
+ }
+
+ if (pred == SpatialOperation.IsDisjointTo) {
+// final Query intersectQuery = makeQuery(new SpatialArgs(SpatialOperation.Intersects, args.getShape()));
+// DocValues.getDocsWithField(reader, geometryStrategy.getFieldName());
+ //TODO resurrect Disjoint spatial query utility accepting a field name known to have DocValues.
+ // update class docs when it's added.
+ throw new UnsupportedSpatialOperation(pred);
+ }
+
+ final ShapePredicateValueSource predicateValueSource =
+ new ShapePredicateValueSource(geometryStrategy.makeShapeValueSource(), pred, args.getShape());
+ //System.out.println("PredOpt: " + optimizePredicates);
+ if (pred == SpatialOperation.Intersects && optimizePredicates) {
+ // We have a smart Intersects impl
+
+ final SpatialPrefixTree grid = indexStrategy.getGrid();
+ final int detailLevel = grid.getLevelForDistance(args.resolveDistErr(ctx, 0.0));//default to max precision
+ return new IntersectsRPTVerifyQuery(args.getShape(), indexStrategy.getFieldName(), grid,
+ detailLevel, indexStrategy.getPrefixGridScanLevel(), predicateValueSource);
+ } else {
+ //The general path; all index matches get verified
+
+ SpatialArgs indexArgs;
+ if (pred == SpatialOperation.Contains) {
+ // note: we could map IsWithin as well but it's pretty darned slow since it touches all world grids
+ indexArgs = args;
+ } else {
+ //TODO add args.clone method with new predicate? Or simply make non-final?
+ indexArgs = new SpatialArgs(SpatialOperation.Intersects, args.getShape());
+ indexArgs.setDistErr(args.getDistErr());
+ indexArgs.setDistErrPct(args.getDistErrPct());
+ }
+
+ if (indexArgs.getDistErr() == null && indexArgs.getDistErrPct() == null) {
+ indexArgs.setDistErrPct(0.10);
+ }
+
+ final Query indexQuery = indexStrategy.makeQuery(indexArgs);
+ return new CompositeVerifyQuery(indexQuery, predicateValueSource);
+ }
+ }
+
+ @Override
+ public Filter makeFilter(SpatialArgs args) {
+ //note: Filters are being deprecated in LUCENE-6301
+ return new QueryWrapperFilter(makeQuery(args));
+ }
+
+}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java (revision )
+++ lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java (revision )
@@ -0,0 +1,123 @@
+package org.apache.lucene.spatial.composite;
+
+/*
+ * 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 java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.ConstantScoreWeight;
+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.util.Bits;
+
+/**
+ * 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()}.
+ *
+ * @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) {
+ this.indexQuery = indexQuery;
+ this.predicateValueSource = predicateValueSource;
+ }
+
+ @Override
+ public Query rewrite(IndexReader reader) throws IOException {
+ final Query rewritten = indexQuery.rewrite(reader);
+ if (rewritten != indexQuery) {
+ return new CompositeVerifyQuery(rewritten, predicateValueSource);
+ }
+ return this;
+ }
+
+ @Override
+ public void extractTerms(Set<Term> terms) {
+ indexQuery.extractTerms(terms);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ CompositeVerifyQuery that = (CompositeVerifyQuery) o;
+
+ if (!indexQuery.equals(that.indexQuery)) return false;
+ if (!predicateValueSource.equals(that.predicateValueSource)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + indexQuery.hashCode();
+ result = 31 * result + predicateValueSource.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString(String field) {
+ //TODO verify this looks good
+ return getClass().getSimpleName() + "(" + indexQuery.toString(field) + ", " + predicateValueSource + ")";
+ }
+
+ @Override
+ public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+ final Weight indexQueryWeight = indexQuery.createWeight(searcher, false);//scores aren't unsupported
+ final Map valueSourceContext = ValueSource.newContext(searcher);
+
+ return new ConstantScoreWeight(this) {
+
+ @Override
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+
+ final Scorer indexQueryScorer = indexQueryWeight.scorer(context, acceptDocs);//pass acceptDocs through
+ if (indexQueryScorer == null) {
+ return null;
+ }
+
+ final FunctionValues predFuncValues = predicateValueSource.getValues(valueSourceContext, context);
+
+ final TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(indexQueryScorer) {
+ @Override
+ public boolean matches() throws IOException {
+ return predFuncValues.boolVal(indexQueryScorer.docID());
+ }
+ };
+
+ return new ConstantScoreScorer(this, score, twoPhaseIterator);
+ }
+ };
+ }
+}
Index: lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java (revision 1671637)
+++ lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java (revision )
@@ -84,7 +84,7 @@
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
return new ConstantScoreWeight(this) {
@Override
- Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
+ protected Scorer scorer(LeafReaderContext context, Bits acceptDocs, float score) throws IOException {
final SortedSetDocValues fcsi = DocValues.getSortedSet(context.reader(), query.field);
TermsEnum termsEnum = query.getTermsEnum(new Terms() {
Index: lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialDocMaker.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialDocMaker.java (revision 1671637)
+++ lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialDocMaker.java (revision )
@@ -17,6 +17,12 @@
* limitations under the License.
*/
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.context.SpatialContextFactory;
import com.spatial4j.core.shape.Point;
@@ -25,16 +31,12 @@
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.spatial.SpatialStrategy;
+import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTreeFactory;
+import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
-import java.util.AbstractMap;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-
/**
* Indexes spatial data according to a configured {@link SpatialStrategy} with optional
* shape transformation via a configured {@link ShapeConverter}. The converter can turn points into
@@ -87,17 +89,27 @@
SpatialContext ctx = SpatialContextFactory.makeSpatialContext(configMap, null);
- //Some day the strategy might be initialized with a factory but such a factory
- // is non-existent.
return makeSpatialStrategy(config, configMap, ctx);
}
protected SpatialStrategy makeSpatialStrategy(final Config config, Map<String, String> configMap,
SpatialContext ctx) {
+ //TODO once strategies have factories, we could use them here.
+ final String strategyName = config.get("spatial.strategy", "rpt");
+ switch (strategyName) {
+ case "rpt": return makeRPTStrategy(SPATIAL_FIELD, config, configMap, ctx);
+ case "composite": return makeCompositeStrategy(config, configMap, ctx);
+ //TODO add more as-needed
+ default: throw new IllegalStateException("Unknown spatial.strategy: " + strategyName);
+ }
+ }
+
+ protected RecursivePrefixTreeStrategy makeRPTStrategy(String spatialField, Config config,
+ Map<String, String> configMap, SpatialContext ctx) {
//A factory for the prefix tree grid
SpatialPrefixTree grid = SpatialPrefixTreeFactory.makeSPT(configMap, null, ctx);
- RecursivePrefixTreeStrategy strategy = new RecursivePrefixTreeStrategy(grid, SPATIAL_FIELD);
+ RecursivePrefixTreeStrategy strategy = new RecursivePrefixTreeStrategy(grid, spatialField);
strategy.setPointsOnly(config.get("spatial.docPointsOnly", false));
strategy.setPruneLeafyBranches(config.get("spatial.pruneLeafyBranches", true));
@@ -108,6 +120,20 @@
double distErrPct = config.get("spatial.distErrPct", .025);//doc & query; a default
strategy.setDistErrPct(distErrPct);
+ return strategy;
+ }
+
+ protected SerializedDVStrategy makeSerializedDVStrategy(String spatialField, Config config,
+ Map<String, String> configMap, SpatialContext ctx) {
+ return new SerializedDVStrategy(ctx, spatialField);
+ }
+
+ protected SpatialStrategy makeCompositeStrategy(Config config, Map<String, String> configMap, SpatialContext ctx) {
+ final CompositeSpatialStrategy strategy = new CompositeSpatialStrategy(
+ SPATIAL_FIELD, makeRPTStrategy(SPATIAL_FIELD + "_rpt", config, configMap, ctx),
+ makeSerializedDVStrategy(SPATIAL_FIELD + "_sdv", config, configMap, ctx)
+ );
+ strategy.setOptimizePredicates(config.get("query.spatial.composite.optimizePredicates", true));
return strategy;
}