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:
Martijn van Groningen 2013-12-18 13:53:21 +01:00
parent 2d77e2a37e
commit a3d6216f40
6 changed files with 115 additions and 35 deletions

View File

@ -19,13 +19,13 @@
package org.elasticsearch.index.query;
import java.io.IOException;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
/**
* {@link FilterBuilder} that builds a GeoShape Filter
*/
@ -46,7 +46,7 @@ public class GeoShapeFilterBuilder extends BaseFilterBuilder {
private final String indexedShapeType;
private String indexedShapeIndex;
private String indexedShapeFieldName;
private String indexedShapePath;
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
*/
public GeoShapeFilterBuilder indexedShapeFieldName(String indexedShapeFieldName) {
this.indexedShapeFieldName = indexedShapeFieldName;
public GeoShapeFilterBuilder indexedShapePath(String indexedShapePath) {
this.indexedShapePath = indexedShapePath;
return this;
}
@ -190,8 +190,8 @@ public class GeoShapeFilterBuilder extends BaseFilterBuilder {
if (indexedShapeIndex != null) {
builder.field("index", indexedShapeIndex);
}
if (indexedShapeFieldName != null) {
builder.field("shape_field_name", indexedShapeFieldName);
if (indexedShapePath != null) {
builder.field("path", indexedShapePath);
}
builder.endObject();
}

View File

@ -88,7 +88,7 @@ public class GeoShapeFilterParser implements FilterParser {
String id = null;
String type = null;
String index = DEFAULTS.INDEX_NAME;
String shapeFieldName = DEFAULTS.SHAPE_FIELD_NAME;
String shapePath = DEFAULTS.SHAPE_FIELD_NAME;
XContentParser.Token token;
String currentFieldName = null;
@ -124,8 +124,8 @@ public class GeoShapeFilterParser implements FilterParser {
type = parser.text();
} else if ("index".equals(currentFieldName)) {
index = parser.text();
} else if ("shape_field_name".equals(currentFieldName)) {
shapeFieldName = parser.text();
} else if ("path".equals(currentFieldName)) {
shapePath = parser.text();
}
}
}
@ -134,8 +134,8 @@ public class GeoShapeFilterParser implements FilterParser {
} else if (type == null) {
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
}
shape = fetchService.fetch(id, type, index, shapeFieldName);
} else {
shape = fetchService.fetch(id, type, index, shapePath);
} else {
throw new QueryParsingException(parseContext.index(), "[geo_shape] filter does not support [" + currentFieldName + "]");
}
}

View File

@ -42,7 +42,7 @@ public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQ
private final String indexedShapeType;
private String indexedShapeIndex;
private String indexedShapeFieldName;
private String indexedShapePath;
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
*/
public GeoShapeQueryBuilder indexedShapeFieldName(String indexedShapeFieldName) {
this.indexedShapeFieldName = indexedShapeFieldName;
public GeoShapeQueryBuilder indexedShapePath(String indexedShapePath) {
this.indexedShapePath = indexedShapePath;
return this;
}
@ -146,8 +146,8 @@ public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQ
if (indexedShapeIndex != null) {
builder.field("index", indexedShapeIndex);
}
if (indexedShapeFieldName != null) {
builder.field("shape_field_name", indexedShapeFieldName);
if (indexedShapePath != null) {
builder.field("path", indexedShapePath);
}
builder.endObject();
}

View File

@ -65,7 +65,7 @@ public class GeoShapeQueryParser implements QueryParser {
String id = null;
String type = null;
String index = DEFAULTS.INDEX_NAME;
String shapeFieldName = DEFAULTS.SHAPE_FIELD_NAME;
String shapePath = DEFAULTS.SHAPE_FIELD_NAME;
XContentParser.Token token;
String currentFieldName = null;
@ -102,8 +102,8 @@ public class GeoShapeQueryParser implements QueryParser {
type = parser.text();
} else if ("index".equals(currentFieldName)) {
index = parser.text();
} else if ("shape_field_name".equals(currentFieldName)) {
shapeFieldName = parser.text();
} else if ("path".equals(currentFieldName)) {
shapePath = parser.text();
}
}
}
@ -112,7 +112,7 @@ public class GeoShapeQueryParser implements QueryParser {
} else if (type == null) {
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 {
throw new QueryParsingException(parseContext.index(), "[geo_shape] query does not support [" + currentFieldName + "]");
}

View File

@ -24,6 +24,7 @@ import org.elasticsearch.ElasticSearchIllegalStateException;
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.component.AbstractComponent;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
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.
*
* @param id ID of the Shape to fetch
* @param type Index type 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 id ID of the Shape to fetch
* @param type Index type where the Shape is indexed
* @param index Index where the Shape is indexed
* @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
*/
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();
if (!response.isExists()) {
throw new ElasticSearchIllegalArgumentException("Shape with ID [" + id + "] in type [" + 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 (shapeField.equals(parser.currentName())) {
if (pathElements[currentPathSlot].equals(parser.currentName())) {
parser.nextToken();
return ShapeBuilder.parse(parser);
if (++currentPathSlot == pathElements.length) {
return ShapeBuilder.parse(parser);
}
} else {
parser.nextToken();
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 {
if (parser != null) {
parser.close();

View File

@ -21,15 +21,17 @@ package org.elasticsearch.search.geo;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.*;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
@ -245,13 +247,85 @@ public class GeoShapeIntegrationTests extends ElasticsearchIntegrationTest {
+ "\"id\": \"1\","
+ "\"type\": \"type1\","
+ "\"index\": \"test\","
+ "\"shape_field_name\": \"location2\""
+ "\"path\": \"location2\""
+ "}}}}";
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).execute().actionGet();
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
public void testThatShapeIsReturnedEvenWhenExclusionsAreSet() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")