Skip to content

Commit

Permalink
Adds ignore_unmapped option to geo queries
Browse files Browse the repository at this point in the history
The change adds a new option to the geo_* queries: ignore_unmapped. If this option is set to false, the toQuery method on the QueryBuilder will throw an exception if the field specified in the query is unmapped. If the option is set to true, the toQuery method on the QueryBuilder will return a MatchNoDocsQuery. The default value is false so the queries work how they do today (throwing an exception on unmapped field)
  • Loading branch information
colings86 committed Apr 14, 2016
1 parent 18f8f3f commit c595322
Show file tree
Hide file tree
Showing 18 changed files with 455 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -58,6 +59,11 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
/** Default type for executing this query (memory as of this writing). */
public static final GeoExecType DEFAULT_TYPE = GeoExecType.MEMORY;

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

private static final ParseField IGNORE_MALFORMED_FIELD = new ParseField("ignore_malformed");
private static final ParseField TYPE_FIELD = new ParseField("type");
private static final ParseField VALIDATION_METHOD_FIELD = new ParseField("validation_method");
Expand All @@ -71,6 +77,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
private static final ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right");
private static final ParseField TOP_RIGHT_FIELD = new ParseField("top_right");
private static final ParseField BOTTOM_LEFT_FIELD = new ParseField("bottom_left");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");

/** Name of field holding geo coordinates to compute the bounding box on.*/
private final String fieldName;
Expand All @@ -83,6 +90,8 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
/** How the query should be run. */
private GeoExecType type = DEFAULT_TYPE;

private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

/**
* Create new bounding box query.
* @param fieldName name of index field containing geo coordinates to operate on.
Expand All @@ -104,6 +113,7 @@ public GeoBoundingBoxQueryBuilder(StreamInput in) throws IOException {
bottomRight = in.readGeoPoint();
type = GeoExecType.readFromStream(in);
validationMethod = GeoValidationMethod.readFromStream(in);
ignoreUnmapped = in.readBoolean();
}

@Override
Expand All @@ -113,6 +123,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
out.writeGeoPoint(bottomRight);
type.writeTo(out);
validationMethod.writeTo(out);
out.writeBoolean(ignoreUnmapped);
}

/**
Expand Down Expand Up @@ -245,6 +256,25 @@ public String fieldName() {
return this.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 GeoBoundingBoxQueryBuilder 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;
}

QueryValidationException checkLatLon(boolean indexCreatedBeforeV2_0) {
// validation was not available prior to 2.x, so to support bwc percolation queries we only ignore_malformed on 2.x created indexes
if (GeoValidationMethod.isIgnoreMalformed(validationMethod) == true || indexCreatedBeforeV2_0) {
Expand Down Expand Up @@ -276,7 +306,11 @@ QueryValidationException checkLatLon(boolean indexCreatedBeforeV2_0) {
public Query doToQuery(QueryShardContext context) {
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");
Expand Down Expand Up @@ -342,6 +376,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
builder.endObject();
builder.field(VALIDATION_METHOD_FIELD.getPreferredName(), validationMethod);
builder.field(TYPE_FIELD.getPreferredName(), type);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);

printBoostAndQueryName(builder);

Expand All @@ -365,6 +400,7 @@ public static GeoBoundingBoxQueryBuilder fromXContent(QueryParseContext parseCon
boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
GeoValidationMethod validationMethod = null;
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

GeoPoint sparse = new GeoPoint();

Expand Down Expand Up @@ -431,6 +467,8 @@ public static GeoBoundingBoxQueryBuilder fromXContent(QueryParseContext parseCon
}
} else if (parseContext.parseFieldMatcher().match(currentFieldName, VALIDATION_METHOD_FIELD)) {
validationMethod = GeoValidationMethod.fromString(parser.text());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
ignoreUnmapped = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
type = parser.text();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_MALFORMED_FIELD)) {
Expand All @@ -449,6 +487,7 @@ public static GeoBoundingBoxQueryBuilder fromXContent(QueryParseContext parseCon
builder.queryName(queryName);
builder.boost(boost);
builder.type(GeoExecType.fromString(type));
builder.ignoreUnmapped(ignoreUnmapped);
if (validationMethod != null) {
// ignore deprecated coerce/ignoreMalformed settings if validationMethod is set
builder.setValidationMethod(validationMethod);
Expand All @@ -464,12 +503,13 @@ protected boolean doEquals(GeoBoundingBoxQueryBuilder other) {
Objects.equals(bottomRight, other.bottomRight) &&
Objects.equals(type, other.type) &&
Objects.equals(validationMethod, other.validationMethod) &&
Objects.equals(fieldName, other.fieldName);
Objects.equals(fieldName, other.fieldName) &&
Objects.equals(ignoreUnmapped, other.ignoreUnmapped);
}

@Override
protected int doHashCode() {
return Objects.hash(topLeft, bottomRight, type, validationMethod, fieldName);
return Objects.hash(topLeft, bottomRight, type, validationMethod, fieldName, ignoreUnmapped);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.GeoPointDistanceQuery;
Expand Down Expand Up @@ -66,13 +67,19 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
/** Default for optimising query through pre computed bounding box query. */
public static final String DEFAULT_OPTIMIZE_BBOX = "memory";

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

private static final ParseField VALIDATION_METHOD_FIELD = new ParseField("validation_method");
private static final ParseField IGNORE_MALFORMED_FIELD = new ParseField("ignore_malformed");
private static final ParseField COERCE_FIELD = new ParseField("coerce", "normalize");
private static final ParseField OPTIMIZE_BBOX_FIELD = new ParseField("optimize_bbox");
private static final ParseField DISTANCE_TYPE_FIELD = new ParseField("distance_type");
private static final ParseField UNIT_FIELD = new ParseField("unit");
private static final ParseField DISTANCE_FIELD = new ParseField("distance");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");

private final String fieldName;
/** Distance from center to cover. */
Expand All @@ -86,6 +93,8 @@ public class GeoDistanceQueryBuilder extends AbstractQueryBuilder<GeoDistanceQue
/** How strict should geo coordinate validation be? */
private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;

private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

/**
* Construct new GeoDistanceQueryBuilder.
* @param fieldName name of indexed geo field to operate distance computation on.
Expand All @@ -108,6 +117,7 @@ public GeoDistanceQueryBuilder(StreamInput in) throws IOException {
center = in.readGeoPoint();
optimizeBbox = in.readString();
geoDistance = GeoDistance.readFromStream(in);
ignoreUnmapped = in.readBoolean();
}

@Override
Expand All @@ -118,6 +128,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
out.writeGeoPoint(center);
out.writeString(optimizeBbox);
geoDistance.writeTo(out);
out.writeBoolean(ignoreUnmapped);
}

/** Name of the field this query is operating on. */
Expand Down Expand Up @@ -243,11 +254,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 GeoDistanceQueryBuilder 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 shardContext) throws IOException {
MappedFieldType fieldType = shardContext.fieldMapper(fieldName);
if (fieldType == null) {
throw new QueryShardException(shardContext, "failed to find geo_point field [" + fieldName + "]");
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
throw new QueryShardException(shardContext, "failed to find geo_point field [" + fieldName + "]");
}
}

if (!(fieldType instanceof BaseGeoPointFieldMapper.GeoPointFieldType)) {
Expand Down Expand Up @@ -289,6 +323,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
builder.field(DISTANCE_TYPE_FIELD.getPreferredName(), geoDistance.name().toLowerCase(Locale.ROOT));
builder.field(OPTIMIZE_BBOX_FIELD.getPreferredName(), optimizeBbox);
builder.field(VALIDATION_METHOD_FIELD.getPreferredName(), validationMethod);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
printBoostAndQueryName(builder);
builder.endObject();
}
Expand All @@ -310,6 +345,7 @@ public static GeoDistanceQueryBuilder fromXContent(QueryParseContext parseContex
boolean coerce = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
boolean ignoreMalformed = GeoValidationMethod.DEFAULT_LENIENT_PARSING;
GeoValidationMethod validationMethod = null;
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
Expand Down Expand Up @@ -370,6 +406,8 @@ public static GeoDistanceQueryBuilder fromXContent(QueryParseContext parseContex
}
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_MALFORMED_FIELD)) {
ignoreMalformed = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
ignoreUnmapped = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, VALIDATION_METHOD_FIELD)) {
validationMethod = GeoValidationMethod.fromString(parser.text());
} else {
Expand Down Expand Up @@ -404,12 +442,13 @@ public static GeoDistanceQueryBuilder fromXContent(QueryParseContext parseContex
qb.geoDistance(geoDistance);
qb.boost(boost);
qb.queryName(queryName);
qb.ignoreUnmapped(ignoreUnmapped);
return qb;
}

@Override
protected int doHashCode() {
return Objects.hash(center, geoDistance, optimizeBbox, distance, validationMethod);
return Objects.hash(center, geoDistance, optimizeBbox, distance, validationMethod, ignoreUnmapped);
}

@Override
Expand All @@ -419,7 +458,8 @@ protected boolean doEquals(GeoDistanceQueryBuilder other) {
Objects.equals(validationMethod, other.validationMethod) &&
Objects.equals(center, other.center) &&
Objects.equals(optimizeBbox, other.optimizeBbox) &&
Objects.equals(geoDistance, other.geoDistance);
Objects.equals(geoDistance, other.geoDistance) &&
Objects.equals(ignoreUnmapped, other.ignoreUnmapped);
}

private QueryValidationException checkLatLon(boolean indexCreatedBeforeV2_0) {
Expand Down
Loading

0 comments on commit c595322

Please sign in to comment.