diff --git a/core/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java index e8533478198e2..a685145e08172 100644 --- a/core/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java @@ -19,6 +19,7 @@ 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.GeoPointInBBoxQuery; @@ -58,6 +59,11 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder points) { if (Strings.isEmpty(fieldName)) { throw new IllegalArgumentException("fieldName must not be null"); @@ -96,6 +105,7 @@ public GeoPolygonQueryBuilder(StreamInput in) throws IOException { shell.add(in.readGeoPoint()); } validationMethod = GeoValidationMethod.readFromStream(in); + ignoreUnmapped = in.readBoolean(); } @Override @@ -106,6 +116,7 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeGeoPoint(point); } validationMethod.writeTo(out); + out.writeBoolean(ignoreUnmapped); } public String fieldName() { @@ -127,11 +138,34 @@ 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 GeoPolygonQueryBuilder 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) { - throw new QueryShardException(context, "failed to find geo_point field [" + fieldName + "]"); + 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"); @@ -201,6 +235,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.field(COERCE_FIELD.getPreferredName(), GeoValidationMethod.isCoerce(validationMethod)); builder.field(IGNORE_MALFORMED_FIELD.getPreferredName(), GeoValidationMethod.isIgnoreMalformed(validationMethod)); + builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped); printBoostAndQueryName(builder); builder.endObject(); @@ -220,6 +255,7 @@ public static GeoPolygonQueryBuilder fromXContent(QueryParseContext parseContext String queryName = null; String currentFieldName = null; XContentParser.Token token; + boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { @@ -257,6 +293,8 @@ public static GeoPolygonQueryBuilder fromXContent(QueryParseContext parseContext if (coerce) { ignoreMalformed = true; } + } else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) { + ignoreUnmapped = parser.booleanValue(); } else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_MALFORMED_FIELD)) { ignoreMalformed = parser.booleanValue(); } else if (parseContext.parseFieldMatcher().match(currentFieldName, VALIDATION_METHOD)) { @@ -283,6 +321,7 @@ public static GeoPolygonQueryBuilder fromXContent(QueryParseContext parseContext if (boost != null) { builder.boost(boost); } + builder.ignoreUnmapped(ignoreUnmapped); return builder; } @@ -290,12 +329,13 @@ public static GeoPolygonQueryBuilder fromXContent(QueryParseContext parseContext protected boolean doEquals(GeoPolygonQueryBuilder other) { return Objects.equals(validationMethod, other.validationMethod) && Objects.equals(fieldName, other.fieldName) - && Objects.equals(shell, other.shell); + && Objects.equals(shell, other.shell) + && Objects.equals(ignoreUnmapped, other.ignoreUnmapped); } @Override protected int doHashCode() { - return Objects.hash(validationMethod, fieldName, shell); + return Objects.hash(validationMethod, fieldName, shell, ignoreUnmapped); } @Override diff --git a/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java index 9a0d4c366084b..f7ef58023c365 100644 --- a/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java @@ -22,6 +22,7 @@ import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.prefix.PrefixTreeStrategy; import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy; @@ -60,6 +61,11 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder { private Integer levels = null; private boolean neighbors = DEFAULT_NEIGHBORS; + private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; + public Builder(String field, GeoPoint point) { this(field, point == null ? null : point.geohash(), false); } @@ -136,6 +145,7 @@ public Builder(StreamInput in) throws IOException { geohash = in.readString(); levels = in.readOptionalVInt(); neighbors = in.readBoolean(); + ignoreUnmapped = in.readBoolean(); } @Override @@ -144,6 +154,7 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeString(geohash); out.writeOptionalVInt(levels); out.writeBoolean(neighbors); + out.writeBoolean(ignoreUnmapped); } public Builder point(GeoPoint point) { @@ -200,12 +211,35 @@ public String fieldName() { return fieldName; } + /** + * 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 GeohashCellQuery.Builder 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) { - throw new QueryShardException(context, "failed to parse [{}] query. missing [{}] field [{}]", NAME, - BaseGeoPointFieldMapper.CONTENT_TYPE, fieldName); + if (ignoreUnmapped) { + return new MatchNoDocsQuery(); + } else { + throw new QueryShardException(context, "failed to parse [{}] query. missing [{}] field [{}]", NAME, + BaseGeoPointFieldMapper.CONTENT_TYPE, fieldName); + } } if (!(fieldType instanceof BaseGeoPointFieldMapper.GeoPointFieldType)) { @@ -241,6 +275,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.field(PRECISION_FIELD.getPreferredName(), levels); } builder.field(fieldName, geohash); + builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped); printBoostAndQueryName(builder); builder.endObject(); } @@ -254,6 +289,7 @@ public static Builder fromXContent(QueryParseContext parseContext) throws IOExce Boolean neighbors = null; String queryName = null; Float boost = null; + boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED; XContentParser.Token token; if ((token = parser.currentToken()) != Token.START_OBJECT) { @@ -280,6 +316,9 @@ public static Builder fromXContent(QueryParseContext parseContext) throws IOExce } else if (parseContext.parseFieldMatcher().match(field, AbstractQueryBuilder.NAME_FIELD)) { parser.nextToken(); queryName = parser.text(); + } else if (parseContext.parseFieldMatcher().match(field, IGNORE_UNMAPPED_FIELD)) { + parser.nextToken(); + ignoreUnmapped = parser.booleanValue(); } else if (parseContext.parseFieldMatcher().match(field, AbstractQueryBuilder.BOOST_FIELD)) { parser.nextToken(); boost = parser.floatValue(); @@ -322,6 +361,7 @@ public static Builder fromXContent(QueryParseContext parseContext) throws IOExce if (boost != null) { builder.boost(boost); } + builder.ignoreUnmapped(ignoreUnmapped); return builder; } @@ -330,12 +370,13 @@ protected boolean doEquals(Builder other) { return Objects.equals(fieldName, other.fieldName) && Objects.equals(geohash, other.geohash) && Objects.equals(levels, other.levels) - && Objects.equals(neighbors, other.neighbors); + && Objects.equals(neighbors, other.neighbors) + && Objects.equals(ignoreUnmapped, other.ignoreUnmapped); } @Override protected int doHashCode() { - return Objects.hash(fieldName, geohash, levels, neighbors); + return Objects.hash(fieldName, geohash, levels, neighbors, ignoreUnmapped); } @Override diff --git a/core/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java index 9d531d2cf1fe2..675851049ffb9 100644 --- a/core/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilderTests.java @@ -23,6 +23,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.LegacyNumericRangeQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.geopoint.search.GeoPointInBBoxQuery; import org.elasticsearch.Version; @@ -36,8 +37,10 @@ import java.io.IOException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -84,6 +87,10 @@ protected GeoBoundingBoxQueryBuilder doCreateTestQueryBuilder() { builder.setValidationMethod(randomFrom(GeoValidationMethod.values())); } + if (randomBoolean()) { + builder.ignoreUnmapped(randomBoolean()); + } + builder.type(randomFrom(GeoExecType.values())); return builder; } @@ -454,6 +461,7 @@ public void testFromJson() throws IOException { " },\n" + " \"validation_method\" : \"STRICT\",\n" + " \"type\" : \"MEMORY\",\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 1.0\n" + " }\n" + "}"; @@ -475,6 +483,7 @@ public void testFromJson() throws IOException { " },\n" + " \"validation_method\" : \"STRICT\",\n" + " \"type\" : \"MEMORY\",\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 1.0\n" + " }\n" + "}"; @@ -494,4 +503,17 @@ public void testMustRewrite() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); super.testMustRewrite(); } + + public void testIgnoreUnmapped() throws IOException { + final GeoBoundingBoxQueryBuilder queryBuilder = new GeoBoundingBoxQueryBuilder("unmapped").setCorners(1.0, 0.0, 0.0, 1.0); + queryBuilder.ignoreUnmapped(true); + Query query = queryBuilder.toQuery(queryShardContext()); + assertThat(query, notNullValue()); + assertThat(query, instanceOf(MatchNoDocsQuery.class)); + + final GeoBoundingBoxQueryBuilder failingQueryBuilder = new GeoBoundingBoxQueryBuilder("unmapped").setCorners(1.0, 0.0, 0.0, 1.0); + failingQueryBuilder.ignoreUnmapped(false); + QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext())); + assertThat(e.getMessage(), containsString("failed to find geo_point field [unmapped]")); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java index 398f4ac449d88..dff2b667d258f 100644 --- a/core/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/GeoDistanceQueryBuilderTests.java @@ -21,6 +21,8 @@ import org.locationtech.spatial4j.shape.Point; import org.apache.lucene.spatial.geopoint.search.GeoPointDistanceQuery; + +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.util.GeoEncodingUtils; import org.elasticsearch.Version; @@ -32,9 +34,11 @@ import java.io.IOException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; public class GeoDistanceQueryBuilderTests extends AbstractQueryTestCase { @@ -73,6 +77,10 @@ protected GeoDistanceQueryBuilder doCreateTestQueryBuilder() { if (randomBoolean()) { qb.geoDistance(randomFrom(GeoDistance.values())); } + + if (randomBoolean()) { + qb.ignoreUnmapped(randomBoolean()); + } return qb; } @@ -402,6 +410,7 @@ public void testFromJson() throws IOException { " \"distance_type\" : \"sloppy_arc\",\n" + " \"optimize_bbox\" : \"memory\",\n" + " \"validation_method\" : \"STRICT\",\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 1.0\n" + " }\n" + "}"; @@ -417,4 +426,17 @@ public void testMustRewrite() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); super.testMustRewrite(); } + + public void testIgnoreUnmapped() throws IOException { + final GeoDistanceQueryBuilder queryBuilder = new GeoDistanceQueryBuilder("unmapped").point(0.0, 0.0).distance("20m"); + queryBuilder.ignoreUnmapped(true); + Query query = queryBuilder.toQuery(queryShardContext()); + assertThat(query, notNullValue()); + assertThat(query, instanceOf(MatchNoDocsQuery.class)); + + final GeoDistanceQueryBuilder failingQueryBuilder = new GeoDistanceQueryBuilder("unmapped").point(0.0, 0.0).distance("20m"); + failingQueryBuilder.ignoreUnmapped(false); + QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext())); + assertThat(e.getMessage(), containsString("failed to find geo_point field [unmapped]")); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/GeoDistanceRangeQueryTests.java b/core/src/test/java/org/elasticsearch/index/query/GeoDistanceRangeQueryTests.java index 20e09afaac801..ab50922e20106 100644 --- a/core/src/test/java/org/elasticsearch/index/query/GeoDistanceRangeQueryTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/GeoDistanceRangeQueryTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.query; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.geopoint.search.XGeoPointDistanceRangeQuery; import org.apache.lucene.spatial.util.GeoDistanceUtils; @@ -35,9 +36,11 @@ import java.io.IOException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase { @@ -111,6 +114,10 @@ protected GeoDistanceRangeQueryBuilder doCreateTestQueryBuilder() { if (randomBoolean()) { builder.setValidationMethod(randomFrom(GeoValidationMethod.values())); } + + if (randomBoolean()) { + builder.ignoreUnmapped(randomBoolean()); + } return builder; } @@ -341,6 +348,7 @@ public void testFromJson() throws IOException { " \"distance_type\" : \"sloppy_arc\",\n" + " \"optimize_bbox\" : \"memory\",\n" + " \"validation_method\" : \"STRICT\",\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 1.0\n" + " }\n" + "}"; @@ -354,4 +362,18 @@ public void testMustRewrite() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); super.testMustRewrite(); } + + public void testIgnoreUnmapped() throws IOException { + final GeoDistanceRangeQueryBuilder queryBuilder = new GeoDistanceRangeQueryBuilder("unmapped", new GeoPoint(0.0, 0.0)).from("20m"); + queryBuilder.ignoreUnmapped(true); + Query query = queryBuilder.toQuery(queryShardContext()); + assertThat(query, notNullValue()); + assertThat(query, instanceOf(MatchNoDocsQuery.class)); + + final GeoDistanceRangeQueryBuilder failingQueryBuilder = new GeoDistanceRangeQueryBuilder("unmapped", new GeoPoint(0.0, 0.0)) + .from("20m"); + failingQueryBuilder.ignoreUnmapped(false); + QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext())); + assertThat(e.getMessage(), containsString("failed to find geo_point field [unmapped]")); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java index f64736e006267..dd91a0e5ead09 100644 --- a/core/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java @@ -20,7 +20,10 @@ package org.elasticsearch.index.query; import org.locationtech.spatial4j.shape.jts.JtsGeometry; + import com.vividsolutions.jts.geom.Coordinate; + +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.spatial.geopoint.search.GeoPointInPolygonQuery; import org.elasticsearch.Version; @@ -39,9 +42,11 @@ import java.util.List; import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase { @@ -52,6 +57,10 @@ protected GeoPolygonQueryBuilder doCreateTestQueryBuilder() { if (randomBoolean()) { builder.setValidationMethod(randomFrom(GeoValidationMethod.values())); } + + if (randomBoolean()) { + builder.ignoreUnmapped(randomBoolean()); + } return builder; } @@ -336,6 +345,7 @@ public void testFromJson() throws IOException { " },\n" + " \"coerce\" : false,\n" + " \"ignore_malformed\" : false,\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 1.0\n" + " }\n" + "}"; @@ -349,4 +359,18 @@ public void testMustRewrite() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); super.testMustRewrite(); } + + public void testIgnoreUnmapped() throws IOException { + List polygon = randomPolygon(randomIntBetween(4, 50)); + final GeoPolygonQueryBuilder queryBuilder = new GeoPolygonQueryBuilder("unmapped", polygon); + queryBuilder.ignoreUnmapped(true); + Query query = queryBuilder.toQuery(queryShardContext()); + assertThat(query, notNullValue()); + assertThat(query, instanceOf(MatchNoDocsQuery.class)); + + final GeoPolygonQueryBuilder failingQueryBuilder = new GeoPolygonQueryBuilder("unmapped", polygon); + failingQueryBuilder.ignoreUnmapped(false); + QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext())); + assertThat(e.getMessage(), containsString("failed to find geo_point field [unmapped]")); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java index 5fc3dbd0dc11c..b4e0e0f18c27b 100644 --- a/core/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java @@ -23,6 +23,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.get.GetRequest; @@ -43,11 +44,12 @@ import java.io.IOException; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase { @@ -92,6 +94,10 @@ protected GeoShapeQueryBuilder doCreateTestQueryBuilder() { builder.relation(randomFrom(ShapeRelation.values())); } } + + if (randomBoolean()) { + builder.ignoreUnmapped(randomBoolean()); + } return builder; } @@ -233,6 +239,7 @@ public void testFromJson() throws IOException { " },\n" + " \"relation\" : \"intersects\"\n" + " },\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 42.0\n" + " }\n" + "}"; @@ -260,4 +267,19 @@ public void testMustRewrite() throws IOException { geoShapeQueryBuilder.relation(sqb.relation()); assertEquals(geoShapeQueryBuilder, rewrite); } + + public void testIgnoreUnmapped() throws IOException { + ShapeType shapeType = ShapeType.randomType(random()); + ShapeBuilder shape = RandomShapeGenerator.createShapeWithin(random(), null, shapeType); + final GeoShapeQueryBuilder queryBuilder = new GeoShapeQueryBuilder("unmapped", shape); + queryBuilder.ignoreUnmapped(true); + Query query = queryBuilder.toQuery(queryShardContext()); + assertThat(query, notNullValue()); + assertThat(query, instanceOf(MatchNoDocsQuery.class)); + + final GeoShapeQueryBuilder failingQueryBuilder = new GeoShapeQueryBuilder("unmapped", shape); + failingQueryBuilder.ignoreUnmapped(false); + QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext())); + assertThat(e.getMessage(), containsString("failed to find geo_shape field [unmapped]")); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/GeohashCellQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/GeohashCellQueryBuilderTests.java index 8ea4534ceeffc..ff61a9b7cf155 100644 --- a/core/src/test/java/org/elasticsearch/index/query/GeohashCellQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/GeohashCellQueryBuilderTests.java @@ -20,21 +20,25 @@ package org.elasticsearch.index.query; import org.locationtech.spatial4j.shape.Point; + import org.apache.lucene.index.Term; import org.apache.lucene.queries.TermsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.unit.DistanceUnit; +import org.elasticsearch.index.mapper.geo.BaseGeoPointFieldMapper; import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; import org.elasticsearch.index.query.GeohashCellQuery.Builder; import org.elasticsearch.test.geo.RandomShapeGenerator; import java.io.IOException; -import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; public class GeohashCellQueryBuilderTests extends AbstractQueryTestCase { @@ -52,6 +56,9 @@ protected Builder doCreateTestQueryBuilder() { builder.precision(randomIntBetween(1, 1000000) + randomFrom(DistanceUnit.values()).toString()); } } + if (randomBoolean()) { + builder.ignoreUnmapped(randomBoolean()); + } return builder; } @@ -138,6 +145,7 @@ public void testFromJson() throws IOException { " \"neighbors\" : true,\n" + " \"precision\" : 3,\n" + " \"pin\" : \"t4mk70fgk067\",\n" + + " \"ignore_unmapped\" : false,\n" + " \"boost\" : 1.0\n" + " }\n" + "}"; @@ -151,4 +159,18 @@ public void testMustRewrite() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); super.testMustRewrite(); } + + public void testIgnoreUnmapped() throws IOException { + final GeohashCellQuery.Builder queryBuilder = new GeohashCellQuery.Builder("unmapped", "c"); + queryBuilder.ignoreUnmapped(true); + Query query = queryBuilder.toQuery(queryShardContext()); + assertThat(query, notNullValue()); + assertThat(query, instanceOf(MatchNoDocsQuery.class)); + + final GeohashCellQuery.Builder failingQueryBuilder = new GeohashCellQuery.Builder("unmapped", "c"); + failingQueryBuilder.ignoreUnmapped(false); + QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext())); + assertThat(e.getMessage(), containsString("failed to parse [" + GeohashCellQuery.NAME + "] query. missing [" + + BaseGeoPointFieldMapper.CONTENT_TYPE + "] field [unmapped]")); + } } diff --git a/docs/reference/query-dsl/geo-bounding-box-query.asciidoc b/docs/reference/query-dsl/geo-bounding-box-query.asciidoc index b2103e5772f6b..593086f2a1a2a 100644 --- a/docs/reference/query-dsl/geo-bounding-box-query.asciidoc +++ b/docs/reference/query-dsl/geo-bounding-box-query.asciidoc @@ -247,3 +247,11 @@ are not supported. Here is an example: } -------------------------------------------------- +[float] +==== Ignore Unmapped + +When set to `true` the `ignore_unmapped` option will ignore an unmapped field +and will not match any documents for this query. This can be useful when +querying multiple indexes which might have different mappings. When set to +`false` (the default value) the query will throw an exception if the field +is not mapped. diff --git a/docs/reference/query-dsl/geo-distance-query.asciidoc b/docs/reference/query-dsl/geo-distance-query.asciidoc index 7ea380bdad293..f053fd3a2dd9b 100644 --- a/docs/reference/query-dsl/geo-distance-query.asciidoc +++ b/docs/reference/query-dsl/geo-distance-query.asciidoc @@ -180,3 +180,11 @@ The `geo_distance` filter can work with multiple locations / points per document. Once a single location / point matches the filter, the document will be included in the filter. +[float] +==== Ignore Unmapped + +When set to `true` the `ignore_unmapped` option will ignore an unmapped field +and will not match any documents for this query. This can be useful when +querying multiple indexes which might have different mappings. When set to +`false` (the default value) the query will throw an exception if the field +is not mapped. diff --git a/docs/reference/query-dsl/geo-distance-range-query.asciidoc b/docs/reference/query-dsl/geo-distance-range-query.asciidoc index 48e9e10d5cad9..abf557d68c76e 100644 --- a/docs/reference/query-dsl/geo-distance-range-query.asciidoc +++ b/docs/reference/query-dsl/geo-distance-range-query.asciidoc @@ -28,3 +28,12 @@ Supports the same point location parameter and query options as the <> filter. And also support the common parameters for range (lt, lte, gt, gte, from, to, include_upper and include_lower). + +[float] +==== Ignore Unmapped + +When set to `true` the `ignore_unmapped` option will ignore an unmapped field +and will not match any documents for this query. This can be useful when +querying multiple indexes which might have different mappings. When set to +`false` (the default value) the query will throw an exception if the field +is not mapped. diff --git a/docs/reference/query-dsl/geo-polygon-query.asciidoc b/docs/reference/query-dsl/geo-polygon-query.asciidoc index 269aeed09cae6..938a0b3dc8762 100644 --- a/docs/reference/query-dsl/geo-polygon-query.asciidoc +++ b/docs/reference/query-dsl/geo-polygon-query.asciidoc @@ -127,3 +127,11 @@ Format in `lat,lon`. The query *requires* the <> type to be set on the relevant field. +[float] +==== Ignore Unmapped + +When set to `true` the `ignore_unmapped` option will ignore an unmapped field +and will not match any documents for this query. This can be useful when +querying multiple indexes which might have different mappings. When set to +`false` (the default value) the query will throw an exception if the field +is not mapped. diff --git a/docs/reference/query-dsl/geo-shape-query.asciidoc b/docs/reference/query-dsl/geo-shape-query.asciidoc index d389380b781ac..a892080dda400 100644 --- a/docs/reference/query-dsl/geo-shape-query.asciidoc +++ b/docs/reference/query-dsl/geo-shape-query.asciidoc @@ -116,4 +116,13 @@ has nothing in common with the query geometry. * `WITHIN` - Return all documents whose `geo_shape` field is within the query geometry. * `CONTAINS` - Return all documents whose `geo_shape` field -contains the query geometry. \ No newline at end of file +contains the query geometry. + +[float] +==== Ignore Unmapped + +When set to `true` the `ignore_unmapped` option will ignore an unmapped field +and will not match any documents for this query. This can be useful when +querying multiple indexes which might have different mappings. When set to +`false` (the default value) the query will throw an exception if the field +is not mapped. diff --git a/docs/reference/query-dsl/geohash-cell-query.asciidoc b/docs/reference/query-dsl/geohash-cell-query.asciidoc index 807e86715729f..2dd701fd95892 100644 --- a/docs/reference/query-dsl/geohash-cell-query.asciidoc +++ b/docs/reference/query-dsl/geohash-cell-query.asciidoc @@ -61,3 +61,11 @@ next to the given cell. } -------------------------------------------------- +[float] +==== Ignore Unmapped + +When set to `true` the `ignore_unmapped` option will ignore an unmapped field +and will not match any documents for this query. This can be useful when +querying multiple indexes which might have different mappings. When set to +`false` (the default value) the query will throw an exception if the field +is not mapped.