Query Refactoring: Refactor of GeoShapeQuery

Moving the query building functionality from the parser to the builders
new toQuery() method analogous to other recent query refactorings.

Relates to #10217

PR goes against the query-refactoring branch
This commit is contained in:
Colin Goodheart-Smithe 2015-09-09 16:45:10 +01:00
parent 853b7fdb7c
commit 2118936deb
16 changed files with 902 additions and 258 deletions

View File

@ -19,13 +19,18 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import java.io.IOException;
import java.util.Locale; import java.util.Locale;
/** /**
* Enum representing the relationship between a Query / Filter Shape and indexed Shapes * Enum representing the relationship between a Query / Filter Shape and indexed Shapes
* that will be used to determine if a Document should be matched or not * that will be used to determine if a Document should be matched or not
*/ */
public enum ShapeRelation { public enum ShapeRelation implements Writeable<ShapeRelation>{
INTERSECTS("intersects"), INTERSECTS("intersects"),
DISJOINT("disjoint"), DISJOINT("disjoint"),
@ -37,6 +42,20 @@ public enum ShapeRelation {
this.relationName = relationName; this.relationName = relationName;
} }
@Override
public ShapeRelation readFrom(StreamInput in) throws IOException {
int ordinal = in.readVInt();
if (ordinal < 0 || ordinal >= values().length) {
throw new IOException("Unknown ShapeRelation ordinal [" + ordinal + "]");
}
return values()[ordinal];
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(ordinal());
}
public static ShapeRelation getRelationByName(String name) { public static ShapeRelation getRelationByName(String name) {
name = name.toLowerCase(Locale.ENGLISH); name = name.toLowerCase(Locale.ENGLISH);
for (ShapeRelation relation : ShapeRelation.values()) { for (ShapeRelation relation : ShapeRelation.values()) {

View File

@ -18,11 +18,16 @@
*/ */
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import java.io.IOException;
/** /**
* *
*/ */
public enum SpatialStrategy { public enum SpatialStrategy implements Writeable<SpatialStrategy> {
TERM("term"), TERM("term"),
RECURSIVE("recursive"); RECURSIVE("recursive");
@ -36,4 +41,27 @@ public enum SpatialStrategy {
public String getStrategyName() { public String getStrategyName() {
return strategyName; return strategyName;
} }
@Override
public SpatialStrategy readFrom(StreamInput in) throws IOException {
int ordinal = in.readVInt();
if (ordinal < 0 || ordinal >= values().length) {
throw new IOException("Unknown SpatialStrategy ordinal [" + ordinal + "]");
}
return values()[ordinal];
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(ordinal());
}
public static SpatialStrategy fromString(String strategyName) {
for (SpatialStrategy strategy : values()) {
if (strategy.strategyName.equals(strategyName)) {
return strategy;
}
}
return null;
}
} }

View File

@ -125,6 +125,7 @@ public class GeoShapeFieldMapper extends FieldMapper {
super(name, Defaults.FIELD_TYPE); super(name, Defaults.FIELD_TYPE);
} }
@Override
public GeoShapeFieldType fieldType() { public GeoShapeFieldType fieldType() {
return (GeoShapeFieldType)fieldType; return (GeoShapeFieldType)fieldType;
} }
@ -400,6 +401,10 @@ public class GeoShapeFieldMapper extends FieldMapper {
return this.defaultStrategy; return this.defaultStrategy;
} }
public PrefixTreeStrategy resolveStrategy(SpatialStrategy strategy) {
return resolveStrategy(strategy.getStrategyName());
}
public PrefixTreeStrategy resolveStrategy(String strategyName) { public PrefixTreeStrategy resolveStrategy(String strategyName) {
if (SpatialStrategy.RECURSIVE.getStrategyName().equals(strategyName)) { if (SpatialStrategy.RECURSIVE.getStrategyName().equals(strategyName)) {
recursiveStrategy.setPointsOnly(pointsOnly()); recursiveStrategy.setPointsOnly(pointsOnly());

View File

@ -19,84 +19,161 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
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.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.ShapesAvailability;
import org.elasticsearch.common.geo.SpatialStrategy; import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder; 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.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
/** /**
* {@link QueryBuilder} that builds a GeoShape Filter * {@link QueryBuilder} that builds a GeoShape Query
*/ */
public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuilder> { public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuilder> {
public static final String NAME = "geo_shape"; public static final String NAME = "geo_shape";
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;
static final GeoShapeQueryBuilder PROTOTYPE = new GeoShapeQueryBuilder(null, null); static final GeoShapeQueryBuilder PROTOTYPE = new GeoShapeQueryBuilder("", new BytesArray(new byte[0]));
private final String name; private final String fieldName;
private final ShapeBuilder shape; // TODO make the ShapeBuilder and subclasses Writable and implement hashCode
// and Equals so ShapeBuilder can be used here
private BytesReference shapeBytes;
private SpatialStrategy strategy = null; private SpatialStrategy strategy = null;
private final String indexedShapeId; private final String indexedShapeId;
private final String indexedShapeType; private final String indexedShapeType;
private String indexedShapeIndex; private String indexedShapeIndex = DEFAULT_SHAPE_INDEX_NAME;
private String indexedShapePath; private String indexedShapePath = DEFAULT_SHAPE_FIELD_NAME;
private ShapeRelation relation = null; private ShapeRelation relation = DEFAULT_SHAPE_RELATION;
/** /**
* Creates a new GeoShapeQueryBuilder whose Filter will be against the * Creates a new GeoShapeQueryBuilder whose Query will be against the given
* given field name using the given Shape * field name using the given Shape
* *
* @param name Name of the field that will be filtered * @param name
* @param shape Shape used in the filter * Name of the field that will be queried
* @param shape
* Shape used in the Query
*/ */
public GeoShapeQueryBuilder(String name, ShapeBuilder shape) { public GeoShapeQueryBuilder(String fieldName, ShapeBuilder shape) throws IOException {
this(name, shape, null, null, null); this(fieldName, shape, null, null);
} }
/** /**
* Creates a new GeoShapeQueryBuilder whose Filter will be against the * Creates a new GeoShapeQueryBuilder whose Query will be against the given
* given field name using the given Shape * field name and will use the Shape found with the given ID in the given
* type
* *
* @param name Name of the field that will be filtered * @param fieldName
* @param relation {@link ShapeRelation} of query and indexed shape * Name of the field that will be filtered
* @param shape Shape used in the filter * @param indexedShapeId
* ID of the indexed Shape that will be used in the Query
* @param indexedShapeType
* Index type of the indexed Shapes
*/ */
public GeoShapeQueryBuilder(String name, ShapeBuilder shape, ShapeRelation relation) { public GeoShapeQueryBuilder(String fieldName, String indexedShapeId, String indexedShapeType) {
this(name, shape, null, null, relation); this(fieldName, (BytesReference) null, indexedShapeId, indexedShapeType);
} }
/** GeoShapeQueryBuilder(String fieldName, BytesReference shapeBytes) {
* Creates a new GeoShapeQueryBuilder whose Filter will be against the given field name this(fieldName, shapeBytes, null, null);
* and will use the Shape found with the given ID in the given type
*
* @param name Name of the field that will be filtered
* @param indexedShapeId ID of the indexed Shape that will be used in the Filter
* @param indexedShapeType Index type of the indexed Shapes
*/
public GeoShapeQueryBuilder(String name, String indexedShapeId, String indexedShapeType, ShapeRelation relation) {
this(name, null, indexedShapeId, indexedShapeType, relation);
} }
private GeoShapeQueryBuilder(String name, ShapeBuilder shape, String indexedShapeId, String indexedShapeType, ShapeRelation relation) { private GeoShapeQueryBuilder(String fieldName, ShapeBuilder shape, String indexedShapeId, String indexedShapeType) throws IOException {
this.name = name; this(fieldName, new BytesArray(new byte[0]), indexedShapeId, indexedShapeType);
this.shape = shape; if (shape != null) {
XContentBuilder builder = XContentFactory.jsonBuilder();
shape.toXContent(builder, EMPTY_PARAMS);
this.shapeBytes = builder.bytes();
}
}
private GeoShapeQueryBuilder(String fieldName, BytesReference shapeBytes, String indexedShapeId, String indexedShapeType) {
if (fieldName == null) {
throw new IllegalArgumentException("fieldName is required");
}
if (shapeBytes == null && indexedShapeId == null) {
throw new IllegalArgumentException("either shapeBytes or indexedShapeId and indexedShapeType are required");
}
if (indexedShapeId != null && indexedShapeType == null) {
throw new IllegalArgumentException("indexedShapeType is required if indexedShapeId is specified");
}
this.fieldName = fieldName;
this.shapeBytes = shapeBytes;
this.indexedShapeId = indexedShapeId; this.indexedShapeId = indexedShapeId;
this.relation = relation;
this.indexedShapeType = indexedShapeType; this.indexedShapeType = indexedShapeType;
} }
/** /**
* Defines which spatial strategy will be used for building the geo shape filter. When not set, the strategy that * @return the name of the field that will be queried
* will be used will be the one that is associated with the geo shape field in the mappings. */
public String fieldName() {
return fieldName;
}
/**
* @return the JSON bytes for the shape used in the Query
*/
public BytesReference shapeBytes() {
return shapeBytes;
}
/**
* @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
*/
public String indexedShapeType() {
return indexedShapeType;
}
/**
* Defines which spatial strategy will be used for building the geo shape
* Query. When not set, the strategy that will be used will be the one that
* is associated with the geo shape field in the mappings.
* *
* @param strategy The spatial strategy to use for building the geo shape filter * @param strategy
* The spatial strategy to use for building the geo shape Query
* @return this * @return this
*/ */
public GeoShapeQueryBuilder strategy(SpatialStrategy strategy) { public GeoShapeQueryBuilder strategy(SpatialStrategy strategy) {
@ -104,6 +181,13 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
return this; return this;
} }
/**
* @return The spatial strategy to use for building the geo shape Query
*/
public SpatialStrategy strategy() {
return strategy;
}
/** /**
* Sets the name of the index where the indexed Shape can be found * Sets the name of the index where the indexed Shape can be found
* *
@ -115,6 +199,14 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
return this; 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 * Sets the path of the field in the indexed Shape document that has the Shape itself
* *
@ -126,6 +218,13 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
return this; return this;
} }
/**
* @return the path of the indexed Shape that will be used in the Query
*/
public String indexedShapePath() {
return indexedShapePath;
}
/** /**
* Sets the relation of query shape and indexed shape. * Sets the relation of query shape and indexed shape.
* *
@ -137,33 +236,176 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
return this; return this;
} }
/**
* @return the relation of query shape and indexed shape to use in the Query
*/
public ShapeRelation relation() {
return relation;
}
@Override
public QueryValidationException validate() {
QueryValidationException errors = null;
if (fieldName == null) {
errors = QueryValidationException.addValidationError(NAME, "No field defined", errors);
}
if ((shapeBytes == null || shapeBytes.length() == 0) && indexedShapeId == null) {
errors = QueryValidationException.addValidationError(NAME, "No Shape defined", errors);
}
if (relation == null) {
errors = QueryValidationException.addValidationError(NAME, "No Shape Relation defined", errors);
}
if (indexedShapeId != null && indexedShapeType == null) {
errors = QueryValidationException.addValidationError(NAME, "Type for indexed shape not provided", errors);
}
if (strategy != null && strategy == SpatialStrategy.TERM && relation != ShapeRelation.INTERSECTS) {
errors = QueryValidationException.addValidationError(NAME,
"strategy [" + strategy.getStrategyName() + "] only supports relation ["
+ ShapeRelation.INTERSECTS.getRelationName() + "] found relation [" + relation.getRelationName() + "]", errors);
}
return errors;
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
ShapeBuilder shape;
if (shapeBytes == null) {
GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
getRequest.copyContextAndHeadersFrom(SearchContext.current());
shape = fetch(context.getClient(), getRequest, indexedShapePath);
} else {
XContentParser shapeParser = XContentHelper.createParser(shapeBytes);
shapeParser.nextToken();
shape = ShapeBuilder.parse(shapeParser);
}
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType == null) {
throw new QueryShardException(context, "Failed to find geo_shape field [" + fieldName + "]");
}
// TODO: This isn't the nicest way to check this
if (!(fieldType instanceof GeoShapeFieldMapper.GeoShapeFieldType)) {
throw new QueryShardException(context, "Field [" + fieldName + "] is not a geo_shape");
}
GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType;
PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy();
if (this.strategy != null) {
strategy = shapeFieldType.resolveStrategy(this.strategy);
}
Query query;
if (strategy instanceof RecursivePrefixTreeStrategy && relation == ShapeRelation.DISJOINT) {
// this strategy doesn't support disjoint anymore: but it did
// before, including creating lucene fieldcache (!)
// in this case, execute disjoint as exists && !intersects
BooleanQuery.Builder bool = new BooleanQuery.Builder();
Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
Filter intersects = strategy.makeFilter(getArgs(shape, ShapeRelation.INTERSECTS));
bool.add(exists, BooleanClause.Occur.MUST);
bool.add(intersects, BooleanClause.Occur.MUST_NOT);
query = new ConstantScoreQuery(bool.build());
} else {
query = strategy.makeQuery(getArgs(shape, relation));
}
return query;
}
/**
* 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
* @return Shape with the given ID
* @throws IOException
* Can be thrown while parsing the Shape Document and extracting
* the Shape
*/
private ShapeBuilder fetch(Client client, GetRequest getRequest, String path) throws IOException {
if (ShapesAvailability.JTS_AVAILABLE == false) {
throw new IllegalStateException("JTS not available");
}
getRequest.preference("_local");
getRequest.operationThreaded(false);
GetResponse response = client.get(getRequest).actionGet();
if (!response.isExists()) {
throw new IllegalArgumentException("Shape with ID [" + getRequest.id() + "] in type [" + getRequest.type() + "] not found");
}
String[] pathElements = Strings.splitStringToArray(path, '.');
int currentPathSlot = 0;
XContentParser parser = null;
try {
parser = XContentHelper.createParser(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) {
return ShapeBuilder.parse(parser);
}
} else {
parser.nextToken();
parser.skipChildren();
}
}
}
throw new IllegalStateException("Shape with name [" + getRequest.id() + "] found but missing " + path + " field");
} finally {
if (parser != null) {
parser.close();
}
}
}
public static SpatialArgs getArgs(ShapeBuilder shape, ShapeRelation relation) {
switch (relation) {
case DISJOINT:
return new SpatialArgs(SpatialOperation.IsDisjointTo, shape.build());
case INTERSECTS:
return new SpatialArgs(SpatialOperation.Intersects, shape.build());
case WITHIN:
return new SpatialArgs(SpatialOperation.IsWithin, shape.build());
default:
throw new IllegalArgumentException("invalid relation [" + relation + "]");
}
}
@Override @Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException { protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME); builder.startObject(NAME);
builder.startObject(name); builder.startObject(fieldName);
if (strategy != null) { if (strategy != null) {
builder.field("strategy", strategy.getStrategyName()); builder.field(GeoShapeQueryParser.STRATEGY_FIELD.getPreferredName(), strategy.getStrategyName());
} }
if (shape != null) { if (shapeBytes != null) {
builder.field("shape", shape); builder.field(GeoShapeQueryParser.SHAPE_FIELD.getPreferredName());
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(shapeBytes);
parser.nextToken();
builder.copyCurrentStructure(parser);
} else { } else {
builder.startObject("indexed_shape") builder.startObject(GeoShapeQueryParser.INDEXED_SHAPE_FIELD.getPreferredName())
.field("id", indexedShapeId) .field(GeoShapeQueryParser.SHAPE_ID_FIELD.getPreferredName(), indexedShapeId)
.field("type", indexedShapeType); .field(GeoShapeQueryParser.SHAPE_TYPE_FIELD.getPreferredName(), indexedShapeType);
if (indexedShapeIndex != null) { if (indexedShapeIndex != null) {
builder.field("index", indexedShapeIndex); builder.field(GeoShapeQueryParser.SHAPE_INDEX_FIELD.getPreferredName(), indexedShapeIndex);
} }
if (indexedShapePath != null) { if (indexedShapePath != null) {
builder.field("path", indexedShapePath); builder.field(GeoShapeQueryParser.SHAPE_PATH_FIELD.getPreferredName(), indexedShapePath);
} }
builder.endObject(); builder.endObject();
} }
if(relation != null) { if(relation != null) {
builder.field("relation", relation.getRelationName()); builder.field(GeoShapeQueryParser.RELATION_FIELD.getPreferredName(), relation.getRelationName());
} }
builder.endObject(); builder.endObject();
@ -173,6 +415,66 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
builder.endObject(); builder.endObject();
} }
@Override
protected GeoShapeQueryBuilder doReadFrom(StreamInput in) throws IOException {
String fieldName = in.readString();
GeoShapeQueryBuilder builder;
if (in.readBoolean()) {
BytesReference shapeBytes = in.readBytesReference();
builder = new GeoShapeQueryBuilder(fieldName, shapeBytes);
} else {
String indexedShapeId = in.readOptionalString();
String indexedShapeType = in.readOptionalString();
String indexedShapeIndex = in.readOptionalString();
String indexedShapePath = in.readOptionalString();
builder = new GeoShapeQueryBuilder(fieldName, indexedShapeId, indexedShapeType);
if (indexedShapeIndex != null) {
builder.indexedShapeIndex = indexedShapeIndex;
}
if (indexedShapePath != null) {
builder.indexedShapePath = indexedShapePath;
}
}
builder.relation = ShapeRelation.DISJOINT.readFrom(in);
builder.strategy = SpatialStrategy.RECURSIVE.readFrom(in);
return builder;
}
@Override
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeString(fieldName);
boolean hasShapeBytes = shapeBytes != null;
out.writeBoolean(hasShapeBytes);
if (hasShapeBytes) {
out.writeBytesReference(shapeBytes);
} else {
out.writeOptionalString(indexedShapeId);
out.writeOptionalString(indexedShapeType);
out.writeOptionalString(indexedShapeIndex);
out.writeOptionalString(indexedShapePath);
}
relation.writeTo(out);
strategy.writeTo(out);
}
@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(relation, other.relation)
&& Objects.equals(shapeBytes, other.shapeBytes)
&& Objects.equals(strategy, other.strategy);
}
@Override
protected int doHashCode() {
return Objects.hash(fieldName, indexedShapeId, indexedShapeIndex,
indexedShapePath, indexedShapeType, relation, shapeBytes, strategy);
}
@Override @Override
public String getWriteableName() { public String getWriteableName() {
return NAME; return NAME;

View File

@ -19,32 +19,27 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.search.*; import org.elasticsearch.common.ParseField;
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.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.ShapesAvailability; import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
public class GeoShapeQueryParser extends BaseQueryParserTemp { public class GeoShapeQueryParser extends BaseQueryParser<GeoShapeQueryBuilder> {
public static class DEFAULTS { public static final ParseField SHAPE_FIELD = new ParseField("shape");
public static final String INDEX_NAME = "shapes"; public static final ParseField STRATEGY_FIELD = new ParseField("strategy");
public static final String SHAPE_FIELD_NAME = "shape"; public static final ParseField RELATION_FIELD = new ParseField("relation");
} public static final ParseField INDEXED_SHAPE_FIELD = new ParseField("indexed_shape");
public static final ParseField SHAPE_ID_FIELD = new ParseField("id");
public static final ParseField SHAPE_TYPE_FIELD = new ParseField("type");
public static final ParseField SHAPE_INDEX_FIELD = new ParseField("index");
public static final ParseField SHAPE_PATH_FIELD = new ParseField("path");
@Override @Override
public String[] names() { public String[] names() {
@ -52,23 +47,22 @@ public class GeoShapeQueryParser extends BaseQueryParserTemp {
} }
@Override @Override
public Query parse(QueryShardContext context) throws IOException, QueryParsingException { public GeoShapeQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
QueryParseContext parseContext = context.parseContext();
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
String fieldName = null; String fieldName = null;
ShapeRelation shapeRelation = ShapeRelation.INTERSECTS; ShapeRelation shapeRelation = null;
String strategyName = null; SpatialStrategy strategy = null;
ShapeBuilder shape = null; BytesReference shape = null;
String id = null; String id = null;
String type = null; String type = null;
String index = DEFAULTS.INDEX_NAME; String index = null;
String shapePath = DEFAULTS.SHAPE_FIELD_NAME; String shapePath = null;
XContentParser.Token token; XContentParser.Token token;
String currentFieldName = null; String currentFieldName = null;
float boost = 1f; float boost = AbstractQueryBuilder.DEFAULT_BOOST;
String queryName = null; String queryName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@ -81,159 +75,78 @@ public class GeoShapeQueryParser extends BaseQueryParserTemp {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
token = parser.nextToken(); token = parser.nextToken();
if ("shape".equals(currentFieldName)) { if (parseContext.parseFieldMatcher().match(currentFieldName, SHAPE_FIELD)) {
shape = ShapeBuilder.parse(parser); XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser);
} else if ("strategy".equals(currentFieldName)) { shape = builder.bytes();
strategyName = parser.text(); } else if (parseContext.parseFieldMatcher().match(currentFieldName, STRATEGY_FIELD)) {
} else if ("relation".equals(currentFieldName)) { String strategyName = parser.text();
strategy = SpatialStrategy.fromString(strategyName);
if (strategy == null) {
throw new QueryParsingException(parseContext, "Unknown strategy [" + strategyName + " ]");
}
} else if (parseContext.parseFieldMatcher().match(currentFieldName, RELATION_FIELD)) {
shapeRelation = ShapeRelation.getRelationByName(parser.text()); shapeRelation = ShapeRelation.getRelationByName(parser.text());
if (shapeRelation == null) { if (shapeRelation == null) {
throw new QueryParsingException(parseContext, "Unknown shape operation [" + parser.text() + " ]"); throw new QueryParsingException(parseContext, "Unknown shape operation [" + parser.text() + " ]");
} }
} else if ("indexed_shape".equals(currentFieldName) || "indexedShape".equals(currentFieldName)) { } else if (parseContext.parseFieldMatcher().match(currentFieldName, INDEXED_SHAPE_FIELD)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
} else if (token.isValue()) { } else if (token.isValue()) {
if ("id".equals(currentFieldName)) { if (parseContext.parseFieldMatcher().match(currentFieldName, SHAPE_ID_FIELD)) {
id = parser.text(); id = parser.text();
} else if ("type".equals(currentFieldName)) { } else if (parseContext.parseFieldMatcher().match(currentFieldName, SHAPE_TYPE_FIELD)) {
type = parser.text(); type = parser.text();
} else if ("index".equals(currentFieldName)) { } else if (parseContext.parseFieldMatcher().match(currentFieldName, SHAPE_INDEX_FIELD)) {
index = parser.text(); index = parser.text();
} else if ("path".equals(currentFieldName)) { } else if (parseContext.parseFieldMatcher().match(currentFieldName, SHAPE_PATH_FIELD)) {
shapePath = parser.text(); shapePath = parser.text();
} }
} }
} }
if (id == null) {
throw new QueryParsingException(parseContext, "ID for indexed shape not provided");
} else if (type == null) {
throw new QueryParsingException(parseContext, "Type for indexed shape not provided");
}
GetRequest getRequest = new GetRequest(index, type, id);
getRequest.copyContextAndHeadersFrom(SearchContext.current());
shape = fetch(context.getClient(), getRequest, shapePath);
} else { } else {
throw new QueryParsingException(parseContext, "[geo_shape] query does not support [" + currentFieldName + "]"); throw new QueryParsingException(parseContext, "[geo_shape] query does not support [" + currentFieldName + "]");
} }
} }
} }
} else if (token.isValue()) { } else if (token.isValue()) {
if ("boost".equals(currentFieldName)) { if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
boost = parser.floatValue(); boost = parser.floatValue();
} else if ("_name".equals(currentFieldName)) { } else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
queryName = parser.text(); queryName = parser.text();
} else { } else {
throw new QueryParsingException(parseContext, "[geo_shape] query does not support [" + currentFieldName + "]"); throw new QueryParsingException(parseContext, "[geo_shape] query does not support [" + currentFieldName + "]");
} }
} }
} }
GeoShapeQueryBuilder builder;
if (shape == null) { if (shape != null) {
throw new QueryParsingException(parseContext, "No Shape defined"); builder = new GeoShapeQueryBuilder(fieldName, shape);
} else if (shapeRelation == null) {
throw new QueryParsingException(parseContext, "No Shape Relation defined");
}
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType == null) {
throw new QueryParsingException(parseContext, "Failed to find geo_shape field [" + fieldName + "]");
}
// TODO: This isn't the nicest way to check this
if (!(fieldType instanceof GeoShapeFieldMapper.GeoShapeFieldType)) {
throw new QueryParsingException(parseContext, "Field [" + fieldName + "] is not a geo_shape");
}
GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType;
PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy();
if (strategyName != null) {
strategy = shapeFieldType.resolveStrategy(strategyName);
}
Query query;
if (strategy instanceof RecursivePrefixTreeStrategy && shapeRelation == ShapeRelation.DISJOINT) {
// this strategy doesn't support disjoint anymore: but it did before, including creating lucene fieldcache (!)
// in this case, execute disjoint as exists && !intersects
BooleanQuery.Builder bool = new BooleanQuery.Builder();
Query exists = ExistsQueryBuilder.newFilter(context, fieldName);
Filter intersects = strategy.makeFilter(getArgs(shape, ShapeRelation.INTERSECTS));
bool.add(exists, BooleanClause.Occur.MUST);
bool.add(intersects, BooleanClause.Occur.MUST_NOT);
query = new ConstantScoreQuery(bool.build());
} else { } else {
query = strategy.makeQuery(getArgs(shape, shapeRelation)); builder = new GeoShapeQueryBuilder(fieldName, id, type);
}
if (index != null) {
builder.indexedShapeIndex(index);
}
if (shapePath != null) {
builder.indexedShapePath(shapePath);
}
if (shapeRelation != null) {
builder.relation(shapeRelation);
}
if (strategy != null) {
builder.strategy(strategy);
} }
query.setBoost(boost);
if (queryName != null) { if (queryName != null) {
context.addNamedQuery(queryName, query); builder.queryName(queryName);
}
return query;
}
public static SpatialArgs getArgs(ShapeBuilder shape, ShapeRelation relation) {
switch(relation) {
case DISJOINT:
return new SpatialArgs(SpatialOperation.IsDisjointTo, shape.build());
case INTERSECTS:
return new SpatialArgs(SpatialOperation.Intersects, shape.build());
case WITHIN:
return new SpatialArgs(SpatialOperation.IsWithin, shape.build());
default:
throw new IllegalArgumentException("");
} }
builder.boost(boost);
return builder;
} }
@Override @Override
public GeoShapeQueryBuilder getBuilderPrototype() { public GeoShapeQueryBuilder getBuilderPrototype() {
return GeoShapeQueryBuilder.PROTOTYPE; return GeoShapeQueryBuilder.PROTOTYPE;
} }
/**
* 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
* @return Shape with the given ID
* @throws IOException Can be thrown while parsing the Shape Document and extracting the Shape
*/
private ShapeBuilder fetch(Client client, GetRequest getRequest, String path) throws IOException {
if (ShapesAvailability.JTS_AVAILABLE == false) {
throw new IllegalStateException("JTS not available");
}
getRequest.preference("_local");
getRequest.operationThreaded(false);
GetResponse response = client.get(getRequest).actionGet();
if (!response.isExists()) {
throw new IllegalArgumentException("Shape with ID [" + getRequest.id() + "] in type [" + getRequest.type() + "] not found");
}
String[] pathElements = Strings.splitStringToArray(path, '.');
int currentPathSlot = 0;
XContentParser parser = null;
try {
parser = XContentHelper.createParser(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) {
return ShapeBuilder.parse(parser);
}
} else {
parser.nextToken();
parser.skipChildren();
}
}
}
throw new IllegalStateException("Shape with name [" + getRequest.id() + "] found but missing " + path + " field");
} finally {
if (parser != null) {
parser.close();
}
}
}
} }

View File

@ -31,6 +31,7 @@ import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.Template; import org.elasticsearch.script.Template;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@ -547,17 +548,6 @@ public abstract class QueryBuilders {
return new WrapperQueryBuilder(source); return new WrapperQueryBuilder(source);
} }
/**
* Query that matches Documents based on the relationship between the given shape and
* indexed shapes
*
* @param name The shape field name
* @param shape Shape to use in the Query
*/
public static GeoShapeQueryBuilder geoShapeQuery(String name, ShapeBuilder shape) {
return new GeoShapeQueryBuilder(name, shape);
}
/** /**
* Facilitates creating template query requests using an inline script * Facilitates creating template query requests using an inline script
*/ */
@ -694,16 +684,12 @@ public abstract class QueryBuilders {
* @param shape Shape to use in the filter * @param shape Shape to use in the filter
* @param relation relation of the shapes * @param relation relation of the shapes
*/ */
public static GeoShapeQueryBuilder geoShapeQuery(String name, ShapeBuilder shape, ShapeRelation relation) { public static GeoShapeQueryBuilder geoShapeQuery(String name, ShapeBuilder shape) throws IOException {
return new GeoShapeQueryBuilder(name, shape, relation); return new GeoShapeQueryBuilder(name, shape);
}
public static GeoShapeQueryBuilder geoShapeQuery(String name, String indexedShapeId, String indexedShapeType, ShapeRelation relation) {
return new GeoShapeQueryBuilder(name, indexedShapeId, indexedShapeType, relation);
} }
public static GeoShapeQueryBuilder geoShapeQuery(String name, String indexedShapeId, String indexedShapeType) { public static GeoShapeQueryBuilder geoShapeQuery(String name, String indexedShapeId, String indexedShapeType) {
return geoShapeQuery(name, indexedShapeId, indexedShapeType, null); return new GeoShapeQueryBuilder(name, indexedShapeId, indexedShapeType);
} }
/** /**
@ -712,12 +698,16 @@ public abstract class QueryBuilders {
* @param name The shape field name * @param name The shape field name
* @param shape Shape to use in the filter * @param shape Shape to use in the filter
*/ */
public static GeoShapeQueryBuilder geoIntersectionQuery(String name, ShapeBuilder shape) { public static GeoShapeQueryBuilder geoIntersectionQuery(String name, ShapeBuilder shape) throws IOException {
return geoShapeQuery(name, shape, ShapeRelation.INTERSECTS); GeoShapeQueryBuilder builder = geoShapeQuery(name, shape);
builder.relation(ShapeRelation.INTERSECTS);
return builder;
} }
public static GeoShapeQueryBuilder geoIntersectionQuery(String name, String indexedShapeId, String indexedShapeType) { public static GeoShapeQueryBuilder geoIntersectionQuery(String name, String indexedShapeId, String indexedShapeType) {
return geoShapeQuery(name, indexedShapeId, indexedShapeType, ShapeRelation.INTERSECTS); GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId, indexedShapeType);
builder.relation(ShapeRelation.INTERSECTS);
return builder;
} }
/** /**
@ -726,12 +716,16 @@ public abstract class QueryBuilders {
* @param name The shape field name * @param name The shape field name
* @param shape Shape to use in the filter * @param shape Shape to use in the filter
*/ */
public static GeoShapeQueryBuilder geoWithinQuery(String name, ShapeBuilder shape) { public static GeoShapeQueryBuilder geoWithinQuery(String name, ShapeBuilder shape) throws IOException {
return geoShapeQuery(name, shape, ShapeRelation.WITHIN); GeoShapeQueryBuilder builder = geoShapeQuery(name, shape);
builder.relation(ShapeRelation.WITHIN);
return builder;
} }
public static GeoShapeQueryBuilder geoWithinQuery(String name, String indexedShapeId, String indexedShapeType) { public static GeoShapeQueryBuilder geoWithinQuery(String name, String indexedShapeId, String indexedShapeType) {
return geoShapeQuery(name, indexedShapeId, indexedShapeType, ShapeRelation.WITHIN); GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId, indexedShapeType);
builder.relation(ShapeRelation.WITHIN);
return builder;
} }
/** /**
@ -740,12 +734,16 @@ public abstract class QueryBuilders {
* @param name The shape field name * @param name The shape field name
* @param shape Shape to use in the filter * @param shape Shape to use in the filter
*/ */
public static GeoShapeQueryBuilder geoDisjointQuery(String name, ShapeBuilder shape) { public static GeoShapeQueryBuilder geoDisjointQuery(String name, ShapeBuilder shape) throws IOException {
return geoShapeQuery(name, shape, ShapeRelation.DISJOINT); GeoShapeQueryBuilder builder = geoShapeQuery(name, shape);
builder.relation(ShapeRelation.DISJOINT);
return builder;
} }
public static GeoShapeQueryBuilder geoDisjointQuery(String name, String indexedShapeId, String indexedShapeType) { public static GeoShapeQueryBuilder geoDisjointQuery(String name, String indexedShapeId, String indexedShapeType) {
return geoShapeQuery(name, indexedShapeId, indexedShapeType, ShapeRelation.DISJOINT); GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId, indexedShapeType);
builder.relation(ShapeRelation.DISJOINT);
return builder;
} }
/** /**

View File

@ -20,6 +20,7 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryparser.classic.MapperQueryParser; import org.apache.lucene.queryparser.classic.MapperQueryParser;
import org.apache.lucene.queryparser.classic.QueryParserSettings; import org.apache.lucene.queryparser.classic.QueryParserSettings;
@ -32,12 +33,17 @@ import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.analysis.AnalysisService; import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperBuilders;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.core.StringFieldMapper; import org.elasticsearch.index.mapper.core.StringFieldMapper;
import org.elasticsearch.index.mapper.object.ObjectMapper; import org.elasticsearch.index.mapper.object.ObjectMapper;
import org.elasticsearch.index.query.support.NestedScope; import org.elasticsearch.index.query.support.NestedScope;
@ -50,7 +56,12 @@ import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.SearchLookup;
import java.util.*; import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* Context object used to create lucene queries on the shard level. * Context object used to create lucene queries on the shard level.

View File

@ -0,0 +1,92 @@
/*
* 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.common.geo;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.test.ESTestCase;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
public class ShapeRalationTests extends ESTestCase {
public void testValidOrdinals() {
assertThat(ShapeRelation.INTERSECTS.ordinal(), equalTo(0));
assertThat(ShapeRelation.DISJOINT.ordinal(), equalTo(1));
assertThat(ShapeRelation.WITHIN.ordinal(), equalTo(2));
}
public void testwriteTo() throws Exception {
try (BytesStreamOutput out = new BytesStreamOutput()) {
ShapeRelation.INTERSECTS.writeTo(out);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(in.readVInt(), equalTo(0));
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
ShapeRelation.DISJOINT.writeTo(out);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(in.readVInt(), equalTo(1));
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
ShapeRelation.WITHIN.writeTo(out);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(in.readVInt(), equalTo(2));
}
}
}
public void testReadFrom() throws Exception {
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(0);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(ShapeRelation.DISJOINT.readFrom(in), equalTo(ShapeRelation.INTERSECTS));
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(1);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(ShapeRelation.DISJOINT.readFrom(in), equalTo(ShapeRelation.DISJOINT));
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(2);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(ShapeRelation.DISJOINT.readFrom(in), equalTo(ShapeRelation.WITHIN));
}
}
}
@Test(expected = IOException.class)
public void testInvalidReadFrom() throws Exception {
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(randomIntBetween(3, Integer.MAX_VALUE));
try (StreamInput in = StreamInput.wrap(out.bytes())) {
ShapeRelation.DISJOINT.readFrom(in);
}
}
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.common.geo;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.test.ESTestCase;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
public class SpatialStrategyTests extends ESTestCase {
public void testValidOrdinals() {
assertThat(SpatialStrategy.TERM.ordinal(), equalTo(0));
assertThat(SpatialStrategy.RECURSIVE.ordinal(), equalTo(1));
}
public void testwriteTo() throws Exception {
try (BytesStreamOutput out = new BytesStreamOutput()) {
SpatialStrategy.TERM.writeTo(out);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(in.readVInt(), equalTo(0));
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
SpatialStrategy.RECURSIVE.writeTo(out);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(in.readVInt(), equalTo(1));
}
}
}
public void testReadFrom() throws Exception {
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(0);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(SpatialStrategy.TERM.readFrom(in), equalTo(SpatialStrategy.TERM));
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(1);
try (StreamInput in = StreamInput.wrap(out.bytes())) {
assertThat(SpatialStrategy.TERM.readFrom(in), equalTo(SpatialStrategy.RECURSIVE));
}
}
}
@Test(expected = IOException.class)
public void testInvalidReadFrom() throws Exception {
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeVInt(randomIntBetween(2, Integer.MAX_VALUE));
try (StreamInput in = StreamInput.wrap(out.bytes())) {
SpatialStrategy.TERM.readFrom(in);
}
}
}
}

View File

@ -22,14 +22,12 @@ package org.elasticsearch.index.mapper.externalvalues;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase;
import org.junit.Test; import org.junit.Test;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -74,7 +72,7 @@ public class ExternalValuesMapperIntegrationIT extends ESIntegTestCase {
assertThat(response.getHits().totalHits(), equalTo((long) 1)); assertThat(response.getHits().totalHits(), equalTo((long) 1));
response = client().prepareSearch("test-idx") response = client().prepareSearch("test-idx")
.setPostFilter(QueryBuilders.geoShapeQuery("field.shape", ShapeBuilder.newPoint(-100, 45), ShapeRelation.WITHIN)) .setPostFilter(QueryBuilders.geoShapeQuery("field.shape", ShapeBuilder.newPoint(-100, 45)).relation(ShapeRelation.WITHIN))
.execute().actionGet(); .execute().actionGet();
assertThat(response.getHits().totalHits(), equalTo((long) 1)); assertThat(response.getHits().totalHits(), equalTo((long) 1));

View File

@ -110,11 +110,12 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
protected static final String BOOLEAN_FIELD_NAME = "mapped_boolean"; protected static final String BOOLEAN_FIELD_NAME = "mapped_boolean";
protected static final String DATE_FIELD_NAME = "mapped_date"; protected static final String DATE_FIELD_NAME = "mapped_date";
protected static final String OBJECT_FIELD_NAME = "mapped_object"; protected static final String OBJECT_FIELD_NAME = "mapped_object";
protected static final String GEO_FIELD_NAME = "mapped_geo"; protected static final String GEO_POINT_FIELD_NAME = "mapped_geo_point";
protected static final String[] MAPPED_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, protected static final String GEO_SHAPE_FIELD_NAME = "mapped_geo_shape";
DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, OBJECT_FIELD_NAME, GEO_FIELD_NAME }; protected static final String[] MAPPED_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME,
protected static final String[] MAPPED_LEAF_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, OBJECT_FIELD_NAME, GEO_POINT_FIELD_NAME, GEO_SHAPE_FIELD_NAME };
DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, GEO_FIELD_NAME }; protected static final String[] MAPPED_LEAF_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME,
BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, GEO_POINT_FIELD_NAME };
private static Injector injector; private static Injector injector;
private static IndexQueryParserService queryParserService; private static IndexQueryParserService queryParserService;
@ -204,7 +205,8 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
BOOLEAN_FIELD_NAME, "type=boolean", BOOLEAN_FIELD_NAME, "type=boolean",
DATE_FIELD_NAME, "type=date", DATE_FIELD_NAME, "type=date",
OBJECT_FIELD_NAME, "type=object", OBJECT_FIELD_NAME, "type=object",
GEO_FIELD_NAME, "type=geo_point,lat_lon=true,geohash=true,geohash_prefix=true" GEO_POINT_FIELD_NAME, "type=geo_point,lat_lon=true,geohash=true,geohash_prefix=true",
GEO_SHAPE_FIELD_NAME, "type=geo_shape"
).string()), false, false); ).string()), false, false);
// also add mappings for two inner field in the object field // also add mappings for two inner field in the object field
mapperService.merge(type, new CompressedXContent("{\"properties\":{\""+OBJECT_FIELD_NAME+"\":{\"type\":\"object\"," mapperService.merge(type, new CompressedXContent("{\"properties\":{\""+OBJECT_FIELD_NAME+"\":{\"type\":\"object\","

View File

@ -38,7 +38,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Override @Override
protected GeoDistanceRangeQueryBuilder doCreateTestQueryBuilder() { protected GeoDistanceRangeQueryBuilder doCreateTestQueryBuilder() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
if (randomBoolean()) { if (randomBoolean()) {
builder.geohash(randomGeohash(1, 12)); builder.geohash(randomGeohash(1, 12));
} else { } else {
@ -164,7 +164,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Test @Test
public void testNoPoint() { public void testNoPoint() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
builder.from(10); builder.from(10);
QueryValidationException exception = builder.validate(); QueryValidationException exception = builder.validate();
assertThat(exception, notNullValue()); assertThat(exception, notNullValue());
@ -175,7 +175,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Test @Test
public void testNoFromOrTo() { public void testNoFromOrTo() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
String geohash = randomGeohash(1, 20); String geohash = randomGeohash(1, 20);
builder.geohash(geohash); builder.geohash(geohash);
QueryValidationException exception = builder.validate(); QueryValidationException exception = builder.validate();
@ -188,7 +188,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Test @Test
public void testInvalidFrom() { public void testInvalidFrom() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
String geohash = randomGeohash(1, 20); String geohash = randomGeohash(1, 20);
builder.geohash(geohash); builder.geohash(geohash);
builder.from(new DateTime()); builder.from(new DateTime());
@ -202,7 +202,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Test @Test
public void testInvalidTo() { public void testInvalidTo() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
String geohash = randomGeohash(1, 20); String geohash = randomGeohash(1, 20);
builder.geohash(geohash); builder.geohash(geohash);
builder.to(new DateTime()); builder.to(new DateTime());
@ -216,7 +216,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Test @Test
public void testInvalidOptimizeBBox() { public void testInvalidOptimizeBBox() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
String geohash = randomGeohash(1, 20); String geohash = randomGeohash(1, 20);
builder.geohash(geohash); builder.geohash(geohash);
builder.from(10); builder.from(10);
@ -231,7 +231,7 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
@Test @Test
public void testMultipleValidationErrors() { public void testMultipleValidationErrors() {
GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_FIELD_NAME); GeoDistanceRangeQueryBuilder builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME);
double lat = randomDouble() * 360 - 180; double lat = randomDouble() * 360 - 180;
double lon = randomDouble() * 360 - 180; double lon = randomDouble() * 360 - 180;
builder.point(lat, lon); builder.point(lat, lon);

View File

@ -48,9 +48,9 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
GeoPolygonQueryBuilder builder; GeoPolygonQueryBuilder builder;
List<GeoPoint> polygon = randomPolygon(randomIntBetween(4, 50)); List<GeoPoint> polygon = randomPolygon(randomIntBetween(4, 50));
if (randomBoolean()) { if (randomBoolean()) {
builder = new GeoPolygonQueryBuilder(GEO_FIELD_NAME, polygon); builder = new GeoPolygonQueryBuilder(GEO_POINT_FIELD_NAME, polygon);
} else { } else {
builder = new GeoPolygonQueryBuilder(GEO_FIELD_NAME); builder = new GeoPolygonQueryBuilder(GEO_POINT_FIELD_NAME);
for (GeoPoint point : polygon) { for (GeoPoint point : polygon) {
int method = randomInt(2); int method = randomInt(2);
switch (method) { switch (method) {
@ -128,7 +128,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
@Test @Test
public void testEmptyPolygon() { public void testEmptyPolygon() {
GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_FIELD_NAME); GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_POINT_FIELD_NAME);
QueryValidationException exception = builder.validate(); QueryValidationException exception = builder.validate();
assertThat(exception, notNullValue()); assertThat(exception, notNullValue());
assertThat(exception.validationErrors(), notNullValue()); assertThat(exception.validationErrors(), notNullValue());
@ -139,7 +139,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
@Test @Test
public void testInvalidClosedPolygon() { public void testInvalidClosedPolygon() {
GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_FIELD_NAME); GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_POINT_FIELD_NAME);
builder.addPoint(new GeoPoint(0, 90)); builder.addPoint(new GeoPoint(0, 90));
builder.addPoint(new GeoPoint(90, 90)); builder.addPoint(new GeoPoint(90, 90));
builder.addPoint(new GeoPoint(0, 90)); builder.addPoint(new GeoPoint(0, 90));
@ -153,7 +153,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
@Test @Test
public void testInvalidOpenPolygon() { public void testInvalidOpenPolygon() {
GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_FIELD_NAME); GeoPolygonQueryBuilder builder = new GeoPolygonQueryBuilder(GEO_POINT_FIELD_NAME);
builder.addPoint(new GeoPoint(0, 90)); builder.addPoint(new GeoPoint(0, 90));
builder.addPoint(new GeoPoint(90, 90)); builder.addPoint(new GeoPoint(90, 90));
QueryValidationException exception = builder.validate(); QueryValidationException exception = builder.validate();
@ -168,7 +168,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint(); XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject(); builder.startObject();
builder.startObject("geo_polygon"); builder.startObject("geo_polygon");
builder.startObject(GEO_FIELD_NAME); builder.startObject(GEO_POINT_FIELD_NAME);
builder.startArray("points"); builder.startArray("points");
builder.value("0,0"); builder.value("0,0");
builder.value("0,90"); builder.value("0,90");

View File

@ -19,13 +19,196 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder; import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
public class GeoShapeQueryBuilderTests extends ESTestCase { import java.io.IOException;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
@Repeat(iterations = 100)
public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQueryBuilder> {
private static String indexedShapeId;
private static String indexedShapeType;
private static String indexedShapePath;
private static String indexedShapeIndex;
private static ShapeBuilder indexedShapeToReturn;
@Override
protected GeoShapeQueryBuilder doCreateTestQueryBuilder() {
ShapeBuilder shape = RandomShapeGenerator.createShapeWithin(getRandom(), null);
GeoShapeQueryBuilder builder;
if (randomBoolean()) {
try {
builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
indexedShapeToReturn = shape;
indexedShapeId = randomAsciiOfLengthBetween(3, 20);
indexedShapeType = randomAsciiOfLengthBetween(3, 20);
builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeId, indexedShapeType);
if (randomBoolean()) {
indexedShapeIndex = randomAsciiOfLengthBetween(3, 20);
builder.indexedShapeIndex(indexedShapeIndex);
}
if (randomBoolean()) {
indexedShapePath = randomAsciiOfLengthBetween(3, 20);
builder.indexedShapePath(indexedShapePath);
}
}
SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
builder.strategy(strategy);
if (strategy != SpatialStrategy.TERM) {
builder.relation(randomFrom(ShapeRelation.values()));
}
return builder;
}
@Override
protected GetResponse executeGet(GetRequest getRequest) {
assertThat(indexedShapeToReturn, notNullValue());
assertThat(indexedShapeId, notNullValue());
assertThat(indexedShapeType, notNullValue());
assertThat(getRequest.id(), equalTo(indexedShapeId));
assertThat(getRequest.type(), equalTo(indexedShapeType));
String expectedShapeIndex = indexedShapeIndex == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_INDEX_NAME : indexedShapeIndex;
assertThat(getRequest.index(), equalTo(expectedShapeIndex));
String expectedShapePath = indexedShapePath == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_FIELD_NAME : indexedShapePath;
String json;
try {
XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject();
builder.field(expectedShapePath, indexedShapeToReturn);
builder.endObject();
json = builder.string();
} catch (IOException ex) {
throw new ElasticsearchException("boom", ex);
}
GetResponse response = new GetResponse(new GetResult(indexedShapeIndex, indexedShapeType, indexedShapeId, 0, true, new BytesArray(
json), null));
return response;
}
@After
public void clearShapeFields() {
indexedShapeToReturn = null;
indexedShapeId = null;
indexedShapeType = null;
indexedShapePath = null;
indexedShapeIndex = null;
}
@Override
protected void doAssertLuceneQuery(GeoShapeQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
// Logic for doToQuery is complex and is hard to test here. Need to rely
// on Integration tests to determine if created query is correct
// TODO improve GeoShapeQueryBuilder.doToQuery() method to make it
// easier to test here
assertThat(query, anyOf(instanceOf(BooleanQuery.class), instanceOf(ConstantScoreQuery.class)));
}
/**
* Overridden here to ensure the test is only run if at least one type is
* present in the mappings. Geo queries do not execute if the field is not
* explicitly mapped
*/
@Override
public void testToQuery() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testToQuery();
}
@Test(expected = IllegalArgumentException.class)
public void testNoFieldName() throws Exception {
ShapeBuilder shape = RandomShapeGenerator.createShapeWithin(getRandom(), null);
new GeoShapeQueryBuilder(null, shape);
}
@Test
public void testNoShape() throws IOException {
try {
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, (ShapeBuilder) null);
QueryValidationException exception = builder.validate();
assertThat(exception, notNullValue());
assertThat(exception.validationErrors(), notNullValue());
assertThat(exception.validationErrors().size(), equalTo(1));
assertThat(exception.validationErrors().get(0), equalTo("[" + GeoShapeQueryBuilder.NAME + "] No Shape defined"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test(expected = IllegalArgumentException.class)
public void testNoIndexedShape() throws IOException {
new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, (String) null, "type");
}
@Test(expected = IllegalArgumentException.class)
public void testNoIndexedShapeType() throws IOException {
new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, "id", (String) null);
}
@Test
public void testNoRelation() {
ShapeBuilder shape = RandomShapeGenerator.createShapeWithin(getRandom(), null);
try {
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
builder.relation(null);
QueryValidationException exception = builder.validate();
assertThat(exception, notNullValue());
assertThat(exception.validationErrors(), notNullValue());
assertThat(exception.validationErrors().size(), equalTo(1));
assertThat(exception.validationErrors().get(0), equalTo("[" + GeoShapeQueryBuilder.NAME + "] No Shape Relation defined"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
public void testInvalidRelation() {
ShapeBuilder shape = RandomShapeGenerator.createShapeWithin(getRandom(), null);
try {
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
builder.strategy(SpatialStrategy.TERM);
ShapeRelation relation = randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN);
builder.relation(relation);
QueryValidationException exception = builder.validate();
assertThat(exception, notNullValue());
assertThat(exception.validationErrors(), notNullValue());
assertThat(exception.validationErrors().size(), equalTo(1));
assertThat(
exception.validationErrors().get(0),
equalTo("[" + GeoShapeQueryBuilder.NAME + "] strategy [" + SpatialStrategy.TERM.getStrategyName()
+ "] only supports relation [" + ShapeRelation.INTERSECTS.getRelationName() + "] found relation ["
+ relation.getRelationName() + "]"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test // see #3878 @Test // see #3878
public void testThatXContentSerializationInsideOfArrayWorks() throws Exception { public void testThatXContentSerializationInsideOfArrayWorks() throws Exception {

View File

@ -38,7 +38,7 @@ public class GeohashCellQueryBuilderTests extends AbstractQueryTestCase<Builder>
@Override @Override
protected Builder doCreateTestQueryBuilder() { protected Builder doCreateTestQueryBuilder() {
GeohashCellQuery.Builder builder = new Builder(GEO_FIELD_NAME); GeohashCellQuery.Builder builder = new Builder(GEO_POINT_FIELD_NAME);
builder.geohash(randomGeohash(1, 12)); builder.geohash(randomGeohash(1, 12));
if (randomBoolean()) { if (randomBoolean()) {
builder.neighbors(randomBoolean()); builder.neighbors(randomBoolean());
@ -95,7 +95,7 @@ public class GeohashCellQueryBuilderTests extends AbstractQueryTestCase<Builder>
@Test @Test
public void testNullGeohash() { public void testNullGeohash() {
GeohashCellQuery.Builder builder = new Builder(GEO_FIELD_NAME); GeohashCellQuery.Builder builder = new Builder(GEO_POINT_FIELD_NAME);
QueryValidationException exception = builder.validate(); QueryValidationException exception = builder.validate();
assertThat(exception, notNullValue()); assertThat(exception, notNullValue());
assertThat(exception.validationErrors(), notNullValue()); assertThat(exception.validationErrors(), notNullValue());
@ -105,7 +105,7 @@ public class GeohashCellQueryBuilderTests extends AbstractQueryTestCase<Builder>
@Test @Test
public void testInvalidPrecision() { public void testInvalidPrecision() {
GeohashCellQuery.Builder builder = new Builder(GEO_FIELD_NAME); GeohashCellQuery.Builder builder = new Builder(GEO_POINT_FIELD_NAME);
builder.geohash(randomGeohash(1, 12)); builder.geohash(randomGeohash(1, 12));
builder.precision(-1); builder.precision(-1);
QueryValidationException exception = builder.validate(); QueryValidationException exception = builder.validate();

View File

@ -49,7 +49,10 @@ import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
public class GeoShapeIntegrationIT extends ESIntegTestCase { public class GeoShapeIntegrationIT extends ESIntegTestCase {
@ -286,28 +289,28 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
.endObject().endObject())); .endObject().endObject()));
ensureSearchable("test", "shapes"); ensureSearchable("test", "shapes");
GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS) GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
.indexedShapeIndex("shapes") .indexedShapeIndex("shapes")
.indexedShapePath("location"); .indexedShapePath("location");
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);
assertHitCount(result, 1); assertHitCount(result, 1);
filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS) filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
.indexedShapeIndex("shapes") .indexedShapeIndex("shapes")
.indexedShapePath("1.location"); .indexedShapePath("1.location");
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);
assertHitCount(result, 1); assertHitCount(result, 1);
filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS) filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
.indexedShapeIndex("shapes") .indexedShapeIndex("shapes")
.indexedShapePath("1.2.location"); .indexedShapePath("1.2.location");
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);
assertHitCount(result, 1); assertHitCount(result, 1);
filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS) filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
.indexedShapeIndex("shapes") .indexedShapeIndex("shapes")
.indexedShapePath("1.2.3.location"); .indexedShapePath("1.2.3.location");
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
@ -360,7 +363,8 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
ShapeBuilder filterShape = (gcb.getShapeAt(randomIntBetween(0, gcb.numShapes() - 1))); ShapeBuilder filterShape = (gcb.getShapeAt(randomIntBetween(0, gcb.numShapes() - 1)));
GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", filterShape, ShapeRelation.INTERSECTS); GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", filterShape);
filter.relation(ShapeRelation.INTERSECTS);
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);
@ -399,19 +403,30 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
.setSource(docSource)); .setSource(docSource));
ensureSearchable("test"); ensureSearchable("test");
GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", ShapeBuilder.newGeometryCollection().polygon(ShapeBuilder.newPolygon().point(99.0, -1.0).point(99.0, 3.0).point(103.0, 3.0).point(103.0, -1.0).point(99.0, -1.0)), ShapeRelation.INTERSECTS); GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery(
"location",
ShapeBuilder.newGeometryCollection()
.polygon(
ShapeBuilder.newPolygon().point(99.0, -1.0).point(99.0, 3.0).point(103.0, 3.0).point(103.0, -1.0)
.point(99.0, -1.0))).relation(ShapeRelation.INTERSECTS);
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);
assertHitCount(result, 1); assertHitCount(result, 1);
filter = QueryBuilders.geoShapeQuery("location", ShapeBuilder.newGeometryCollection().polygon(ShapeBuilder.newPolygon().point(199.0, -11.0).point(199.0, 13.0).point(193.0, 13.0).point(193.0, -11.0).point(199.0, -11.0)), ShapeRelation.INTERSECTS); filter = QueryBuilders.geoShapeQuery(
"location",
ShapeBuilder.newGeometryCollection().polygon(
ShapeBuilder.newPolygon().point(199.0, -11.0).point(199.0, 13.0).point(193.0, 13.0).point(193.0, -11.0)
.point(199.0, -11.0))).relation(ShapeRelation.INTERSECTS);
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);
assertHitCount(result, 0); assertHitCount(result, 0);
filter = QueryBuilders.geoShapeQuery("location", ShapeBuilder.newGeometryCollection() filter = QueryBuilders.geoShapeQuery("location", ShapeBuilder.newGeometryCollection()
.polygon(ShapeBuilder.newPolygon().point(99.0, -1.0).point(99.0, 3.0).point(103.0, 3.0).point(103.0, -1.0).point(99.0, -1.0)) .polygon(ShapeBuilder.newPolygon().point(99.0, -1.0).point(99.0, 3.0).point(103.0, 3.0).point(103.0, -1.0).point(99.0, -1.0))
.polygon(ShapeBuilder.newPolygon().point(199.0, -11.0).point(199.0, 13.0).point(193.0, 13.0).point(193.0, -11.0).point(199.0, -11.0)), ShapeRelation.INTERSECTS); .polygon(
ShapeBuilder.newPolygon().point(199.0, -11.0).point(199.0, 13.0).point(193.0, 13.0).point(193.0, -11.0)
.point(199.0, -11.0))).relation(ShapeRelation.INTERSECTS);
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
.setPostFilter(filter).get(); .setPostFilter(filter).get();
assertSearchResponse(result); assertSearchResponse(result);