Add support for indexed shape routing in geo_shape query (#30760)
Adds ability to specify the routing value for the indexed shape in the geo_shape query. Closes #7663
This commit is contained in:
parent
a1b538122c
commit
4b6915976c
|
@ -93,6 +93,7 @@ to 'shapes'.
|
||||||
* `type` - Index type where the pre-indexed shape is.
|
* `type` - Index type where the pre-indexed shape is.
|
||||||
* `path` - The field specified as path containing the pre-indexed shape.
|
* `path` - The field specified as path containing the pre-indexed shape.
|
||||||
Defaults to 'shape'.
|
Defaults to 'shape'.
|
||||||
|
* `routing` - The routing of the shape document if required.
|
||||||
|
|
||||||
The following is an example of using the Filter with a pre-indexed
|
The following is an example of using the Filter with a pre-indexed
|
||||||
shape:
|
shape:
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||||
import org.apache.lucene.util.SetOnce;
|
import org.apache.lucene.util.SetOnce;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.get.GetRequest;
|
import org.elasticsearch.action.get.GetRequest;
|
||||||
import org.elasticsearch.action.get.GetResponse;
|
import org.elasticsearch.action.get.GetResponse;
|
||||||
|
@ -77,6 +78,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
private static final ParseField SHAPE_TYPE_FIELD = new ParseField("type");
|
private static final ParseField SHAPE_TYPE_FIELD = new ParseField("type");
|
||||||
private static final ParseField SHAPE_INDEX_FIELD = new ParseField("index");
|
private static final ParseField SHAPE_INDEX_FIELD = new ParseField("index");
|
||||||
private static final ParseField SHAPE_PATH_FIELD = new ParseField("path");
|
private static final ParseField SHAPE_PATH_FIELD = new ParseField("path");
|
||||||
|
private static final ParseField SHAPE_ROUTING_FIELD = new ParseField("routing");
|
||||||
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
|
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
|
||||||
|
|
||||||
private final String fieldName;
|
private final String fieldName;
|
||||||
|
@ -89,8 +91,10 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
private final String indexedShapeId;
|
private final String indexedShapeId;
|
||||||
private final String indexedShapeType;
|
private final String indexedShapeType;
|
||||||
|
|
||||||
|
|
||||||
private String indexedShapeIndex = DEFAULT_SHAPE_INDEX_NAME;
|
private String indexedShapeIndex = DEFAULT_SHAPE_INDEX_NAME;
|
||||||
private String indexedShapePath = DEFAULT_SHAPE_FIELD_NAME;
|
private String indexedShapePath = DEFAULT_SHAPE_FIELD_NAME;
|
||||||
|
private String indexedShapeRouting;
|
||||||
|
|
||||||
private ShapeRelation relation = DEFAULT_SHAPE_RELATION;
|
private ShapeRelation relation = DEFAULT_SHAPE_RELATION;
|
||||||
|
|
||||||
|
@ -166,6 +170,11 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
indexedShapeType = in.readOptionalString();
|
indexedShapeType = in.readOptionalString();
|
||||||
indexedShapeIndex = in.readOptionalString();
|
indexedShapeIndex = in.readOptionalString();
|
||||||
indexedShapePath = in.readOptionalString();
|
indexedShapePath = in.readOptionalString();
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
indexedShapeRouting = in.readOptionalString();
|
||||||
|
} else {
|
||||||
|
indexedShapeRouting = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
relation = ShapeRelation.readFromStream(in);
|
relation = ShapeRelation.readFromStream(in);
|
||||||
strategy = in.readOptionalWriteable(SpatialStrategy::readFromStream);
|
strategy = in.readOptionalWriteable(SpatialStrategy::readFromStream);
|
||||||
|
@ -188,6 +197,11 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
out.writeOptionalString(indexedShapeType);
|
out.writeOptionalString(indexedShapeType);
|
||||||
out.writeOptionalString(indexedShapeIndex);
|
out.writeOptionalString(indexedShapeIndex);
|
||||||
out.writeOptionalString(indexedShapePath);
|
out.writeOptionalString(indexedShapePath);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeOptionalString(indexedShapeRouting);
|
||||||
|
} else if (indexedShapeRouting != null) {
|
||||||
|
throw new IllegalStateException("indexed shape routing cannot be serialized to older nodes");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
relation.writeTo(out);
|
relation.writeTo(out);
|
||||||
out.writeOptionalWriteable(strategy);
|
out.writeOptionalWriteable(strategy);
|
||||||
|
@ -285,6 +299,26 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
return indexedShapePath;
|
return indexedShapePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the optional routing to the indexed Shape that will be used in the query
|
||||||
|
*
|
||||||
|
* @param indexedShapeRouting indexed shape routing
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GeoShapeQueryBuilder indexedShapeRouting(String indexedShapeRouting) {
|
||||||
|
this.indexedShapeRouting = indexedShapeRouting;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the optional routing to the indexed Shape that will be used in the
|
||||||
|
* Query
|
||||||
|
*/
|
||||||
|
public String indexedShapeRouting() {
|
||||||
|
return indexedShapeRouting;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the relation of query shape and indexed shape.
|
* Sets the relation of query shape and indexed shape.
|
||||||
*
|
*
|
||||||
|
@ -473,6 +507,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
if (indexedShapePath != null) {
|
if (indexedShapePath != null) {
|
||||||
builder.field(SHAPE_PATH_FIELD.getPreferredName(), indexedShapePath);
|
builder.field(SHAPE_PATH_FIELD.getPreferredName(), indexedShapePath);
|
||||||
}
|
}
|
||||||
|
if (indexedShapeRouting != null) {
|
||||||
|
builder.field(SHAPE_ROUTING_FIELD.getPreferredName(), indexedShapeRouting);
|
||||||
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,6 +535,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
String type = null;
|
String type = null;
|
||||||
String index = null;
|
String index = null;
|
||||||
String shapePath = null;
|
String shapePath = null;
|
||||||
|
String shapeRouting = null;
|
||||||
|
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
|
@ -544,6 +582,8 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
index = parser.text();
|
index = parser.text();
|
||||||
} else if (SHAPE_PATH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
} else if (SHAPE_PATH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||||
shapePath = parser.text();
|
shapePath = parser.text();
|
||||||
|
} else if (SHAPE_ROUTING_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||||
|
shapeRouting = parser.text();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ParsingException(parser.getTokenLocation(), "[" + GeoShapeQueryBuilder.NAME +
|
throw new ParsingException(parser.getTokenLocation(), "[" + GeoShapeQueryBuilder.NAME +
|
||||||
|
@ -581,6 +621,9 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
if (shapePath != null) {
|
if (shapePath != null) {
|
||||||
builder.indexedShapePath(shapePath);
|
builder.indexedShapePath(shapePath);
|
||||||
}
|
}
|
||||||
|
if (shapeRouting != null) {
|
||||||
|
builder.indexedShapeRouting(shapeRouting);
|
||||||
|
}
|
||||||
if (shapeRelation != null) {
|
if (shapeRelation != null) {
|
||||||
builder.relation(shapeRelation);
|
builder.relation(shapeRelation);
|
||||||
}
|
}
|
||||||
|
@ -602,6 +645,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
&& Objects.equals(indexedShapeIndex, other.indexedShapeIndex)
|
&& Objects.equals(indexedShapeIndex, other.indexedShapeIndex)
|
||||||
&& Objects.equals(indexedShapePath, other.indexedShapePath)
|
&& Objects.equals(indexedShapePath, other.indexedShapePath)
|
||||||
&& Objects.equals(indexedShapeType, other.indexedShapeType)
|
&& Objects.equals(indexedShapeType, other.indexedShapeType)
|
||||||
|
&& Objects.equals(indexedShapeRouting, other.indexedShapeRouting)
|
||||||
&& Objects.equals(relation, other.relation)
|
&& Objects.equals(relation, other.relation)
|
||||||
&& Objects.equals(shape, other.shape)
|
&& Objects.equals(shape, other.shape)
|
||||||
&& Objects.equals(supplier, other.supplier)
|
&& Objects.equals(supplier, other.supplier)
|
||||||
|
@ -612,7 +656,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
@Override
|
@Override
|
||||||
protected int doHashCode() {
|
protected int doHashCode() {
|
||||||
return Objects.hash(fieldName, indexedShapeId, indexedShapeIndex,
|
return Objects.hash(fieldName, indexedShapeId, indexedShapeIndex,
|
||||||
indexedShapePath, indexedShapeType, relation, shape, strategy, ignoreUnmapped, supplier);
|
indexedShapePath, indexedShapeType, indexedShapeRouting, relation, shape, strategy, ignoreUnmapped, supplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -629,6 +673,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
||||||
SetOnce<ShapeBuilder> supplier = new SetOnce<>();
|
SetOnce<ShapeBuilder> supplier = new SetOnce<>();
|
||||||
queryRewriteContext.registerAsyncAction((client, listener) -> {
|
queryRewriteContext.registerAsyncAction((client, listener) -> {
|
||||||
GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
|
GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
|
||||||
|
getRequest.routing(indexedShapeRouting);
|
||||||
fetch(client, getRequest, indexedShapePath, ActionListener.wrap(builder-> {
|
fetch(client, getRequest, indexedShapePath, ActionListener.wrap(builder-> {
|
||||||
supplier.set(builder);
|
supplier.set(builder);
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
|
|
|
@ -59,6 +59,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
||||||
private static String indexedShapeType;
|
private static String indexedShapeType;
|
||||||
private static String indexedShapePath;
|
private static String indexedShapePath;
|
||||||
private static String indexedShapeIndex;
|
private static String indexedShapeIndex;
|
||||||
|
private static String indexedShapeRouting;
|
||||||
private static ShapeBuilder indexedShapeToReturn;
|
private static ShapeBuilder indexedShapeToReturn;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,6 +86,10 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
||||||
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
|
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
|
||||||
builder.indexedShapePath(indexedShapePath);
|
builder.indexedShapePath(indexedShapePath);
|
||||||
}
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
|
||||||
|
builder.indexedShapeRouting(indexedShapeRouting);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
|
SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
|
||||||
|
@ -112,6 +117,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
||||||
assertThat(indexedShapeType, notNullValue());
|
assertThat(indexedShapeType, notNullValue());
|
||||||
assertThat(getRequest.id(), equalTo(indexedShapeId));
|
assertThat(getRequest.id(), equalTo(indexedShapeId));
|
||||||
assertThat(getRequest.type(), equalTo(indexedShapeType));
|
assertThat(getRequest.type(), equalTo(indexedShapeType));
|
||||||
|
assertThat(getRequest.routing(), equalTo(indexedShapeRouting));
|
||||||
String expectedShapeIndex = indexedShapeIndex == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_INDEX_NAME : indexedShapeIndex;
|
String expectedShapeIndex = indexedShapeIndex == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_INDEX_NAME : indexedShapeIndex;
|
||||||
assertThat(getRequest.index(), equalTo(expectedShapeIndex));
|
assertThat(getRequest.index(), equalTo(expectedShapeIndex));
|
||||||
String expectedShapePath = indexedShapePath == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_FIELD_NAME : indexedShapePath;
|
String expectedShapePath = indexedShapePath == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_FIELD_NAME : indexedShapePath;
|
||||||
|
@ -136,6 +142,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
|
||||||
indexedShapeType = null;
|
indexedShapeType = null;
|
||||||
indexedShapePath = null;
|
indexedShapePath = null;
|
||||||
indexedShapeIndex = null;
|
indexedShapeIndex = null;
|
||||||
|
indexedShapeRouting = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.indices.IndicesService;
|
import org.elasticsearch.indices.IndicesService;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
|
|
||||||
|
import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
|
||||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
@ -121,6 +122,43 @@ public class GeoShapeIntegrationIT extends ESIntegTestCase {
|
||||||
assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L));
|
assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that the indexed shape routing can be provided if it is required
|
||||||
|
*/
|
||||||
|
public void testIndexShapeRouting() throws Exception {
|
||||||
|
String mapping = "{\n" +
|
||||||
|
" \"_routing\": {\n" +
|
||||||
|
" \"required\": true\n" +
|
||||||
|
" },\n" +
|
||||||
|
" \"properties\": {\n" +
|
||||||
|
" \"shape\": {\n" +
|
||||||
|
" \"type\": \"geo_shape\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }";
|
||||||
|
|
||||||
|
|
||||||
|
// create index
|
||||||
|
assertAcked(client().admin().indices().prepareCreate("test").addMapping("doc", mapping, XContentType.JSON).get());
|
||||||
|
ensureGreen();
|
||||||
|
|
||||||
|
String source = "{\n" +
|
||||||
|
" \"shape\" : {\n" +
|
||||||
|
" \"type\" : \"circle\",\n" +
|
||||||
|
" \"coordinates\" : [-45.0, 45.0],\n" +
|
||||||
|
" \"radius\" : \"100m\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
indexRandom(true, client().prepareIndex("test", "doc", "0").setSource(source, XContentType.JSON).setRouting("ABC"));
|
||||||
|
|
||||||
|
SearchResponse searchResponse = client().prepareSearch("test").setQuery(
|
||||||
|
geoShapeQuery("shape", "0", "doc").indexedShapeIndex("test").indexedShapeRouting("ABC")
|
||||||
|
).get();
|
||||||
|
|
||||||
|
assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L));
|
||||||
|
}
|
||||||
|
|
||||||
private String findNodeName(String index) {
|
private String findNodeName(String index) {
|
||||||
ClusterState state = client().admin().cluster().prepareState().get().getState();
|
ClusterState state = client().admin().cluster().prepareState().get().getState();
|
||||||
IndexShardRoutingTable shard = state.getRoutingTable().index(index).shard(0);
|
IndexShardRoutingTable shard = state.getRoutingTable().index(index).shard(0);
|
||||||
|
|
Loading…
Reference in New Issue