Make doc lookup in geo_shape filter and query consistent with terms lookup.
The `geo_shape filter and query` option in geo_shape filter and query has been replaced with the `path` option, which allows these filter and query to fetch shapes from within objects as well. Closes #4486
This commit is contained in:
parent
2d77e2a37e
commit
a3d6216f40
|
@ -19,13 +19,13 @@
|
||||||
|
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.geo.ShapeRelation;
|
import org.elasticsearch.common.geo.ShapeRelation;
|
||||||
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.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link FilterBuilder} that builds a GeoShape Filter
|
* {@link FilterBuilder} that builds a GeoShape Filter
|
||||||
*/
|
*/
|
||||||
|
@ -46,7 +46,7 @@ public class GeoShapeFilterBuilder extends BaseFilterBuilder {
|
||||||
private final String indexedShapeType;
|
private final String indexedShapeType;
|
||||||
|
|
||||||
private String indexedShapeIndex;
|
private String indexedShapeIndex;
|
||||||
private String indexedShapeFieldName;
|
private String indexedShapePath;
|
||||||
|
|
||||||
private ShapeRelation relation = null;
|
private ShapeRelation relation = null;
|
||||||
|
|
||||||
|
@ -150,13 +150,13 @@ public class GeoShapeFilterBuilder extends BaseFilterBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the name 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
|
||||||
*
|
*
|
||||||
* @param indexedShapeFieldName Name of the field where the Shape itself is defined
|
* @param indexedShapePath Path of the field where the Shape itself is defined
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public GeoShapeFilterBuilder indexedShapeFieldName(String indexedShapeFieldName) {
|
public GeoShapeFilterBuilder indexedShapePath(String indexedShapePath) {
|
||||||
this.indexedShapeFieldName = indexedShapeFieldName;
|
this.indexedShapePath = indexedShapePath;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,8 +190,8 @@ public class GeoShapeFilterBuilder extends BaseFilterBuilder {
|
||||||
if (indexedShapeIndex != null) {
|
if (indexedShapeIndex != null) {
|
||||||
builder.field("index", indexedShapeIndex);
|
builder.field("index", indexedShapeIndex);
|
||||||
}
|
}
|
||||||
if (indexedShapeFieldName != null) {
|
if (indexedShapePath != null) {
|
||||||
builder.field("shape_field_name", indexedShapeFieldName);
|
builder.field("path", indexedShapePath);
|
||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class GeoShapeFilterParser implements FilterParser {
|
||||||
String id = null;
|
String id = null;
|
||||||
String type = null;
|
String type = null;
|
||||||
String index = DEFAULTS.INDEX_NAME;
|
String index = DEFAULTS.INDEX_NAME;
|
||||||
String shapeFieldName = DEFAULTS.SHAPE_FIELD_NAME;
|
String shapePath = DEFAULTS.SHAPE_FIELD_NAME;
|
||||||
|
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
|
@ -124,8 +124,8 @@ public class GeoShapeFilterParser implements FilterParser {
|
||||||
type = parser.text();
|
type = parser.text();
|
||||||
} else if ("index".equals(currentFieldName)) {
|
} else if ("index".equals(currentFieldName)) {
|
||||||
index = parser.text();
|
index = parser.text();
|
||||||
} else if ("shape_field_name".equals(currentFieldName)) {
|
} else if ("path".equals(currentFieldName)) {
|
||||||
shapeFieldName = parser.text();
|
shapePath = parser.text();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,8 +134,8 @@ public class GeoShapeFilterParser implements FilterParser {
|
||||||
} else if (type == null) {
|
} else if (type == null) {
|
||||||
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
|
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
|
||||||
}
|
}
|
||||||
shape = fetchService.fetch(id, type, index, shapeFieldName);
|
shape = fetchService.fetch(id, type, index, shapePath);
|
||||||
} else {
|
} else {
|
||||||
throw new QueryParsingException(parseContext.index(), "[geo_shape] filter does not support [" + currentFieldName + "]");
|
throw new QueryParsingException(parseContext.index(), "[geo_shape] filter does not support [" + currentFieldName + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQ
|
||||||
private final String indexedShapeType;
|
private final String indexedShapeType;
|
||||||
|
|
||||||
private String indexedShapeIndex;
|
private String indexedShapeIndex;
|
||||||
private String indexedShapeFieldName;
|
private String indexedShapePath;
|
||||||
|
|
||||||
private String queryName;
|
private String queryName;
|
||||||
|
|
||||||
|
@ -109,13 +109,13 @@ public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQ
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the name 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
|
||||||
*
|
*
|
||||||
* @param indexedShapeFieldName Name of the field where the Shape itself is defined
|
* @param indexedShapePath path of the field where the Shape itself is defined
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public GeoShapeQueryBuilder indexedShapeFieldName(String indexedShapeFieldName) {
|
public GeoShapeQueryBuilder indexedShapePath(String indexedShapePath) {
|
||||||
this.indexedShapeFieldName = indexedShapeFieldName;
|
this.indexedShapePath = indexedShapePath;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,8 +146,8 @@ public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQ
|
||||||
if (indexedShapeIndex != null) {
|
if (indexedShapeIndex != null) {
|
||||||
builder.field("index", indexedShapeIndex);
|
builder.field("index", indexedShapeIndex);
|
||||||
}
|
}
|
||||||
if (indexedShapeFieldName != null) {
|
if (indexedShapePath != null) {
|
||||||
builder.field("shape_field_name", indexedShapeFieldName);
|
builder.field("path", indexedShapePath);
|
||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class GeoShapeQueryParser implements QueryParser {
|
||||||
String id = null;
|
String id = null;
|
||||||
String type = null;
|
String type = null;
|
||||||
String index = DEFAULTS.INDEX_NAME;
|
String index = DEFAULTS.INDEX_NAME;
|
||||||
String shapeFieldName = DEFAULTS.SHAPE_FIELD_NAME;
|
String shapePath = DEFAULTS.SHAPE_FIELD_NAME;
|
||||||
|
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
|
@ -102,8 +102,8 @@ public class GeoShapeQueryParser implements QueryParser {
|
||||||
type = parser.text();
|
type = parser.text();
|
||||||
} else if ("index".equals(currentFieldName)) {
|
} else if ("index".equals(currentFieldName)) {
|
||||||
index = parser.text();
|
index = parser.text();
|
||||||
} else if ("shape_field_name".equals(currentFieldName)) {
|
} else if ("path".equals(currentFieldName)) {
|
||||||
shapeFieldName = parser.text();
|
shapePath = parser.text();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ public class GeoShapeQueryParser implements QueryParser {
|
||||||
} else if (type == null) {
|
} else if (type == null) {
|
||||||
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
|
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
|
||||||
}
|
}
|
||||||
shape = fetchService.fetch(id, type, index, shapeFieldName);
|
shape = fetchService.fetch(id, type, index, shapePath);
|
||||||
} else {
|
} else {
|
||||||
throw new QueryParsingException(parseContext.index(), "[geo_shape] query does not support [" + currentFieldName + "]");
|
throw new QueryParsingException(parseContext.index(), "[geo_shape] query does not support [" + currentFieldName + "]");
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.ElasticSearchIllegalStateException;
|
||||||
import org.elasticsearch.action.get.GetRequest;
|
import org.elasticsearch.action.get.GetRequest;
|
||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
|
@ -49,35 +50,40 @@ public class ShapeFetchService extends AbstractComponent {
|
||||||
/**
|
/**
|
||||||
* Fetches the Shape with the given ID in the given type and index.
|
* Fetches the Shape with the given ID in the given type and index.
|
||||||
*
|
*
|
||||||
* @param id ID of the Shape to fetch
|
* @param id ID of the Shape to fetch
|
||||||
* @param type Index type where the Shape is indexed
|
* @param type Index type where the Shape is indexed
|
||||||
* @param index Index where the Shape is indexed
|
* @param index Index where the Shape is indexed
|
||||||
* @param shapeField Name of the field in the Shape Document where the Shape itself is located
|
* @param path Name or path of the field in the Shape Document where the Shape itself is located
|
||||||
* @return Shape with the given ID
|
* @return Shape with the given ID
|
||||||
* @throws IOException Can be thrown while parsing the Shape Document and extracting the Shape
|
* @throws IOException Can be thrown while parsing the Shape Document and extracting the Shape
|
||||||
*/
|
*/
|
||||||
public ShapeBuilder fetch(String id, String type, String index, String shapeField) throws IOException {
|
public ShapeBuilder fetch(String id, String type, String index, String path) throws IOException {
|
||||||
GetResponse response = client.get(new GetRequest(index, type, id).preference("_local").operationThreaded(false)).actionGet();
|
GetResponse response = client.get(new GetRequest(index, type, id).preference("_local").operationThreaded(false)).actionGet();
|
||||||
if (!response.isExists()) {
|
if (!response.isExists()) {
|
||||||
throw new ElasticSearchIllegalArgumentException("Shape with ID [" + id + "] in type [" + type + "] not found");
|
throw new ElasticSearchIllegalArgumentException("Shape with ID [" + id + "] in type [" + type + "] not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String[] pathElements = Strings.splitStringToArray(path, '.');
|
||||||
|
int currentPathSlot = 0;
|
||||||
|
|
||||||
XContentParser parser = null;
|
XContentParser parser = null;
|
||||||
try {
|
try {
|
||||||
parser = XContentHelper.createParser(response.getSourceAsBytesRef());
|
parser = XContentHelper.createParser(response.getSourceAsBytesRef());
|
||||||
XContentParser.Token currentToken;
|
XContentParser.Token currentToken;
|
||||||
while ((currentToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
while ((currentToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
if (currentToken == XContentParser.Token.FIELD_NAME) {
|
if (currentToken == XContentParser.Token.FIELD_NAME) {
|
||||||
if (shapeField.equals(parser.currentName())) {
|
if (pathElements[currentPathSlot].equals(parser.currentName())) {
|
||||||
parser.nextToken();
|
parser.nextToken();
|
||||||
return ShapeBuilder.parse(parser);
|
if (++currentPathSlot == pathElements.length) {
|
||||||
|
return ShapeBuilder.parse(parser);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
parser.nextToken();
|
parser.nextToken();
|
||||||
parser.skipChildren();
|
parser.skipChildren();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new ElasticSearchIllegalStateException("Shape with name [" + id + "] found but missing " + shapeField + " field");
|
throw new ElasticSearchIllegalStateException("Shape with name [" + id + "] found but missing " + path + " field");
|
||||||
} finally {
|
} finally {
|
||||||
if (parser != null) {
|
if (parser != null) {
|
||||||
parser.close();
|
parser.close();
|
||||||
|
|
|
@ -21,15 +21,17 @@ package org.elasticsearch.search.geo;
|
||||||
|
|
||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
|
import org.elasticsearch.common.geo.ShapeRelation;
|
||||||
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.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.*;
|
||||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
|
@ -245,13 +247,85 @@ public class GeoShapeIntegrationTests extends ElasticsearchIntegrationTest {
|
||||||
+ "\"id\": \"1\","
|
+ "\"id\": \"1\","
|
||||||
+ "\"type\": \"type1\","
|
+ "\"type\": \"type1\","
|
||||||
+ "\"index\": \"test\","
|
+ "\"index\": \"test\","
|
||||||
+ "\"shape_field_name\": \"location2\""
|
+ "\"path\": \"location2\""
|
||||||
+ "}}}}";
|
+ "}}}}";
|
||||||
|
|
||||||
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).execute().actionGet();
|
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).execute().actionGet();
|
||||||
assertHitCount(result, 1);
|
assertHitCount(result, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShapeFetching_path() throws IOException {
|
||||||
|
prepareCreate("shapes").execute().actionGet();
|
||||||
|
prepareCreate("test").addMapping("type", "location", "type=geo_shape").execute().actionGet();
|
||||||
|
String location = "\"location\" : {\"type\":\"polygon\", \"coordinates\":[[[-10,-10],[10,-10],[10,10],[-10,10],[-10,-10]]]}";
|
||||||
|
client().prepareIndex("shapes", "type", "1")
|
||||||
|
.setSource(
|
||||||
|
String.format(
|
||||||
|
Locale.ROOT, "{ %s, \"1\" : { %s, \"2\" : { %s, \"3\" : { %s } }} }", location, location, location, location
|
||||||
|
)
|
||||||
|
).get();
|
||||||
|
client().prepareIndex("test", "type", "1")
|
||||||
|
.setSource(jsonBuilder().startObject().startObject("location")
|
||||||
|
.field("type", "polygon")
|
||||||
|
.startArray("coordinates").startArray()
|
||||||
|
.startArray().value(-20).value(-20).endArray()
|
||||||
|
.startArray().value(20).value(-20).endArray()
|
||||||
|
.startArray().value(20).value(20).endArray()
|
||||||
|
.startArray().value(-20).value(20).endArray()
|
||||||
|
.startArray().value(-20).value(-20).endArray()
|
||||||
|
.endArray().endArray()
|
||||||
|
.endObject().endObject()).get();
|
||||||
|
client().admin().indices().prepareRefresh("test", "shapes").execute().actionGet();
|
||||||
|
|
||||||
|
GeoShapeFilterBuilder filter = FilterBuilders.geoShapeFilter("location", "1", "type", ShapeRelation.INTERSECTS)
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("location");
|
||||||
|
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||||
|
.setPostFilter(filter).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
filter = FilterBuilders.geoShapeFilter("location", "1", "type", ShapeRelation.INTERSECTS)
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("1.location");
|
||||||
|
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||||
|
.setPostFilter(filter).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
filter = FilterBuilders.geoShapeFilter("location", "1", "type", ShapeRelation.INTERSECTS)
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("1.2.location");
|
||||||
|
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||||
|
.setPostFilter(filter).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
filter = FilterBuilders.geoShapeFilter("location", "1", "type", ShapeRelation.INTERSECTS)
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("1.2.3.location");
|
||||||
|
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||||
|
.setPostFilter(filter).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
|
||||||
|
// now test the query variant
|
||||||
|
GeoShapeQueryBuilder query = QueryBuilders.geoShapeQuery("location", "1", "type")
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("location");
|
||||||
|
result = client().prepareSearch("test").setQuery(query).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
query = QueryBuilders.geoShapeQuery("location", "1", "type")
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("1.location");
|
||||||
|
result = client().prepareSearch("test").setQuery(query).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
query = QueryBuilders.geoShapeQuery("location", "1", "type")
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("1.2.location");
|
||||||
|
result = client().prepareSearch("test").setQuery(query).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
query = QueryBuilders.geoShapeQuery("location", "1", "type")
|
||||||
|
.indexedShapeIndex("shapes")
|
||||||
|
.indexedShapePath("1.2.3.location");
|
||||||
|
result = client().prepareSearch("test").setQuery(query).get();
|
||||||
|
assertHitCount(result, 1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test // Issue 2944
|
@Test // Issue 2944
|
||||||
public void testThatShapeIsReturnedEvenWhenExclusionsAreSet() throws Exception {
|
public void testThatShapeIsReturnedEvenWhenExclusionsAreSet() throws Exception {
|
||||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||||
|
|
Loading…
Reference in New Issue