瀏覽代碼

[Geo] Refactor GeoShapeQueryBuilder to derive from AbstractGeometryQueryBuilder (#44780)

Refactors GeoShapeQueryBuilder to derive from a new AbstractGeometryQueryBuilder that provides common parsing and build logic for spatial geometries. This will allow development of custom geometry queries by extending AbstractGeometryQueryBuilder preventing duplication of common spatial query logic.
Nick Knize 6 年之前
父節點
當前提交
0482894e57

+ 631 - 0
server/src/main/java/org/elasticsearch/index/query/AbstractGeometryQueryBuilder.java

@@ -0,0 +1,631 @@
+/*
+ * 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.util.SetOnce;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.get.GetRequest;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.ParsingException;
+import org.elasticsearch.common.geo.GeoJson;
+import org.elasticsearch.common.geo.GeometryIO;
+import org.elasticsearch.common.geo.GeometryParser;
+import org.elasticsearch.common.geo.ShapeRelation;
+import org.elasticsearch.common.geo.builders.ShapeBuilder;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentHelper;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.geo.geometry.Geometry;
+import org.elasticsearch.index.mapper.MappedFieldType;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * Base {@link QueryBuilder} that builds a Geometry Query
+ */
+public abstract class AbstractGeometryQueryBuilder<QB extends AbstractGeometryQueryBuilder<QB>> extends AbstractQueryBuilder<QB> {
+
+    static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Types are deprecated in [geo_shape] queries. " +
+        "The type should no longer be specified in the [indexed_shape] section.";
+
+    public static final String DEFAULT_SHAPE_INDEX_NAME = "shapes";
+    public static final String DEFAULT_SHAPE_FIELD_NAME = "shape";
+    public static final ShapeRelation DEFAULT_SHAPE_RELATION = ShapeRelation.INTERSECTS;
+
+    /** registry of content types this query can be used with */
+    protected final List<String> validContentTypes = new ArrayList<>(validContentTypes());
+
+    /** The default value for ignore_unmapped. */
+    public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
+
+    protected static final ParseField SHAPE_FIELD = new ParseField("shape");
+    protected static final ParseField RELATION_FIELD = new ParseField("relation");
+    protected static final ParseField INDEXED_SHAPE_FIELD = new ParseField("indexed_shape");
+    protected static final ParseField SHAPE_ID_FIELD = new ParseField("id");
+    protected static final ParseField SHAPE_TYPE_FIELD = new ParseField("type");
+    protected static final ParseField SHAPE_INDEX_FIELD = new ParseField("index");
+    protected static final ParseField SHAPE_PATH_FIELD = new ParseField("path");
+    protected static final ParseField SHAPE_ROUTING_FIELD = new ParseField("routing");
+    protected static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
+
+    protected final String fieldName;
+    protected final Supplier<Geometry> supplier;
+
+    protected final String indexedShapeId;
+    protected final String indexedShapeType;
+
+    protected Geometry shape;
+    protected String indexedShapeIndex = DEFAULT_SHAPE_INDEX_NAME;
+    protected String indexedShapePath = DEFAULT_SHAPE_FIELD_NAME;
+    protected String indexedShapeRouting;
+
+    protected ShapeRelation relation = DEFAULT_SHAPE_RELATION;
+
+    protected boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
+
+    /**
+     * Creates a new ShapeQueryBuilder whose Query will be against the given
+     * field name using the given Shape
+     *
+     * @param fieldName
+     *            Name of the field that will be queried
+     * @param shape
+     *            Shape used in the Query
+     * @deprecated use {@link #AbstractGeometryQueryBuilder(String, Geometry)} instead
+     */
+    @Deprecated
+    protected AbstractGeometryQueryBuilder(String fieldName, ShapeBuilder shape) {
+        this(fieldName, shape == null ? null : shape.buildGeometry(), null, null);
+    }
+
+    /**
+     * Creates a new AbstractGeometryQueryBuilder whose Query will be against the given
+     * field name using the given Shape
+     *
+     * @param fieldName
+     *            Name of the field that will be queried
+     * @param shape
+     *            Shape used in the Query
+     */
+    public AbstractGeometryQueryBuilder(String fieldName, Geometry shape) {
+        this(fieldName, shape, null, null);
+    }
+
+    /**
+     * Creates a new ShapeQueryBuilder whose Query will be against the given
+     * field name and will use the Shape found with the given ID
+     *
+     * @param fieldName
+     *            Name of the field that will be filtered
+     * @param indexedShapeId
+     *            ID of the indexed Shape that will be used in the Query
+     */
+    protected AbstractGeometryQueryBuilder(String fieldName, String indexedShapeId) {
+        this(fieldName, (Geometry) null, indexedShapeId, null);
+    }
+
+    /**
+     * Creates a new AbstractGeometryQueryBuilder whose Query will be against the given
+     * field name and will use the Shape found with the given ID in the given
+     * type
+     *
+     * @param fieldName
+     *            Name of the field that will be filtered
+     * @param indexedShapeId
+     *            ID of the indexed Shape that will be used in the Query
+     * @param indexedShapeType
+     *            Index type of the indexed Shapes
+     * @deprecated use {@link #AbstractGeometryQueryBuilder(String, String)} instead
+     */
+    @Deprecated
+    protected AbstractGeometryQueryBuilder(String fieldName, String indexedShapeId, String indexedShapeType) {
+        this(fieldName, (Geometry) null, indexedShapeId, indexedShapeType);
+    }
+
+    protected AbstractGeometryQueryBuilder(String fieldName, Geometry shape, String indexedShapeId, @Nullable String indexedShapeType) {
+        if (fieldName == null) {
+            throw new IllegalArgumentException("fieldName is required");
+        }
+        if (shape == null && indexedShapeId == null) {
+            throw new IllegalArgumentException("either shape or indexedShapeId is required");
+        }
+        this.fieldName = fieldName;
+        this.shape = shape;
+        this.indexedShapeId = indexedShapeId;
+        this.indexedShapeType = indexedShapeType;
+        this.supplier = null;
+    }
+
+    protected AbstractGeometryQueryBuilder(String fieldName, Supplier<Geometry> supplier, String indexedShapeId,
+                                           @Nullable String indexedShapeType) {
+        this.fieldName = fieldName;
+        this.shape = null;
+        this.supplier = supplier;
+        this.indexedShapeId = indexedShapeId;
+        this.indexedShapeType = indexedShapeType;
+    }
+
+    /**
+     * Read from a stream.
+     */
+    protected AbstractGeometryQueryBuilder(StreamInput in) throws IOException {
+        super(in);
+        fieldName = in.readString();
+        if (in.readBoolean()) {
+            shape = GeometryIO.readGeometry(in);
+            indexedShapeId = null;
+            indexedShapeType = null;
+        } else {
+            shape = null;
+            indexedShapeId = in.readOptionalString();
+            indexedShapeType = in.readOptionalString();
+            indexedShapeIndex = in.readOptionalString();
+            indexedShapePath = in.readOptionalString();
+            indexedShapeRouting = in.readOptionalString();
+        }
+        relation = ShapeRelation.readFromStream(in);
+        ignoreUnmapped = in.readBoolean();
+        supplier = null;
+    }
+
+    @Override
+    protected void doWriteTo(StreamOutput out) throws IOException {
+        if (supplier != null) {
+            throw new IllegalStateException("supplier must be null, can't serialize suppliers, missing a rewriteAndFetch?");
+        }
+        out.writeString(fieldName);
+        boolean hasShape = shape != null;
+        out.writeBoolean(hasShape);
+        if (hasShape) {
+            GeometryIO.writeGeometry(out, shape);
+        } else {
+            out.writeOptionalString(indexedShapeId);
+            out.writeOptionalString(indexedShapeType);
+            out.writeOptionalString(indexedShapeIndex);
+            out.writeOptionalString(indexedShapePath);
+            out.writeOptionalString(indexedShapeRouting);
+        }
+        relation.writeTo(out);
+        out.writeBoolean(ignoreUnmapped);
+    }
+
+    /**
+     * @return the name of the field that will be queried
+     */
+    public String fieldName() {
+        return fieldName;
+    }
+
+    /**
+     * Sets the shapeBuilder for the query shape.
+     *
+     * @param geometry the geometry
+     * @return this
+     */
+    public QB shape(Geometry geometry) {
+        if (geometry == null) {
+            throw new IllegalArgumentException("No geometry defined");
+        }
+        this.shape = geometry;
+        return (QB)this;
+    }
+
+    /**
+     * @return the shape used in the Query
+     */
+    public Geometry shape() {
+        return shape;
+    }
+
+    /**
+     * @return the ID of the indexed Shape that will be used in the Query
+     */
+    public String indexedShapeId() {
+        return indexedShapeId;
+    }
+
+    /**
+     * @return the document type of the indexed Shape that will be used in the
+     *         Query
+     *
+     * @deprecated Types are in the process of being removed.
+     */
+    @Deprecated
+    public String indexedShapeType() {
+        return indexedShapeType;
+    }
+
+    /**
+     * Sets the name of the index where the indexed Shape can be found
+     *
+     * @param indexedShapeIndex Name of the index where the indexed Shape is
+     * @return this
+     */
+    public QB indexedShapeIndex(String indexedShapeIndex) {
+        this.indexedShapeIndex = indexedShapeIndex;
+        return (QB)this;
+    }
+
+    /**
+     * @return the index name for the indexed Shape that will be used in the
+     *         Query
+     */
+    public String indexedShapeIndex() {
+        return indexedShapeIndex;
+    }
+
+    /**
+     * Sets the path of the field in the indexed Shape document that has the Shape itself
+     *
+     * @param indexedShapePath Path of the field where the Shape itself is defined
+     * @return this
+     */
+    public QB indexedShapePath(String indexedShapePath) {
+        this.indexedShapePath = indexedShapePath;
+        return (QB)this;
+    }
+
+    /**
+     * @return the path of the indexed Shape that will be used in the Query
+     */
+    public String indexedShapePath() {
+        return indexedShapePath;
+    }
+
+    /**
+     * Sets the optional routing to the indexed Shape that will be used in the query
+     *
+     * @param indexedShapeRouting indexed shape routing
+     * @return this
+     */
+    public QB indexedShapeRouting(String indexedShapeRouting) {
+        this.indexedShapeRouting = indexedShapeRouting;
+        return (QB)this;
+    }
+
+
+    /**
+     * @return the optional routing to the indexed Shape that will be used in the
+     *         Query
+     */
+    public String indexedShapeRouting() {
+        return indexedShapeRouting;
+    }
+
+    /**
+     * Sets the relation of query shape and indexed shape.
+     *
+     * @param relation relation of the shapes
+     * @return this
+     */
+    public QB relation(ShapeRelation relation) {
+        if (relation == null) {
+            throw new IllegalArgumentException("No Shape Relation defined");
+        }
+        this.relation = relation;
+        return (QB)this;
+    }
+
+    /**
+     * @return the relation of query shape and indexed shape to use in the Query
+     */
+    public ShapeRelation relation() {
+        return relation;
+    }
+
+    /**
+     * 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 AbstractGeometryQueryBuilder<QB> 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;
+    }
+
+    /** list of content types this shape query is compatible with */
+    protected abstract List validContentTypes();
+    /** builds the appropriate lucene shape query */
+    protected abstract Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType);
+    /** returns expected content type for this query */
+    protected abstract String queryFieldType();
+    /** writes the xcontent specific to this shape query */
+    protected abstract void doShapeQueryXContent(XContentBuilder builder, Params params) throws IOException;
+    /** creates a new ShapeQueryBuilder from the provided field name and shape builder */
+    protected abstract AbstractGeometryQueryBuilder<QB> newShapeQueryBuilder(String fieldName, Geometry shape);
+    /** creates a new ShapeQueryBuilder from the provided field name, supplier, indexed shape id, and indexed shape type */
+    protected abstract AbstractGeometryQueryBuilder<QB> newShapeQueryBuilder(String fieldName, Supplier<Geometry> shapeSupplier,
+                                                                             String indexedShapeId, String indexedShapeType);
+
+    /** returns true if the provided field type is valid for this query */
+    protected boolean isValidContentType(String typeName) {
+        return validContentTypes.contains(typeName);
+    }
+
+    @Override
+    protected Query doToQuery(QueryShardContext context) {
+        if (shape == null || supplier != null) {
+            throw new UnsupportedOperationException("query must be rewritten first");
+        }
+        final MappedFieldType fieldType = context.fieldMapper(fieldName);
+        if (fieldType == null) {
+            if (ignoreUnmapped) {
+                return new MatchNoDocsQuery();
+            } else {
+                throw new QueryShardException(context, "failed to find " + queryFieldType() + " field [" + fieldName + "]");
+            }
+        }
+
+        return buildShapeQuery(context, fieldType);
+    }
+
+    /**
+     * Fetches the Shape with the given ID in the given type and index.
+     *
+     * @param getRequest
+     *            GetRequest containing index, type and id
+     * @param path
+     *            Name or path of the field in the Shape Document where the
+     *            Shape itself is located
+     */
+    private void fetch(Client client, GetRequest getRequest, String path, ActionListener<Geometry> listener) {
+        getRequest.preference("_local");
+        client.get(getRequest, new ActionListener<GetResponse>(){
+
+            @Override
+            public void onResponse(GetResponse response) {
+                try {
+                    if (!response.isExists()) {
+                        throw new IllegalArgumentException("Shape with ID [" + getRequest.id() + "] in type [" + getRequest.type()
+                            + "] not found");
+                    }
+                    if (response.isSourceEmpty()) {
+                        throw new IllegalArgumentException("Shape with ID [" + getRequest.id() + "] in type [" + getRequest.type() +
+                            "] source disabled");
+                    }
+
+                    String[] pathElements = path.split("\\.");
+                    int currentPathSlot = 0;
+
+                    // It is safe to use EMPTY here because this never uses namedObject
+                    try (XContentParser parser = XContentHelper
+                        .createParser(NamedXContentRegistry.EMPTY,
+                            LoggingDeprecationHandler.INSTANCE, response.getSourceAsBytesRef())) {
+                        XContentParser.Token currentToken;
+                        while ((currentToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+                            if (currentToken == XContentParser.Token.FIELD_NAME) {
+                                if (pathElements[currentPathSlot].equals(parser.currentName())) {
+                                    parser.nextToken();
+                                    if (++currentPathSlot == pathElements.length) {
+                                        listener.onResponse(new GeometryParser(true, true, true).parse(parser));
+                                        return;
+                                    }
+                                } else {
+                                    parser.nextToken();
+                                    parser.skipChildren();
+                                }
+                            }
+                        }
+                        throw new IllegalStateException("Shape with name [" + getRequest.id() + "] found but missing " + path + " field");
+                    }
+                } catch (Exception e) {
+                    onFailure(e);
+                }
+            }
+
+            @Override
+            public void onFailure(Exception e) {
+                listener.onFailure(e);
+            }
+        });
+    }
+
+    @Override
+    protected void doXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject(getWriteableName());
+
+        builder.startObject(fieldName);
+
+        if (shape != null) {
+            builder.field(SHAPE_FIELD.getPreferredName());
+            GeoJson.toXContent(shape, builder, params);
+        } else {
+            builder.startObject(INDEXED_SHAPE_FIELD.getPreferredName())
+                .field(SHAPE_ID_FIELD.getPreferredName(), indexedShapeId);
+            if (indexedShapeType != null) {
+                builder.field(SHAPE_TYPE_FIELD.getPreferredName(), indexedShapeType);
+            }
+            if (indexedShapeIndex != null) {
+                builder.field(SHAPE_INDEX_FIELD.getPreferredName(), indexedShapeIndex);
+            }
+            if (indexedShapePath != null) {
+                builder.field(SHAPE_PATH_FIELD.getPreferredName(), indexedShapePath);
+            }
+            if (indexedShapeRouting != null) {
+                builder.field(SHAPE_ROUTING_FIELD.getPreferredName(), indexedShapeRouting);
+            }
+            builder.endObject();
+        }
+
+        if(relation != null) {
+            builder.field(RELATION_FIELD.getPreferredName(), relation.getRelationName());
+        }
+
+        doShapeQueryXContent(builder, params);
+        builder.endObject();
+        builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
+
+        printBoostAndQueryName(builder);
+
+        builder.endObject();
+    }
+
+    @Override
+    protected boolean doEquals(AbstractGeometryQueryBuilder other) {
+        return Objects.equals(fieldName, other.fieldName)
+            && Objects.equals(indexedShapeId, other.indexedShapeId)
+            && Objects.equals(indexedShapeIndex, other.indexedShapeIndex)
+            && Objects.equals(indexedShapePath, other.indexedShapePath)
+            && Objects.equals(indexedShapeType, other.indexedShapeType)
+            && Objects.equals(indexedShapeRouting, other.indexedShapeRouting)
+            && Objects.equals(relation, other.relation)
+            && Objects.equals(shape, other.shape)
+            && Objects.equals(supplier, other.supplier)
+            && Objects.equals(ignoreUnmapped, other.ignoreUnmapped);
+    }
+
+    @Override
+    protected int doHashCode() {
+        return Objects.hash(fieldName, indexedShapeId, indexedShapeIndex,
+            indexedShapePath, indexedShapeType, indexedShapeRouting, relation, shape, ignoreUnmapped, supplier);
+    }
+
+    @Override
+    protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
+        if (supplier != null) {
+            return supplier.get() == null ? this : newShapeQueryBuilder(this.fieldName, supplier.get()).relation(relation);
+        } else if (this.shape == null) {
+            SetOnce<Geometry> supplier = new SetOnce<>();
+            queryRewriteContext.registerAsyncAction((client, listener) -> {
+                GetRequest getRequest;
+                if (indexedShapeType == null) {
+                    getRequest = new GetRequest(indexedShapeIndex, indexedShapeId);
+                } else {
+                    getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
+                }
+                getRequest.routing(indexedShapeRouting);
+                fetch(client, getRequest, indexedShapePath, ActionListener.wrap(builder-> {
+                    supplier.set(builder);
+                    listener.onResponse(null);
+                }, listener::onFailure));
+            });
+            return newShapeQueryBuilder(this.fieldName, supplier::get, this.indexedShapeId, this.indexedShapeType).relation(relation);
+        }
+        return this;
+    }
+
+    /** local class that encapsulates xcontent parsed shape parameters */
+    protected abstract static class ParsedShapeQueryParams {
+        public String fieldName;
+        public ShapeRelation relation;
+        public ShapeBuilder shape;
+
+        public String id = null;
+        public String type = null;
+        public String index = null;
+        public String shapePath = null;
+        public String shapeRouting = null;
+
+        public float boost;
+        public String queryName;
+        public boolean ignoreUnmapped;
+
+        protected abstract boolean parseXContentField(XContentParser parser) throws IOException;
+    }
+
+    public static ParsedShapeQueryParams parsedParamsFromXContent(XContentParser parser, ParsedShapeQueryParams params)
+        throws IOException {
+        String fieldName = null;
+        XContentParser.Token token;
+        String currentFieldName = null;
+
+        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+            if (token == XContentParser.Token.FIELD_NAME) {
+                currentFieldName = parser.currentName();
+            } else if (token == XContentParser.Token.START_OBJECT) {
+                if (fieldName != null) {
+                    throw new ParsingException(parser.getTokenLocation(), "point specified twice. [" + currentFieldName + "]");
+                }
+                fieldName = currentFieldName;
+                while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+                    if (token == XContentParser.Token.FIELD_NAME) {
+                        currentFieldName = parser.currentName();
+                        token = parser.nextToken();
+                        if (RELATION_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                            params.relation = ShapeRelation.getRelationByName(parser.text());
+                            if (params.relation == null) {
+                                throw new ParsingException(parser.getTokenLocation(), "Unknown shape operation [" + parser.text() + " ]");
+                            }
+                        } else if (params.parseXContentField(parser)) {
+                            continue;
+                        } else if (INDEXED_SHAPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                            while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
+                                if (token == XContentParser.Token.FIELD_NAME) {
+                                    currentFieldName = parser.currentName();
+                                } else if (token.isValue()) {
+                                    if (SHAPE_ID_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                                        params.id = parser.text();
+                                    } else if (SHAPE_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                                        params.type = parser.text();
+                                    } else if (SHAPE_INDEX_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                                        params.index = parser.text();
+                                    } else if (SHAPE_PATH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                                        params.shapePath = parser.text();
+                                    } else if (SHAPE_ROUTING_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                                        params.shapeRouting = parser.text();
+                                    }
+                                } else {
+                                    throw new ParsingException(parser.getTokenLocation(), "unknown token [" + token
+                                        + "] after [" + currentFieldName + "]");
+                                }
+                            }
+                        } else {
+                            throw new ParsingException(parser.getTokenLocation(), "query does not support [" + currentFieldName + "]");
+                        }
+                    }
+                }
+            } else if (token.isValue()) {
+                if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                    params.boost = parser.floatValue();
+                } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                    params.queryName = parser.text();
+                } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
+                    params.ignoreUnmapped = parser.booleanValue();
+                } else {
+                    throw new ParsingException(parser.getTokenLocation(), "query does not support [" + currentFieldName + "]");
+                }
+            }
+        }
+        params.fieldName = fieldName;
+        return params;
+    }
+}

+ 259 - 637
server/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java

@@ -32,19 +32,11 @@ 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.apache.lucene.util.SetOnce;
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.action.get.GetRequest;
-import org.elasticsearch.action.get.GetResponse;
-import org.elasticsearch.client.Client;
 import org.elasticsearch.common.Nullable;
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.ParsingException;
-import org.elasticsearch.common.geo.GeoJson;
 import org.elasticsearch.common.geo.GeoShapeType;
-import org.elasticsearch.common.geo.GeometryIO;
 import org.elasticsearch.common.geo.GeometryIndexer;
-import org.elasticsearch.common.geo.GeometryParser;
 import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.geo.SpatialStrategy;
 import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
@@ -60,10 +52,7 @@ import org.elasticsearch.common.geo.parsers.ShapeParser;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.logging.DeprecationLogger;
-import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
-import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentHelper;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.geo.geometry.Circle;
 import org.elasticsearch.geo.geometry.Geometry;
@@ -76,6 +65,7 @@ import org.elasticsearch.geo.geometry.MultiPolygon;
 import org.elasticsearch.geo.geometry.Point;
 import org.elasticsearch.geo.geometry.Rectangle;
 import org.elasticsearch.index.mapper.BaseGeoShapeFieldMapper;
+import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.locationtech.jts.geom.Coordinate;
@@ -83,6 +73,7 @@ import org.locationtech.spatial4j.shape.Shape;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Supplier;
@@ -90,54 +81,17 @@ import java.util.function.Supplier;
 import static org.elasticsearch.index.mapper.GeoShapeFieldMapper.toLucenePolygon;
 
 /**
- * {@link QueryBuilder} that builds a GeoShape Query
+ * Derived {@link AbstractGeometryQueryBuilder} that builds a lat, lon GeoShape Query
  */
-public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuilder> {
+public class GeoShapeQueryBuilder extends AbstractGeometryQueryBuilder<GeoShapeQueryBuilder> {
     public static final String NAME = "geo_shape";
     private static final DeprecationLogger deprecationLogger = new DeprecationLogger(
         LogManager.getLogger(GeoShapeQueryBuilder.class));
-    static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Types are deprecated in [geo_shape] queries. " +
-        "The type should no longer be specified in the [indexed_shape] section.";
 
-    public static final String DEFAULT_SHAPE_INDEX_NAME = "shapes";
-    public static final String DEFAULT_SHAPE_FIELD_NAME = "shape";
-    public static final ShapeRelation DEFAULT_SHAPE_RELATION = ShapeRelation.INTERSECTS;
-
-    /**
-     * The default value for ignore_unmapped.
-     */
-    public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
-
-    private static final ParseField SHAPE_FIELD = new ParseField("shape");
-    private static final ParseField STRATEGY_FIELD = new ParseField("strategy");
-    private static final ParseField RELATION_FIELD = new ParseField("relation");
-    private static final ParseField INDEXED_SHAPE_FIELD = new ParseField("indexed_shape");
-    private static final ParseField SHAPE_ID_FIELD = new ParseField("id");
-    private static final ParseField SHAPE_TYPE_FIELD = new ParseField("type");
-    private static final ParseField SHAPE_INDEX_FIELD = new ParseField("index");
-    private static final ParseField SHAPE_PATH_FIELD = new ParseField("path");
-    private static final ParseField SHAPE_ROUTING_FIELD = new ParseField("routing");
-    private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
-
-    private final String fieldName;
-
-    private final Geometry shape;
-    private final Supplier<Geometry> supplier;
+    protected static final ParseField STRATEGY_FIELD = new ParseField("strategy");
 
     private SpatialStrategy strategy;
 
-    private final String indexedShapeId;
-    private final String indexedShapeType;
-
-
-    private String indexedShapeIndex = DEFAULT_SHAPE_INDEX_NAME;
-    private String indexedShapePath = DEFAULT_SHAPE_FIELD_NAME;
-    private String indexedShapeRouting;
-
-    private ShapeRelation relation = DEFAULT_SHAPE_RELATION;
-
-    private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
-
     /**
      * Creates a new GeoShapeQueryBuilder whose Query will be against the given
      * field name using the given Shape
@@ -146,12 +100,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
      *            Name of the field that will be queried
      * @param shape
      *            Shape used in the Query
-     *
-     * @deprecated use {@link #GeoShapeQueryBuilder(String, Geometry)} instead
      */
-    @Deprecated
-    public GeoShapeQueryBuilder(String fieldName, ShapeBuilder shape) {
-        this(fieldName, shape == null ? null : shape.buildGeometry(), null, null);
+    public GeoShapeQueryBuilder(String fieldName, Geometry shape) {
+        super(fieldName, shape);
     }
 
     /**
@@ -162,23 +113,17 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
      *            Name of the field that will be queried
      * @param shape
      *            Shape used in the Query
+     *
+     * @deprecated use {@link #GeoShapeQueryBuilder(String, Geometry)} instead
      */
-    public GeoShapeQueryBuilder(String fieldName, Geometry shape) {
-        this(fieldName, shape, null, null);
+    @Deprecated
+    public GeoShapeQueryBuilder(String fieldName, ShapeBuilder shape) {
+        super(fieldName, shape);
     }
 
-
-    /**
-     * Creates a new GeoShapeQueryBuilder whose Query will be against the given
-     * field name and will use the Shape found with the given ID
-     *
-     * @param fieldName
-     *            Name of the field that will be filtered
-     * @param indexedShapeId
-     *            ID of the indexed Shape that will be used in the Query
-     */
-    public GeoShapeQueryBuilder(String fieldName, String indexedShapeId) {
-        this(fieldName, (Geometry) null, indexedShapeId, null);
+    public GeoShapeQueryBuilder(String fieldName, Supplier<Geometry> shapeSupplier, String indexedShapeId,
+                                @Nullable String indexedShapeType) {
+        super(fieldName, shapeSupplier, indexedShapeId, indexedShapeType);
     }
 
     /**
@@ -196,108 +141,36 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
      */
     @Deprecated
     public GeoShapeQueryBuilder(String fieldName, String indexedShapeId, String indexedShapeType) {
-        this(fieldName, (Geometry) null, indexedShapeId, indexedShapeType);
-    }
-
-    private GeoShapeQueryBuilder(String fieldName, Geometry shape, String indexedShapeId, @Nullable String indexedShapeType) {
-        if (fieldName == null) {
-            throw new IllegalArgumentException("fieldName is required");
-        }
-        if (shape == null && indexedShapeId == null) {
-            throw new IllegalArgumentException("either shape or indexedShapeId is required");
-        }
-        this.fieldName = fieldName;
-        this.shape = shape;
-        this.indexedShapeId = indexedShapeId;
-        this.indexedShapeType = indexedShapeType;
-        this.supplier = null;
-    }
-
-    private GeoShapeQueryBuilder(String fieldName, Supplier<Geometry> supplier, String indexedShapeId,
-            @Nullable String indexedShapeType) {
-        this.fieldName = fieldName;
-        this.shape = null;
-        this.supplier = supplier;
-        this.indexedShapeId = indexedShapeId;
-        this.indexedShapeType = indexedShapeType;
+        super(fieldName, indexedShapeId, indexedShapeType);
     }
 
     /**
-     * Read from a stream.
+     * Creates a new GeoShapeQueryBuilder whose Query will be against the given
+     * field name and will use the Shape found with the given ID
+     *
+     * @param fieldName
+     *            Name of the field that will be filtered
+     * @param indexedShapeId
+     *            ID of the indexed Shape that will be used in the Query
      */
+    public GeoShapeQueryBuilder(String fieldName, String indexedShapeId) {
+        super(fieldName, indexedShapeId);
+    }
+
     public GeoShapeQueryBuilder(StreamInput in) throws IOException {
         super(in);
-        fieldName = in.readString();
-        if (in.readBoolean()) {
-            shape = GeometryIO.readGeometry(in);
-            indexedShapeId = null;
-            indexedShapeType = null;
-        } else {
-            shape = null;
-            indexedShapeId = in.readOptionalString();
-            indexedShapeType = in.readOptionalString();
-            indexedShapeIndex = in.readOptionalString();
-            indexedShapePath = in.readOptionalString();
-            indexedShapeRouting = in.readOptionalString();
-        }
-        relation = ShapeRelation.readFromStream(in);
         strategy = in.readOptionalWriteable(SpatialStrategy::readFromStream);
-        ignoreUnmapped = in.readBoolean();
-        supplier = null;
     }
 
     @Override
     protected void doWriteTo(StreamOutput out) throws IOException {
-        if (supplier != null) {
-            throw new IllegalStateException("supplier must be null, can't serialize suppliers, missing a rewriteAndFetch?");
-        }
-        out.writeString(fieldName);
-        boolean hasShape = shape != null;
-        out.writeBoolean(hasShape);
-        if (hasShape) {
-            GeometryIO.writeGeometry(out, shape);;
-        } else {
-            out.writeOptionalString(indexedShapeId);
-            out.writeOptionalString(indexedShapeType);
-            out.writeOptionalString(indexedShapeIndex);
-            out.writeOptionalString(indexedShapePath);
-            out.writeOptionalString(indexedShapeRouting);
-        }
-        relation.writeTo(out);
+        super.doWriteTo(out);
         out.writeOptionalWriteable(strategy);
-        out.writeBoolean(ignoreUnmapped);
-    }
-
-    /**
-     * @return the name of the field that will be queried
-     */
-    public String fieldName() {
-        return fieldName;
-    }
-
-    /**
-     * @return the shape used in the Query
-     */
-    public Geometry shape() {
-        return shape;
-    }
-
-    /**
-     * @return the ID of the indexed Shape that will be used in the Query
-     */
-    public String indexedShapeId() {
-        return indexedShapeId;
     }
 
-    /**
-     * @return the document type of the indexed Shape that will be used in the
-     *         Query
-     *
-     * @deprecated Types are in the process of being removed.
-     */
-    @Deprecated
-    public String indexedShapeType() {
-        return indexedShapeType;
+    @Override
+    public String getWriteableName() {
+        return NAME;
     }
 
     /**
@@ -312,12 +185,11 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
     public GeoShapeQueryBuilder strategy(SpatialStrategy strategy) {
         if (strategy != null && strategy == SpatialStrategy.TERM && relation != ShapeRelation.INTERSECTS) {
             throw new IllegalArgumentException("strategy [" + strategy.getStrategyName() + "] only supports relation ["
-                    + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
+                + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
         }
         this.strategy = strategy;
         return this;
     }
-
     /**
      * @return The spatial strategy to use for building the geo shape Query
      */
@@ -325,122 +197,39 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
         return strategy;
     }
 
-    /**
-     * Sets the name of the index where the indexed Shape can be found
-     *
-     * @param indexedShapeIndex Name of the index where the indexed Shape is
-     * @return this
-     */
-    public GeoShapeQueryBuilder indexedShapeIndex(String indexedShapeIndex) {
-        this.indexedShapeIndex = indexedShapeIndex;
-        return this;
-    }
-
-    /**
-     * @return the index name for the indexed Shape that will be used in the
-     *         Query
-     */
-    public String indexedShapeIndex() {
-        return indexedShapeIndex;
-    }
-
-    /**
-     * Sets the path of the field in the indexed Shape document that has the Shape itself
-     *
-     * @param indexedShapePath Path of the field where the Shape itself is defined
-     * @return this
-     */
-    public GeoShapeQueryBuilder indexedShapePath(String indexedShapePath) {
-        this.indexedShapePath = indexedShapePath;
-        return this;
-    }
-
-    /**
-     * @return the path of the indexed Shape that will be used in the Query
-     */
-    public String indexedShapePath() {
-        return indexedShapePath;
-    }
-
-    /**
-     * Sets the optional routing to the indexed Shape that will be used in the query
-     *
-     * @param indexedShapeRouting indexed shape routing
-     * @return this
-     */
-    public GeoShapeQueryBuilder indexedShapeRouting(String indexedShapeRouting) {
-        this.indexedShapeRouting = indexedShapeRouting;
-        return this;
+    @Override
+    protected List validContentTypes() {
+        return Arrays.asList(BaseGeoShapeFieldMapper.CONTENT_TYPE);
     }
 
-
-    /**
-     * @return the optional routing to the indexed Shape that will be used in the
-     *         Query
-     */
-    public String indexedShapeRouting() {
-        return indexedShapeRouting;
+    @Override
+    public String queryFieldType() {
+        return BaseGeoShapeFieldMapper.CONTENT_TYPE;
     }
 
-    /**
-     * Sets the relation of query shape and indexed shape.
-     *
-     * @param relation relation of the shapes
-     * @return this
-     */
-    public GeoShapeQueryBuilder relation(ShapeRelation relation) {
-        if (relation == null) {
-            throw new IllegalArgumentException("No Shape Relation defined");
-        }
-        if (SpatialStrategy.TERM.equals(strategy) && relation != ShapeRelation.INTERSECTS) {
-            throw new IllegalArgumentException("current strategy [" + strategy.getStrategyName() + "] only supports relation ["
-                + ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]");
+    @Override
+    public void doShapeQueryXContent(XContentBuilder builder, Params params) throws IOException {
+        if (strategy != null) {
+            builder.field(STRATEGY_FIELD.getPreferredName(), strategy.getStrategyName());
         }
-        this.relation = relation;
-        return this;
     }
 
-    /**
-     * @return the relation of query shape and indexed shape to use in the Query
-     */
-    public ShapeRelation relation() {
-        return relation;
-    }
-
-    /**
-     * 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 GeoShapeQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
-        this.ignoreUnmapped = ignoreUnmapped;
-        return this;
+    @Override
+    protected GeoShapeQueryBuilder newShapeQueryBuilder(String fieldName, Geometry shape) {
+        return new GeoShapeQueryBuilder(fieldName, shape);
     }
 
-    /**
-     * 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 GeoShapeQueryBuilder newShapeQueryBuilder(String fieldName, Supplier<Geometry> shapeSupplier,
+                                                        String indexedShapeId, String indexedShapeType) {
+        return new GeoShapeQueryBuilder(fieldName, shapeSupplier, indexedShapeId, indexedShapeType);
     }
 
     @Override
-    protected Query doToQuery(QueryShardContext context) {
-        if (shape == null || supplier != null) {
-            throw new UnsupportedOperationException("query must be rewritten first");
-        }
-        final MappedFieldType fieldType = context.fieldMapper(fieldName);
-        if (fieldType == null) {
-            if (ignoreUnmapped) {
-                return new MatchNoDocsQuery();
-            } else {
-                throw new QueryShardException(context, "failed to find geo_shape field [" + fieldName + "]");
-            }
-        } else if (fieldType.typeName().equals(BaseGeoShapeFieldMapper.CONTENT_TYPE) == false) {
+    public Query buildShapeQuery(QueryShardContext context, MappedFieldType fieldType) {
+        if (fieldType.typeName().equals(BaseGeoShapeFieldMapper.CONTENT_TYPE) == false) {
             throw new QueryShardException(context,
-                    "Field [" + fieldName + "] is not of type [geo_shape] but of type [" + fieldType.typeName() + "]");
+                "Field [" + fieldName + "] is not of type [" + queryFieldType() + "] but of type [" + fieldType.typeName() + "]");
         }
 
         final BaseGeoShapeFieldMapper.BaseGeoShapeFieldType ft = (BaseGeoShapeFieldMapper.BaseGeoShapeFieldType) fieldType;
@@ -471,13 +260,41 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
         return query;
     }
 
+    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();
+    }
+
     private Query getVectorQuery(QueryShardContext context, Geometry queryShape) {
         // CONTAINS queries are not yet supported by VECTOR strategy
         if (relation == ShapeRelation.CONTAINS) {
             throw new QueryShardException(context,
                 ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "]");
         }
+        // wrap geoQuery as a ConstantScoreQuery
+        return getVectorQueryFromShape(context, queryShape);
+    }
 
+    protected Query getVectorQueryFromShape(QueryShardContext context, Geometry queryShape) {
         // TODO: Move this to QueryShardContext
         GeometryIndexer geometryIndexer = new GeometryIndexer(true);
 
@@ -486,463 +303,268 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
         if (processedShape == null) {
             return new MatchNoDocsQuery();
         }
+        return queryShape.visit(new ShapeVisitor(context));
+    }
 
-        return processedShape.visit(new GeometryVisitor<Query, RuntimeException>() {
+    public static ShapeBuilder<?, ?, ?> geometryToShapeBuilder(Geometry geometry) {
+        ShapeBuilder<?, ?, ?> shapeBuilder = geometry.visit(new GeometryVisitor<>() {
             @Override
-            public Query visit(Circle circle) {
-                throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle");
+            public ShapeBuilder<?, ?, ?> visit(Circle circle) {
+                throw new UnsupportedOperationException("circle is not supported");
             }
 
             @Override
-            public Query visit(GeometryCollection<?> collection) {
-                BooleanQuery.Builder bqb = new BooleanQuery.Builder();
-                visit(bqb, collection);
-                return bqb.build();
-            }
-
-            private void visit(BooleanQuery.Builder bqb, GeometryCollection<?> collection) {
-                for (Geometry shape : collection) {
-                    if (shape instanceof MultiPoint) {
-                        // Flatten multipoints
-                        visit(bqb, (GeometryCollection<?>) shape);
-                    } else {
-                        bqb.add(shape.visit(this), BooleanClause.Occur.SHOULD);
-                    }
+            public ShapeBuilder<?, ?, ?> visit(GeometryCollection<?> collection) {
+                GeometryCollectionBuilder shapes = new GeometryCollectionBuilder();
+                for (Geometry geometry : collection) {
+                    shapes.shape(geometry.visit(this));
                 }
+                return shapes;
             }
 
             @Override
-            public Query visit(org.elasticsearch.geo.geometry.Line line) {
-                return LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), new Line(line.getLats(), line.getLons()));
-            }
-
-            @Override
-            public Query visit(LinearRing ring) {
-                throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing");
-            }
-
-            @Override
-            public Query visit(MultiLine multiLine) {
-                Line[] lines = new Line[multiLine.size()];
-                for (int i=0; i<multiLine.size(); i++) {
-                    lines[i] = new Line(multiLine.get(i).getLats(), multiLine.get(i).getLons());
+            public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Line line) {
+                List<Coordinate> coordinates = new ArrayList<>();
+                for (int i = 0; i < line.length(); i++) {
+                    coordinates.add(new Coordinate(line.getLon(i), line.getLat(i), line.getAlt(i)));
                 }
-                return LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), lines);
+                return new LineStringBuilder(coordinates);
             }
 
             @Override
-            public Query visit(MultiPoint multiPoint) {
-                throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.MULTIPOINT +
-                    " queries");
+            public ShapeBuilder<?, ?, ?> visit(LinearRing ring) {
+                throw new UnsupportedOperationException("circle is not supported");
             }
 
             @Override
-            public Query visit(MultiPolygon multiPolygon) {
-                Polygon[] polygons = new Polygon[multiPolygon.size()];
-                for (int i=0; i<multiPolygon.size(); i++) {
-                    polygons[i] = toLucenePolygon(multiPolygon.get(i));
+            public ShapeBuilder<?, ?, ?> visit(MultiLine multiLine) {
+                MultiLineStringBuilder lines = new MultiLineStringBuilder();
+                for (int i = 0; i < multiLine.size(); i++) {
+                    lines.linestring((LineStringBuilder) visit(multiLine.get(i)));
                 }
-                return LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), polygons);
+                return lines;
             }
 
             @Override
-            public Query visit(Point point) {
-                return LatLonShape.newBoxQuery(fieldName, relation.getLuceneRelation(),
-                    point.getLat(), point.getLat(), point.getLon(), point.getLon());
+            public ShapeBuilder<?, ?, ?> visit(MultiPoint multiPoint) {
+                List<Coordinate> coordinates = new ArrayList<>();
+                for (int i = 0; i < multiPoint.size(); i++) {
+                    Point p = multiPoint.get(i);
+                    coordinates.add(new Coordinate(p.getLon(), p.getLat(), p.getAlt()));
+                }
+                return new MultiPointBuilder(coordinates);
             }
 
             @Override
-            public Query visit(org.elasticsearch.geo.geometry.Polygon polygon) {
-                return LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), toLucenePolygon(polygon));
+            public ShapeBuilder<?, ?, ?> visit(MultiPolygon multiPolygon) {
+                MultiPolygonBuilder polygons = new MultiPolygonBuilder();
+                for (int i = 0; i < multiPolygon.size(); i++) {
+                    polygons.polygon((PolygonBuilder) visit(multiPolygon.get(i)));
+                }
+                return polygons;
             }
 
             @Override
-            public Query visit(org.elasticsearch.geo.geometry.Rectangle r) {
-                return LatLonShape.newBoxQuery(fieldName(), relation.getLuceneRelation(),
-                    r.getMinLat(), r.getMaxLat(), r.getMinLon(), r.getMaxLon());
+            public ShapeBuilder<?, ?, ?> visit(Point point) {
+                return new PointBuilder(point.getLon(), point.getLat());
             }
-        });
-    }
-
-    /**
-     * Fetches the Shape with the given ID in the given type and index.
-     *
-     * @param getRequest
-     *            GetRequest containing index, type and id
-     * @param path
-     *            Name or path of the field in the Shape Document where the
-     *            Shape itself is located
-     */
-    private void fetch(Client client, GetRequest getRequest, String path, ActionListener<Geometry> listener) {
-        getRequest.preference("_local");
-        client.get(getRequest, new ActionListener<GetResponse>(){
 
             @Override
-            public void onResponse(GetResponse response) {
-                try {
-                    if (!response.isExists()) {
-                        throw new IllegalArgumentException("Shape with ID [" + getRequest.id() + "] in type [" + getRequest.type()
-                            + "] not found");
-                    }
-                    if (response.isSourceEmpty()) {
-                        throw new IllegalArgumentException("Shape with ID [" + getRequest.id() + "] in type [" + getRequest.type() +
-                            "] source disabled");
-                    }
-
-                    String[] pathElements = path.split("\\.");
-                    int currentPathSlot = 0;
-
-                    // It is safe to use EMPTY here because this never uses namedObject
-                    try (XContentParser parser = XContentHelper
-                            .createParser(NamedXContentRegistry.EMPTY,
-                                    LoggingDeprecationHandler.INSTANCE, response.getSourceAsBytesRef())) {
-                        XContentParser.Token currentToken;
-                        while ((currentToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
-                            if (currentToken == XContentParser.Token.FIELD_NAME) {
-                                if (pathElements[currentPathSlot].equals(parser.currentName())) {
-                                    parser.nextToken();
-                                    if (++currentPathSlot == pathElements.length) {
-                                        listener.onResponse(new GeometryParser(true, true, true).parse(parser));
-                                        return;
-                                    }
-                                } else {
-                                    parser.nextToken();
-                                    parser.skipChildren();
-                                }
-                            }
-                        }
-                        throw new IllegalStateException("Shape with name [" + getRequest.id() + "] found but missing " + path + " field");
-                    }
-                } catch (Exception e) {
-                    onFailure(e);
+            public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Polygon polygon) {
+                PolygonBuilder polygonBuilder =
+                    new PolygonBuilder((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getPolygon()),
+                        ShapeBuilder.Orientation.RIGHT, false);
+                for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
+                    polygonBuilder.hole((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getHole(i)));
                 }
+                return polygonBuilder;
             }
 
             @Override
-            public void onFailure(Exception e) {
-                listener.onFailure(e);
+            public ShapeBuilder<?, ?, ?> visit(Rectangle rectangle) {
+                return new EnvelopeBuilder(new Coordinate(rectangle.getMinLon(), rectangle.getMaxLat()),
+                    new Coordinate(rectangle.getMaxLon(), rectangle.getMinLat()));
             }
         });
-
-    }
-
-    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 + "]");
-        }
+        return shapeBuilder;
     }
 
-    @Override
-    protected void doXContent(XContentBuilder builder, Params params) throws IOException {
-        builder.startObject(NAME);
-
-        builder.startObject(fieldName);
+    private class ShapeVisitor implements GeometryVisitor<Query, RuntimeException> {
+        QueryShardContext context;
+        MappedFieldType fieldType;
 
-        if (strategy != null) {
-            builder.field(STRATEGY_FIELD.getPreferredName(), strategy.getStrategyName());
+        ShapeVisitor(QueryShardContext context) {
+            this.context = context;
+            this.fieldType = context.fieldMapper(fieldName);
         }
 
-        if (shape != null) {
-            builder.field(SHAPE_FIELD.getPreferredName());
-            GeoJson.toXContent(shape, builder,params);
-        } else {
-            builder.startObject(INDEXED_SHAPE_FIELD.getPreferredName())
-                    .field(SHAPE_ID_FIELD.getPreferredName(), indexedShapeId);
-            if (indexedShapeType != null) {
-                builder.field(SHAPE_TYPE_FIELD.getPreferredName(), indexedShapeType);
-            }
-            if (indexedShapeIndex != null) {
-                builder.field(SHAPE_INDEX_FIELD.getPreferredName(), indexedShapeIndex);
-            }
-            if (indexedShapePath != null) {
-                builder.field(SHAPE_PATH_FIELD.getPreferredName(), indexedShapePath);
-            }
-            if (indexedShapeRouting != null) {
-                builder.field(SHAPE_ROUTING_FIELD.getPreferredName(), indexedShapeRouting);
-            }
-            builder.endObject();
+        @Override
+        public Query visit(Circle circle) {
+            throw new QueryShardException(context, "Field [" + fieldName + "] found and unknown shape Circle");
         }
 
-        if(relation != null) {
-            builder.field(RELATION_FIELD.getPreferredName(), relation.getRelationName());
+        @Override
+        public Query visit(GeometryCollection<?> collection) {
+            BooleanQuery.Builder bqb = new BooleanQuery.Builder();
+            visit(bqb, collection);
+            return bqb.build();
         }
 
-        builder.endObject();
-        builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
-
-        printBoostAndQueryName(builder);
-
-        builder.endObject();
-    }
-
-    public static GeoShapeQueryBuilder fromXContent(XContentParser parser) throws IOException {
-        String fieldName = null;
-        ShapeRelation shapeRelation = null;
-        SpatialStrategy strategy = null;
-        ShapeBuilder shape = null;
-
-        String id = null;
-        String type = null;
-        String index = null;
-        String shapePath = null;
-        String shapeRouting = null;
-
-        XContentParser.Token token;
-        String currentFieldName = null;
-        float boost = AbstractQueryBuilder.DEFAULT_BOOST;
-        String queryName = 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 (token == XContentParser.Token.START_OBJECT) {
-                if (fieldName != null) {
-                    throw new ParsingException(parser.getTokenLocation(), "[" +
-                            GeoShapeQueryBuilder.NAME + "] point specified twice. [" + currentFieldName + "]");
-                }
-                fieldName = currentFieldName;
-                while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
-                    if (token == XContentParser.Token.FIELD_NAME) {
-                        currentFieldName = parser.currentName();
-                        token = parser.nextToken();
-                        if (SHAPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                            shape = ShapeParser.parse(parser);
-                        } else if (STRATEGY_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                            String strategyName = parser.text();
-                            strategy = SpatialStrategy.fromString(strategyName);
-                            if (strategy == null) {
-                                throw new ParsingException(parser.getTokenLocation(), "Unknown strategy [" + strategyName + " ]");
-                            }
-                        } else if (RELATION_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                            shapeRelation = ShapeRelation.getRelationByName(parser.text());
-                            if (shapeRelation == null) {
-                                throw new ParsingException(parser.getTokenLocation(), "Unknown shape operation [" + parser.text() + " ]");
-                            }
-                        } else if (INDEXED_SHAPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                            while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
-                                if (token == XContentParser.Token.FIELD_NAME) {
-                                    currentFieldName = parser.currentName();
-                                } else if (token.isValue()) {
-                                    if (SHAPE_ID_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                                        id = parser.text();
-                                    } else if (SHAPE_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                                        type = parser.text();
-                                    } else if (SHAPE_INDEX_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                                        index = parser.text();
-                                    } else if (SHAPE_PATH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                                        shapePath = parser.text();
-                                    } else if (SHAPE_ROUTING_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                                        shapeRouting = parser.text();
-                                    }
-                                } else {
-                                    throw new ParsingException(parser.getTokenLocation(), "[" + GeoShapeQueryBuilder.NAME +
-                                            "] unknown token [" + token + "] after [" + currentFieldName + "]");
-                                }
-                            }
-                        } else {
-                            throw new ParsingException(parser.getTokenLocation(), "[" + GeoShapeQueryBuilder.NAME +
-                                    "] query does not support [" + currentFieldName + "]");
-                        }
-                    }
-                }
-            } else if (token.isValue()) {
-                if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                    boost = parser.floatValue();
-                } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                    queryName = parser.text();
-                } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
-                    ignoreUnmapped = parser.booleanValue();
+        private void visit(BooleanQuery.Builder bqb, GeometryCollection<?> collection) {
+            for (Geometry shape : collection) {
+                if (shape instanceof MultiPoint) {
+                    // Flatten multipoints
+                    visit(bqb, (GeometryCollection<?>) shape);
                 } else {
-                    throw new ParsingException(parser.getTokenLocation(), "[" + GeoShapeQueryBuilder.NAME +
-                            "] query does not support [" + currentFieldName + "]");
+                    bqb.add(shape.visit(this), BooleanClause.Occur.SHOULD);
                 }
             }
         }
-        GeoShapeQueryBuilder builder;
-        if (type != null) {
-            deprecationLogger.deprecatedAndMaybeLog(
-                "geo_share_query_with_types", TYPES_DEPRECATION_MESSAGE);
+
+        @Override
+        public Query visit(org.elasticsearch.geo.geometry.Line line) {
+            validateIsGeoShapeFieldType();
+            return LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), new Line(line.getLats(), line.getLons()));
         }
 
-        if (shape != null) {
-            builder = new GeoShapeQueryBuilder(fieldName, shape);
-        } else {
-            builder = new GeoShapeQueryBuilder(fieldName, id, type);
+        @Override
+        public Query visit(LinearRing ring) {
+            throw new QueryShardException(context, "Field [" + fieldName + "] found and unsupported shape LinearRing");
         }
-        if (index != null) {
-            builder.indexedShapeIndex(index);
+
+        @Override
+        public Query visit(MultiLine multiLine) {
+            validateIsGeoShapeFieldType();
+            Line[] lines = new Line[multiLine.size()];
+            for (int i=0; i<multiLine.size(); i++) {
+                lines[i] = new Line(multiLine.get(i).getLats(), multiLine.get(i).getLons());
+            }
+            return LatLonShape.newLineQuery(fieldName(), relation.getLuceneRelation(), lines);
         }
-        if (shapePath != null) {
-            builder.indexedShapePath(shapePath);
+
+        @Override
+        public Query visit(MultiPoint multiPoint) {
+            throw new QueryShardException(context, "Field [" + fieldName + "] does not support " + GeoShapeType.MULTIPOINT +
+                " queries");
         }
-        if (shapeRouting != null) {
-            builder.indexedShapeRouting(shapeRouting);
+
+        @Override
+        public Query visit(MultiPolygon multiPolygon) {
+            Polygon[] polygons = new Polygon[multiPolygon.size()];
+            for (int i=0; i<multiPolygon.size(); i++) {
+                polygons[i] = toLucenePolygon(multiPolygon.get(i));
+            }
+            return LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), polygons);
         }
-        if (shapeRelation != null) {
-            builder.relation(shapeRelation);
+
+        @Override
+        public Query visit(Point point) {
+            validateIsGeoShapeFieldType();
+            return LatLonShape.newBoxQuery(fieldName, relation.getLuceneRelation(),
+                point.getLat(), point.getLat(), point.getLon(), point.getLon());
         }
-        if (strategy != null) {
-            builder.strategy(strategy);
+
+        @Override
+        public Query visit(org.elasticsearch.geo.geometry.Polygon polygon) {
+            return LatLonShape.newPolygonQuery(fieldName(), relation.getLuceneRelation(), toLucenePolygon(polygon));
         }
-        if (queryName != null) {
-            builder.queryName(queryName);
+
+        @Override
+        public Query visit(org.elasticsearch.geo.geometry.Rectangle r) {
+            return LatLonShape.newBoxQuery(fieldName(), relation.getLuceneRelation(),
+                r.getMinLat(), r.getMaxLat(), r.getMinLon(), r.getMaxLon());
+        }
+
+        private void validateIsGeoShapeFieldType() {
+            if (fieldType instanceof GeoShapeFieldMapper.GeoShapeFieldType == false) {
+                throw new QueryShardException(context, "Expected " + GeoShapeFieldMapper.CONTENT_TYPE
+                    + " field type for Field [" + fieldName + "] but found " + fieldType.typeName());
+            }
         }
-        builder.boost(boost);
-        builder.ignoreUnmapped(ignoreUnmapped);
-        return builder;
     }
 
     @Override
     protected boolean doEquals(GeoShapeQueryBuilder other) {
-        return Objects.equals(fieldName, other.fieldName)
-                && Objects.equals(indexedShapeId, other.indexedShapeId)
-                && Objects.equals(indexedShapeIndex, other.indexedShapeIndex)
-                && Objects.equals(indexedShapePath, other.indexedShapePath)
-                && Objects.equals(indexedShapeType, other.indexedShapeType)
-                && Objects.equals(indexedShapeRouting, other.indexedShapeRouting)
-                && Objects.equals(relation, other.relation)
-                && Objects.equals(shape, other.shape)
-                && Objects.equals(supplier, other.supplier)
-                && Objects.equals(strategy, other.strategy)
-                && Objects.equals(ignoreUnmapped, other.ignoreUnmapped);
+        return super.doEquals((AbstractGeometryQueryBuilder)other)
+            && Objects.equals(strategy, other.strategy);
     }
 
     @Override
     protected int doHashCode() {
-        return Objects.hash(fieldName, indexedShapeId, indexedShapeIndex,
-                indexedShapePath, indexedShapeType, indexedShapeRouting, relation, shape, strategy, ignoreUnmapped, supplier);
+        return Objects.hash(super.doHashCode(), strategy);
     }
 
     @Override
-    public String getWriteableName() {
-        return NAME;
+    protected GeoShapeQueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
+        GeoShapeQueryBuilder builder = (GeoShapeQueryBuilder)super.doRewrite(queryRewriteContext);
+        builder.strategy(strategy);
+        return builder;
     }
 
-    @Override
-    protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
-        if (supplier != null) {
-            return supplier.get() == null ? this : new GeoShapeQueryBuilder(this.fieldName, supplier.get()).relation(relation).strategy
-                (strategy);
-        } else if (this.shape == null) {
-            SetOnce<Geometry> supplier = new SetOnce<>();
-            queryRewriteContext.registerAsyncAction((client, listener) -> {
-                GetRequest getRequest;
-                if (indexedShapeType == null) {
-                    getRequest = new GetRequest(indexedShapeIndex, indexedShapeId);
+    private static class ParsedGeoShapeQueryParams extends ParsedShapeQueryParams {
+        SpatialStrategy strategy;
+
+        @Override
+        protected boolean parseXContentField(XContentParser parser) throws IOException {
+            SpatialStrategy strategy;
+            if (SHAPE_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) {
+                this.shape = ShapeParser.parse(parser);
+                return true;
+            } else if (STRATEGY_FIELD.match(parser.currentName(), parser.getDeprecationHandler())) {
+                String strategyName = parser.text();
+                strategy = SpatialStrategy.fromString(strategyName);
+                if (strategy == null) {
+                    throw new ParsingException(parser.getTokenLocation(), "Unknown strategy [" + strategyName + " ]");
                 } else {
-                    getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
+                    this.strategy = strategy;
                 }
-                getRequest.routing(indexedShapeRouting);
-                fetch(client, getRequest, indexedShapePath, ActionListener.wrap(builder-> {
-                    supplier.set(builder);
-                    listener.onResponse(null);
-                }, listener::onFailure));
-            });
-            return new GeoShapeQueryBuilder(this.fieldName, supplier::get, this.indexedShapeId, this.indexedShapeType).relation(relation)
-                .strategy(strategy);
+                return true;
+            }
+            return false;
         }
-        return this;
-    }
-
-    /**
-     * 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<>() {
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(Circle circle) {
-                throw new UnsupportedOperationException("circle is not supported");
-            }
+    public static GeoShapeQueryBuilder fromXContent(XContentParser parser) throws IOException {
+        ParsedGeoShapeQueryParams pgsqp =
+            (ParsedGeoShapeQueryParams) AbstractGeometryQueryBuilder.parsedParamsFromXContent(parser, new ParsedGeoShapeQueryParams());
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(GeometryCollection<?> collection) {
-                GeometryCollectionBuilder shapes = new GeometryCollectionBuilder();
-                for (Geometry geometry : collection) {
-                    shapes.shape(geometry.visit(this));
-                }
-                return shapes;
-            }
+        GeoShapeQueryBuilder builder;
+        if (pgsqp.type != null) {
+            deprecationLogger.deprecatedAndMaybeLog("geo_share_query_with_types", TYPES_DEPRECATION_MESSAGE);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Line line) {
-                List<Coordinate> coordinates = new ArrayList<>();
-                for (int i = 0; i < line.length(); i++) {
-                    coordinates.add(new Coordinate(line.getLon(i), line.getLat(i), line.getAlt(i)));
-                }
-                return new LineStringBuilder(coordinates);
-            }
+        if (pgsqp.shape != null) {
+            builder = new GeoShapeQueryBuilder(pgsqp.fieldName, pgsqp.shape);
+        } else {
+            builder = new GeoShapeQueryBuilder(pgsqp.fieldName, pgsqp.id, pgsqp.type);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(LinearRing ring) {
-                throw new UnsupportedOperationException("circle is not supported");
-            }
+        if (pgsqp.index != null) {
+            builder.indexedShapeIndex(pgsqp.index);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(MultiLine multiLine) {
-                MultiLineStringBuilder lines = new MultiLineStringBuilder();
-                for (int i = 0; i < multiLine.size(); i++) {
-                    lines.linestring((LineStringBuilder) visit(multiLine.get(i)));
-                }
-                return lines;
-            }
+        if (pgsqp.shapePath != null) {
+            builder.indexedShapePath(pgsqp.shapePath);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(MultiPoint multiPoint) {
-                List<Coordinate> coordinates = new ArrayList<>();
-                for (int i = 0; i < multiPoint.size(); i++) {
-                    Point p = multiPoint.get(i);
-                    coordinates.add(new Coordinate(p.getLon(), p.getLat(), p.getAlt()));
-                }
-                return new MultiPointBuilder(coordinates);
-            }
+        if (pgsqp.shapeRouting != null) {
+            builder.indexedShapeRouting(pgsqp.shapeRouting);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(MultiPolygon multiPolygon) {
-                MultiPolygonBuilder polygons = new MultiPolygonBuilder();
-                for (int i = 0; i < multiPolygon.size(); i++) {
-                    polygons.polygon((PolygonBuilder) visit(multiPolygon.get(i)));
-                }
-                return polygons;
-            }
+        if (pgsqp.relation != null) {
+            builder.relation(pgsqp.relation);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(Point point) {
-                return new PointBuilder(point.getLon(), point.getLat());
-            }
+        if (pgsqp.strategy != null) {
+            builder.strategy(pgsqp.strategy);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(org.elasticsearch.geo.geometry.Polygon polygon) {
-                PolygonBuilder polygonBuilder =
-                    new PolygonBuilder((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getPolygon()),
-                        ShapeBuilder.Orientation.RIGHT, false);
-                for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
-                    polygonBuilder.hole((LineStringBuilder) visit((org.elasticsearch.geo.geometry.Line) polygon.getHole(i)));
-                }
-                return polygonBuilder;
-            }
+        if (pgsqp.queryName != null) {
+            builder.queryName(pgsqp.queryName);
+        }
 
-            @Override
-            public ShapeBuilder<?, ?, ?> visit(Rectangle rectangle) {
-                return new EnvelopeBuilder(new Coordinate(rectangle.getMinLon(), rectangle.getMaxLat()),
-                    new Coordinate(rectangle.getMaxLon(), rectangle.getMinLat()));
-            }
-        });
-        return shapeBuilder;
+        builder.boost(pgsqp.boost);
+        builder.ignoreUnmapped(pgsqp.ignoreUnmapped);
+        return builder;
     }
 }