/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.lucene; import io.crate.analyze.symbol.Function; import io.crate.operation.operator.*; import org.apache.lucene.document.LatLonPoint; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.lucene.search.Queries; import static io.crate.lucene.LuceneQueryBuilder.Visitor.genericFunctionFilter; enum DistanceQueries { ; /** * Create a LatLonPoint distance query. * * * <pre> * , - ~ ~ ~ - , * , ' ' , * , , X = lonLat coordinates * , , * , [distance] , * , p<------------>X * , ^ , * , | , * , [columnName] , * , point , ' * ' - , _ _ _ , ' * * lt and lte -> match everything WITHIN distance * gt and gte -> match everything OUTSIDE distance * * eq distance ~ 0 -> match everything within distance + tolerance * * eq distance > 0 -> build two circles, one slightly smaller, one slightly larger * distance must not be within the smaller distance but within the larger. * </pre> */ public static Query esV5DistanceQuery(Function parentFunction, LuceneQueryBuilder.Context context, String parentOperatorName, String columnName, Double distance, Double[] lonLat) { switch (parentOperatorName) { // We documented that using distance in the WHERE clause utilizes the index which isn't precise so treating // lte & lt the same should be acceptable case LteOperator.NAME: case LtOperator.NAME: return LatLonPoint.newDistanceQuery(columnName, lonLat[1], lonLat[0], distance); case GteOperator.NAME: if (distance - GeoUtils.TOLERANCE <= 0.0d) { return Queries.newMatchAllQuery(); } // fall-through case GtOperator.NAME: return Queries.not(LatLonPoint.newDistanceQuery(columnName, lonLat[1], lonLat[0], distance)); case EqOperator.NAME: return eqDistance(parentFunction, context, columnName, distance, lonLat); default: return null; } } private static Query eqDistance(Function parentFunction, LuceneQueryBuilder.Context context, String columnName, Double distance, Double[] lonLat) { double smallDistance = distance * 0.99; if (smallDistance <= 0.0) { return LatLonPoint.newDistanceQuery(columnName, lonLat[1], lonLat[0], 0); } Query withinSmallCircle = LatLonPoint.newDistanceQuery(columnName, lonLat[1], lonLat[0], smallDistance); Query withinLargeCircle = LatLonPoint.newDistanceQuery(columnName, lonLat[1], lonLat[0], distance * 1.01); return new BooleanQuery.Builder() .add(withinLargeCircle, BooleanClause.Occur.MUST) .add(withinSmallCircle, BooleanClause.Occur.MUST_NOT) .add(genericFunctionFilter(parentFunction, context), BooleanClause.Occur.FILTER) .build(); } }