/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.index.query; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.prefix.PrefixTreeStrategy; import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy; import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialOperation; import org.opensearch.OpenSearchException; import org.opensearch.common.geo.ShapeRelation; import org.opensearch.common.geo.SpatialStrategy; import org.opensearch.common.geo.builders.CircleBuilder; import org.opensearch.common.geo.builders.EnvelopeBuilder; import org.opensearch.common.geo.builders.GeometryCollectionBuilder; import org.opensearch.common.geo.builders.LineStringBuilder; import org.opensearch.common.geo.builders.MultiLineStringBuilder; import org.opensearch.common.geo.builders.MultiPointBuilder; import org.opensearch.common.geo.builders.MultiPolygonBuilder; import org.opensearch.common.geo.builders.PointBuilder; import org.opensearch.common.geo.builders.PolygonBuilder; import org.opensearch.common.geo.builders.ShapeBuilder; import org.opensearch.common.unit.DistanceUnit; import org.opensearch.geometry.Circle; import org.opensearch.geometry.Geometry; import org.opensearch.geometry.GeometryCollection; import org.opensearch.geometry.GeometryVisitor; import org.opensearch.geometry.Line; import org.opensearch.geometry.LinearRing; import org.opensearch.geometry.MultiLine; import org.opensearch.geometry.MultiPoint; import org.opensearch.geometry.MultiPolygon; import org.opensearch.geometry.Point; import org.opensearch.geometry.Polygon; import org.opensearch.geometry.Rectangle; import org.opensearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.opensearch.index.mapper.LegacyGeoShapeFieldMapper; import org.locationtech.jts.geom.Coordinate; import org.locationtech.spatial4j.shape.Shape; import java.util.ArrayList; import java.util.List; import static org.opensearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES; /** * Query Processor for Legacy Geo Shape types (prefix trees) * * @opensearch.internal * * @deprecated this will be removed in a future release */ @Deprecated public class LegacyGeoShapeQueryProcessor { private AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType ft; public LegacyGeoShapeQueryProcessor(AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType ft) { this.ft = ft; } public Query geoShapeQuery( Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation, QueryShardContext context ) { if (context.allowExpensiveQueries() == false) { throw new OpenSearchException( "[geo-shape] queries on [PrefixTree geo shapes] cannot be executed when '" + ALLOW_EXPENSIVE_QUERIES.getKey() + "' is set to false." ); } LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (LegacyGeoShapeFieldMapper.GeoShapeFieldType) ft; SpatialStrategy spatialStrategy = shapeFieldType.strategy(); if (strategy != null) { spatialStrategy = strategy; } PrefixTreeStrategy prefixTreeStrategy = shapeFieldType.resolvePrefixTreeStrategy(spatialStrategy); if (prefixTreeStrategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) { // this strategy doesn't support disjoint anymore: but it did // before, including creating lucene fieldcache (!) // in this case, execute disjoint as exists && !intersects BooleanQuery.Builder bool = new BooleanQuery.Builder(); Query exists = ExistsQueryBuilder.newFilter(context, fieldName, false); Query intersects = prefixTreeStrategy.makeQuery(getArgs(shape, ShapeRelation.INTERSECTS)); bool.add(exists, BooleanClause.Occur.MUST); bool.add(intersects, BooleanClause.Occur.MUST_NOT); return bool.build(); } else { return prefixTreeStrategy.makeQuery(getArgs(shape, relation)); } } public static SpatialArgs getArgs(Geometry shape, ShapeRelation relation) { switch (relation) { case DISJOINT: return new SpatialArgs(SpatialOperation.IsDisjointTo, buildS4J(shape)); case INTERSECTS: return new SpatialArgs(SpatialOperation.Intersects, buildS4J(shape)); case WITHIN: return new SpatialArgs(SpatialOperation.IsWithin, buildS4J(shape)); case CONTAINS: return new SpatialArgs(SpatialOperation.Contains, buildS4J(shape)); default: throw new IllegalArgumentException("invalid relation [" + relation + "]"); } } /** * Builds JTS shape from a geometry *
* This method is needed to handle legacy indices and will be removed when we no longer need to build JTS shapes
*/
private static Shape buildS4J(Geometry geometry) {
return geometryToShapeBuilder(geometry).buildS4J();
}
public static ShapeBuilder, ?, ?> geometryToShapeBuilder(Geometry geometry) {
ShapeBuilder, ?, ?> shapeBuilder = geometry.visit(new GeometryVisitor