org.apache.lucene.spatial.composite.CompositeSpatialStrategy Maven / Gradle / Ivy
/*
* 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.composite;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.lucene.document.Field;
import org.apache.lucene.search.DoubleValuesSource;
import org.apache.lucene.search.Query;
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.ShapeValuesPredicate;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape;
/**
* 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.
*
* @lucene.experimental
*/
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;
}
public RecursivePrefixTreeStrategy getIndexStrategy() {
return indexStrategy;
}
public SerializedDVStrategy getGeometryStrategy() {
return geometryStrategy;
}
public boolean isOptimizePredicates() {
return optimizePredicates;
}
/**
* 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 fields = new ArrayList<>();
Collections.addAll(fields, indexStrategy.createIndexableFields(shape));
Collections.addAll(fields, geometryStrategy.createIndexableFields(shape));
return fields.toArray(new Field[fields.size()]);
}
@Override
public DoubleValuesSource 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 ShapeValuesPredicate predicateValueSource =
new ShapeValuesPredicate(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);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy