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; 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();
} }

View File

@ -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,7 +134,7 @@ 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 + "]");
} }

View File

@ -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();
} }

View File

@ -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 + "]");
} }

View File

@ -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;
@ -52,32 +53,37 @@ public class ShapeFetchService extends AbstractComponent {
* @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();
if (++currentPathSlot == pathElements.length) {
return ShapeBuilder.parse(parser); 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();

View File

@ -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")