All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.index.query.GeoDistanceRangeQueryBuilder Maven / Gradle / Ivy

There is a newer version: 8.14.1
Show newest version
/*
 * 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.
 */

package org.elasticsearch.index.query;

import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
import org.apache.lucene.spatial.geopoint.search.XGeoPointDistanceRangeQuery;
import org.elasticsearch.Version;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.mapper.BaseGeoPointFieldMapper;
import org.elasticsearch.index.mapper.BaseGeoPointFieldMapper.LegacyGeoPointFieldType;
import org.elasticsearch.index.mapper.GeoPointFieldMapper;
import org.elasticsearch.index.mapper.LatLonPointFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.search.geo.LegacyGeoDistanceRangeQuery;

import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

public class GeoDistanceRangeQueryBuilder extends AbstractQueryBuilder {
    public static final String NAME = "geo_distance_range";

    private static final DeprecationLogger deprecationLogger = new DeprecationLogger(Loggers.getLogger(GeoDistanceRangeQueryBuilder.class));

    public static final boolean DEFAULT_INCLUDE_LOWER = true;
    public static final boolean DEFAULT_INCLUDE_UPPER = true;
    public static final GeoDistance DEFAULT_GEO_DISTANCE = GeoDistance.ARC;
    public static final DistanceUnit DEFAULT_UNIT = DistanceUnit.DEFAULT;
    @Deprecated
    public static final String DEFAULT_OPTIMIZE_BBOX = "memory";

    /**
     * The default value for ignore_unmapped.
     */
    public static final boolean DEFAULT_IGNORE_UNMAPPED = false;

    private static final ParseField FROM_FIELD = new ParseField("from");
    private static final ParseField TO_FIELD = new ParseField("to");
    private static final ParseField INCLUDE_LOWER_FIELD = new ParseField("include_lower");
    private static final ParseField INCLUDE_UPPER_FIELD = new ParseField("include_upper");
    private static final ParseField GT_FIELD = new ParseField("gt");
    private static final ParseField GTE_FIELD = new ParseField("gte", "ge");
    private static final ParseField LT_FIELD = new ParseField("lt");
    private static final ParseField LTE_FIELD = new ParseField("lte", "le");
    private static final ParseField UNIT_FIELD = new ParseField("unit");
    private static final ParseField DISTANCE_TYPE_FIELD = new ParseField("distance_type");
    private static final ParseField NAME_FIELD = new ParseField("_name");
    private static final ParseField BOOST_FIELD = new ParseField("boost");
    @Deprecated
    private static final ParseField OPTIMIZE_BBOX_FIELD = new ParseField("optimize_bbox")
            .withAllDeprecated("no replacement: `optimize_bbox` is no longer supported due to recent improvements");
    private static final ParseField COERCE_FIELD = new ParseField("coerce", "normalize")
            .withAllDeprecated("use validation_method instead");
    private static final ParseField IGNORE_MALFORMED_FIELD = new ParseField("ignore_malformed")
            .withAllDeprecated("use validation_method instead");
    private static final ParseField VALIDATION_METHOD = new ParseField("validation_method");
    private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");

    private final String fieldName;

    private Object from;
    private Object to;
    private boolean includeLower = DEFAULT_INCLUDE_LOWER;
    private boolean includeUpper = DEFAULT_INCLUDE_UPPER;

    private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

    private final GeoPoint point;

    private GeoDistance geoDistance = DEFAULT_GEO_DISTANCE;

    private DistanceUnit unit = DEFAULT_UNIT;

    private String optimizeBbox = null;

    private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;

    public GeoDistanceRangeQueryBuilder(String fieldName, GeoPoint point) {
        if (Strings.isEmpty(fieldName)) {
            throw new IllegalArgumentException("fieldName must not be null");
        }
        if (point == null) {
            throw new IllegalArgumentException("point must not be null");
        }
        this.fieldName = fieldName;
        this.point = point;
    }

    public GeoDistanceRangeQueryBuilder(String fieldName, double lat, double lon) {
        this(fieldName, new GeoPoint(lat, lon));
    }

    public GeoDistanceRangeQueryBuilder(String fieldName, String geohash) {
        this(fieldName, geohash == null ? null : new GeoPoint().resetFromGeoHash(geohash));
    }

    /**
     * Read from a stream.
     */
    public GeoDistanceRangeQueryBuilder(StreamInput in) throws IOException {
        super(in);
        fieldName = in.readString();
        point = in.readGeoPoint();
        from = in.readGenericValue();
        to = in.readGenericValue();
        includeLower = in.readBoolean();
        includeUpper = in.readBoolean();
        unit = DistanceUnit.valueOf(in.readString());
        geoDistance = GeoDistance.readFromStream(in);
        optimizeBbox = in.readOptionalString();
        validationMethod = GeoValidationMethod.readFromStream(in);
        ignoreUnmapped = in.readBoolean();
    }

    @Override
    protected void doWriteTo(StreamOutput out) throws IOException {
        out.writeString(fieldName);
        out.writeGeoPoint(point);
        out.writeGenericValue(from);
        out.writeGenericValue(to);
        out.writeBoolean(includeLower);
        out.writeBoolean(includeUpper);
        out.writeString(unit.name());
        geoDistance.writeTo(out);;
        out.writeOptionalString(optimizeBbox);
        validationMethod.writeTo(out);
        out.writeBoolean(ignoreUnmapped);
    }

    public String fieldName() {
        return fieldName;
    }

    public GeoPoint point() {
        return point;
    }

    public GeoDistanceRangeQueryBuilder from(String from) {
        if (from == null) {
            throw new IllegalArgumentException("[from] must not be null");
        }
        this.from = from;
        return this;
    }

    public GeoDistanceRangeQueryBuilder from(Number from) {
        if (from == null) {
            throw new IllegalArgumentException("[from] must not be null");
        }
        this.from = from;
        return this;
    }

    public Object from() {
        return from;
    }

    public GeoDistanceRangeQueryBuilder to(String to) {
        if (to == null) {
            throw new IllegalArgumentException("[to] must not be null");
        }
        this.to = to;
        return this;
    }

    public GeoDistanceRangeQueryBuilder to(Number to) {
        if (to == null) {
            throw new IllegalArgumentException("[to] must not be null");
        }
        this.to = to;
        return this;
    }

    public Object to() {
        return to;
    }

    public GeoDistanceRangeQueryBuilder includeLower(boolean includeLower) {
        this.includeLower = includeLower;
        return this;
    }

    public boolean includeLower() {
        return includeLower;
    }

    public GeoDistanceRangeQueryBuilder includeUpper(boolean includeUpper) {
        this.includeUpper = includeUpper;
        return this;
    }

    public boolean includeUpper() {
        return includeUpper;
    }

    public GeoDistanceRangeQueryBuilder geoDistance(GeoDistance geoDistance) {
        if (geoDistance == null) {
            throw new IllegalArgumentException("geoDistance calculation mode must not be null");
        }
        this.geoDistance = geoDistance;
        return this;
    }

    public GeoDistance geoDistance() {
        return geoDistance;
    }

    public GeoDistanceRangeQueryBuilder unit(DistanceUnit unit) {
        if (unit == null) {
            throw new IllegalArgumentException("distance unit must not be null");
        }
        this.unit = unit;
        return this;
    }

    public DistanceUnit unit() {
        return unit;
    }

    @Deprecated
    public GeoDistanceRangeQueryBuilder optimizeBbox(String optimizeBbox) {
        this.optimizeBbox = optimizeBbox;
        return this;
    }

    @Deprecated
    public String optimizeBbox() {
        return optimizeBbox;
    }

    /** Set validation method for coordinates. */
    public GeoDistanceRangeQueryBuilder setValidationMethod(GeoValidationMethod method) {
        this.validationMethod = method;
        return this;
    }

    /** Returns validation method for coordinates. */
    public GeoValidationMethod getValidationMethod() {
        return this.validationMethod;
    }

    /**
     * Sets whether the query builder should ignore unmapped fields (and run a
     * {@link MatchNoDocsQuery} in place of this query) or throw an exception if
     * the field is unmapped.
     */
    public GeoDistanceRangeQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
        this.ignoreUnmapped = ignoreUnmapped;
        return this;
    }

    /**
     * Gets whether the query builder will ignore unmapped fields (and run a
     * {@link MatchNoDocsQuery} in place of this query) or throw an exception if
     * the field is unmapped.
     */
    public boolean ignoreUnmapped() {
        return ignoreUnmapped;
    }

    @Override
    protected Query doToQuery(QueryShardContext context) throws IOException {
        MappedFieldType fieldType = context.fieldMapper(fieldName);
        if (fieldType == null) {
            if (ignoreUnmapped) {
                return new MatchNoDocsQuery();
            } else {
                throw new QueryShardException(context, "failed to find geo_point field [" + fieldName + "]");
            }
        }
        if (!(fieldType instanceof BaseGeoPointFieldMapper.GeoPointFieldType)) {
            throw new QueryShardException(context, "field [" + fieldName + "] is not a geo_point field");
        }

        final boolean indexCreatedBeforeV2_0 = context.indexVersionCreated().before(Version.V_2_0_0);
        final boolean indexCreatedBeforeV2_2 = context.indexVersionCreated().before(Version.V_2_2_0);
        // validation was not available prior to 2.x, so to support bwc
        // percolation queries we only ignore_malformed on 2.x created indexes
        if (!indexCreatedBeforeV2_0 && !GeoValidationMethod.isIgnoreMalformed(validationMethod)) {
            if (!GeoUtils.isValidLatitude(point.lat())) {
                throw new QueryShardException(context, "illegal latitude value [{}] for [{}]", point.lat(), NAME);
            }
            if (!GeoUtils.isValidLongitude(point.lon())) {
                throw new QueryShardException(context, "illegal longitude value [{}] for [{}]", point.lon(), NAME);
            }
        }

        GeoPoint point = new GeoPoint(this.point);
        if (indexCreatedBeforeV2_2 == false || GeoValidationMethod.isCoerce(validationMethod)) {
            GeoUtils.normalizePoint(point, true, true);
        }

        Double fromValue;
        Double toValue;
        if (from != null) {
            if (from instanceof Number) {
                fromValue = unit.toMeters(((Number) from).doubleValue());
            } else {
                fromValue = DistanceUnit.parse((String) from, unit, DistanceUnit.DEFAULT);
            }
        } else {
            fromValue = 0.0;
        }

        if (to != null) {
            if (to instanceof Number) {
                toValue = unit.toMeters(((Number) to).doubleValue());
            } else {
                toValue = DistanceUnit.parse((String) to, unit, DistanceUnit.DEFAULT);
            }
        } else {
            toValue = GeoUtils.maxRadialDistanceMeters(point.lat(), point.lon());
        }

        final Version indexVersionCreated = context.indexVersionCreated();
        if (indexVersionCreated.onOrAfter(LatLonPointFieldMapper.LAT_LON_FIELD_VERSION)) {
            throw new QueryShardException(context, "[{}] queries are no longer supported for geo_point field types. "
                + "Use geo_distance sort or aggregations", NAME);
        }

        deprecationLogger.deprecated("geo_distance_range search is deprecated. Use geo_distance aggregation or sort instead.");

        if (indexVersionCreated.before(Version.V_2_2_0)) {
            IndexGeoPointFieldData indexFieldData = context.getForField(fieldType);
            return new LegacyGeoDistanceRangeQuery(point, fromValue, toValue, includeLower, includeUpper, geoDistance,
                ((LegacyGeoPointFieldType) fieldType), indexFieldData, optimizeBbox, context);
        }

        // if index created V_2_2 use (soon to be legacy) numeric encoding postings format
        // if index created V_2_3 > use prefix encoded postings format
        final GeoPointField.TermEncoding encoding = (indexVersionCreated.before(Version.V_2_3_0)) ?
            GeoPointField.TermEncoding.NUMERIC : GeoPointField.TermEncoding.PREFIX;

        return new XGeoPointDistanceRangeQuery(fieldType.name(), encoding, point.lat(), point.lon(),
            (includeLower) ? fromValue : fromValue + GeoUtils.TOLERANCE,
            (includeUpper) ? toValue : toValue - GeoUtils.TOLERANCE);
    }

    @Override
    protected void doXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject(NAME);
        builder.startArray(fieldName).value(point.lon()).value(point.lat()).endArray();
        builder.field(FROM_FIELD.getPreferredName(), from);
        builder.field(TO_FIELD.getPreferredName(), to);
        builder.field(INCLUDE_LOWER_FIELD.getPreferredName(), includeLower);
        builder.field(INCLUDE_UPPER_FIELD.getPreferredName(), includeUpper);
        builder.field(UNIT_FIELD.getPreferredName(), unit);
        builder.field(DISTANCE_TYPE_FIELD.getPreferredName(), geoDistance.name().toLowerCase(Locale.ROOT));
        if (Strings.isEmpty(optimizeBbox) == false) {
            builder.field(OPTIMIZE_BBOX_FIELD.getPreferredName(), optimizeBbox);
        }
        builder.field(VALIDATION_METHOD.getPreferredName(), validationMethod);
        builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
        printBoostAndQueryName(builder);
        builder.endObject();
    }

    public static Optional fromXContent(QueryParseContext parseContext) throws IOException {
        XContentParser parser = parseContext.parser();

        XContentParser.Token token;

        Float boost = null;
        String queryName = null;
        String currentFieldName = null;
        GeoPoint point = null;
        String fieldName = null;
        Object vFrom = null;
        Object vTo = null;
        Boolean includeLower = null;
        Boolean includeUpper = null;
        DistanceUnit unit = null;
        GeoDistance geoDistance = null;
        String optimizeBbox = null;
        boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
        boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
        GeoValidationMethod validationMethod = null;
        boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
            if (token == XContentParser.Token.FIELD_NAME) {
                currentFieldName = parser.currentName();
            } else if (parseContext.isDeprecatedSetting(currentFieldName)) {
                // skip
            } else if (token == XContentParser.Token.START_ARRAY) {
                if (fieldName == null) {
                    if (point == null) {
                        point = new GeoPoint();
                    }
                    GeoUtils.parseGeoPoint(parser, point);
                    fieldName = currentFieldName;
                } else {
                    throw new ParsingException(parser.getTokenLocation(), "[" + GeoDistanceRangeQueryBuilder.NAME +
                            "] field name already set to [" + fieldName + "] but found [" + currentFieldName + "]");
                }
            } else if (token == XContentParser.Token.START_OBJECT) {
                // the json in the format of -> field : { lat : 30, lon : 12 }
                if (fieldName == null) {
                    fieldName = currentFieldName;
                    if (point == null) {
                        point = new GeoPoint();
                    }
                    GeoUtils.parseGeoPoint(parser, point);
                } else {
                    throw new ParsingException(parser.getTokenLocation(), "[" + GeoDistanceRangeQueryBuilder.NAME +
                            "] field name already set to [" + fieldName + "] but found [" + currentFieldName + "]");
                }
            } else if (token.isValue()) {
                if (FROM_FIELD.match(currentFieldName)) {
                    if (token == XContentParser.Token.VALUE_NULL) {
                    } else if (token == XContentParser.Token.VALUE_STRING) {
                        vFrom = parser.text(); // a String
                    } else {
                        vFrom = parser.numberValue(); // a Number
                    }
                } else if (TO_FIELD.match(currentFieldName)) {
                    if (token == XContentParser.Token.VALUE_NULL) {
                    } else if (token == XContentParser.Token.VALUE_STRING) {
                        vTo = parser.text(); // a String
                    } else {
                        vTo = parser.numberValue(); // a Number
                    }
                } else if (INCLUDE_LOWER_FIELD.match(currentFieldName)) {
                    includeLower = parser.booleanValue();
                } else if (INCLUDE_UPPER_FIELD.match(currentFieldName)) {
                    includeUpper = parser.booleanValue();
                } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName)) {
                    ignoreUnmapped = parser.booleanValue();
                } else if (GT_FIELD.match(currentFieldName)) {
                    if (token == XContentParser.Token.VALUE_NULL) {
                    } else if (token == XContentParser.Token.VALUE_STRING) {
                        vFrom = parser.text(); // a String
                    } else {
                        vFrom = parser.numberValue(); // a Number
                    }
                    includeLower = false;
                } else if (GTE_FIELD.match(currentFieldName)) {
                    if (token == XContentParser.Token.VALUE_NULL) {
                    } else if (token == XContentParser.Token.VALUE_STRING) {
                        vFrom = parser.text(); // a String
                    } else {
                        vFrom = parser.numberValue(); // a Number
                    }
                    includeLower = true;
                } else if (LT_FIELD.match(currentFieldName)) {
                    if (token == XContentParser.Token.VALUE_NULL) {
                    } else if (token == XContentParser.Token.VALUE_STRING) {
                        vTo = parser.text(); // a String
                    } else {
                        vTo = parser.numberValue(); // a Number
                    }
                    includeUpper = false;
                } else if (LTE_FIELD.match(currentFieldName)) {
                    if (token == XContentParser.Token.VALUE_NULL) {
                    } else if (token == XContentParser.Token.VALUE_STRING) {
                        vTo = parser.text(); // a String
                    } else {
                        vTo = parser.numberValue(); // a Number
                    }
                    includeUpper = true;
                } else if (UNIT_FIELD.match(currentFieldName)) {
                    unit = DistanceUnit.fromString(parser.text());
                } else if (DISTANCE_TYPE_FIELD.match(currentFieldName)) {
                    geoDistance = GeoDistance.fromString(parser.text());
                } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LAT_SUFFIX)) {
                    String maybeFieldName = currentFieldName.substring(0,
                            currentFieldName.length() - GeoPointFieldMapper.Names.LAT_SUFFIX.length());
                    if (fieldName == null || fieldName.equals(maybeFieldName)) {
                        fieldName = maybeFieldName;
                    } else {
                        throw new ParsingException(parser.getTokenLocation(), "[" + GeoDistanceRangeQueryBuilder.NAME +
                                "] field name already set to [" + fieldName + "] but found [" + currentFieldName + "]");
                    }
                    if (point == null) {
                        point = new GeoPoint();
                    }
                    point.resetLat(parser.doubleValue());
                } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LON_SUFFIX)) {
                    String maybeFieldName = currentFieldName.substring(0,
                            currentFieldName.length() - GeoPointFieldMapper.Names.LON_SUFFIX.length());
                    if (fieldName == null || fieldName.equals(maybeFieldName)) {
                        fieldName = maybeFieldName;
                    } else {
                        throw new ParsingException(parser.getTokenLocation(), "[" + GeoDistanceRangeQueryBuilder.NAME +
                                "] field name already set to [" + fieldName + "] but found [" + currentFieldName + "]");
                    }
                    if (point == null) {
                        point = new GeoPoint();
                    }
                    point.resetLon(parser.doubleValue());
                } else if (NAME_FIELD.match(currentFieldName)) {
                    queryName = parser.text();
                } else if (BOOST_FIELD.match(currentFieldName)) {
                    boost = parser.floatValue();
                } else if (OPTIMIZE_BBOX_FIELD.match(currentFieldName)) {
                    optimizeBbox = parser.textOrNull();
                } else if (COERCE_FIELD.match(currentFieldName)) {
                    coerce = parser.booleanValue();
                } else if (IGNORE_MALFORMED_FIELD.match(currentFieldName)) {
                    ignoreMalformed = parser.booleanValue();
                } else if (VALIDATION_METHOD.match(currentFieldName)) {
                    validationMethod = GeoValidationMethod.fromString(parser.text());
                } else {
                    if (fieldName == null) {
                        if (point == null) {
                            point = new GeoPoint();
                        }
                        point.resetFromString(parser.text());
                        fieldName = currentFieldName;
                    } else {
                        throw new ParsingException(parser.getTokenLocation(), "[" + GeoDistanceRangeQueryBuilder.NAME +
                                "] field name already set to [" + fieldName + "] but found [" + currentFieldName + "]");
                    }
                }
            }
        }

        GeoDistanceRangeQueryBuilder queryBuilder = new GeoDistanceRangeQueryBuilder(fieldName, point);
        if (boost != null) {
            queryBuilder.boost(boost);
        }

        if (queryName != null) {
            queryBuilder.queryName(queryName);
        }

        if (vFrom != null) {
            if (vFrom instanceof Number) {
                queryBuilder.from((Number) vFrom);
            } else {
                queryBuilder.from((String) vFrom);
            }
        }

        if (vTo != null) {
            if (vTo instanceof Number) {
                queryBuilder.to((Number) vTo);
            } else {
                queryBuilder.to((String) vTo);
            }
        }

        if (includeUpper != null) {
            queryBuilder.includeUpper(includeUpper);
        }

        if (includeLower != null) {
            queryBuilder.includeLower(includeLower);
        }

        if (unit != null) {
            queryBuilder.unit(unit);
        }

        if (geoDistance != null) {
            queryBuilder.geoDistance(geoDistance);
        }

        if (optimizeBbox != null) {
            queryBuilder.optimizeBbox(optimizeBbox);
        }

        if (validationMethod != null) {
            // if validation method is set explicitly ignore deprecated coerce/ignore malformed fields if any
            queryBuilder.setValidationMethod(validationMethod);
        } else {
            queryBuilder.setValidationMethod(GeoValidationMethod.infer(coerce, ignoreMalformed));
        }
        queryBuilder.ignoreUnmapped(ignoreUnmapped);
        return Optional.of(queryBuilder);
    }

    @Override
    protected boolean doEquals(GeoDistanceRangeQueryBuilder other) {
        return ((Objects.equals(fieldName, other.fieldName)) &&
                (Objects.equals(point, other.point)) &&
                (Objects.equals(from, other.from)) &&
                (Objects.equals(to, other.to)) &&
                (Objects.equals(includeUpper, other.includeUpper)) &&
                (Objects.equals(includeLower, other.includeLower)) &&
                (Objects.equals(geoDistance, other.geoDistance)) &&
                (Objects.equals(optimizeBbox, other.optimizeBbox)) &&
                (Objects.equals(validationMethod, other.validationMethod))) &&
                Objects.equals(ignoreUnmapped, other.ignoreUnmapped);
    }

    @Override
    protected int doHashCode() {
        return Objects.hash(fieldName, point, from, to, includeUpper, includeLower, geoDistance, optimizeBbox, validationMethod,
                ignoreUnmapped);
    }

    @Override
    public String getWriteableName() {
        return NAME;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy