/*
 * 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 SpatialStrategy based on RecursivePrefixTreeStrategy (RPT) and 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
/** * 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.
/** 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.
/** 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 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); } } }