Skip to content

Commit 89aa7bd

Browse files
committed
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
1 parent 5aee050 commit 89aa7bd

File tree

4 files changed

+92
-1
lines changed

4 files changed

+92
-1
lines changed

docs/reference/query-dsl/geo-shape-query.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ to 'shapes'.
9393
* `type` - Index type where the pre-indexed shape is.
9494
* `path` - The field specified as path containing the pre-indexed shape.
9595
Defaults to 'shape'.
96+
* `routing` - The routing of the shape document if required.
9697

9798
The following is an example of using the Filter with a pre-indexed
9899
shape:

server/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.lucene.spatial.query.SpatialArgs;
3030
import org.apache.lucene.spatial.query.SpatialOperation;
3131
import org.apache.lucene.util.SetOnce;
32+
import org.elasticsearch.Version;
3233
import org.elasticsearch.action.ActionListener;
3334
import org.elasticsearch.action.get.GetRequest;
3435
import org.elasticsearch.action.get.GetResponse;
@@ -77,6 +78,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
7778
private static final ParseField SHAPE_TYPE_FIELD = new ParseField("type");
7879
private static final ParseField SHAPE_INDEX_FIELD = new ParseField("index");
7980
private static final ParseField SHAPE_PATH_FIELD = new ParseField("path");
81+
private static final ParseField SHAPE_ROUTING_FIELD = new ParseField("routing");
8082
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
8183

8284
private final String fieldName;
@@ -89,8 +91,10 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
8991
private final String indexedShapeId;
9092
private final String indexedShapeType;
9193

94+
9295
private String indexedShapeIndex = DEFAULT_SHAPE_INDEX_NAME;
9396
private String indexedShapePath = DEFAULT_SHAPE_FIELD_NAME;
97+
private String indexedShapeRouting;
9498

9599
private ShapeRelation relation = DEFAULT_SHAPE_RELATION;
96100

@@ -166,6 +170,11 @@ public GeoShapeQueryBuilder(StreamInput in) throws IOException {
166170
indexedShapeType = in.readOptionalString();
167171
indexedShapeIndex = in.readOptionalString();
168172
indexedShapePath = in.readOptionalString();
173+
if (in.getVersion().onOrAfter(Version.V_6_4_0)) {
174+
indexedShapeRouting = in.readOptionalString();
175+
} else {
176+
indexedShapeRouting = null;
177+
}
169178
}
170179
relation = ShapeRelation.readFromStream(in);
171180
strategy = in.readOptionalWriteable(SpatialStrategy::readFromStream);
@@ -188,6 +197,11 @@ protected void doWriteTo(StreamOutput out) throws IOException {
188197
out.writeOptionalString(indexedShapeType);
189198
out.writeOptionalString(indexedShapeIndex);
190199
out.writeOptionalString(indexedShapePath);
200+
if (out.getVersion().onOrAfter(Version.V_6_4_0)) {
201+
out.writeOptionalString(indexedShapeRouting);
202+
} else if (indexedShapeRouting != null) {
203+
throw new IllegalStateException("indexed shape routing cannot be serialized to older nodes");
204+
}
191205
}
192206
relation.writeTo(out);
193207
out.writeOptionalWriteable(strategy);
@@ -285,6 +299,26 @@ public String indexedShapePath() {
285299
return indexedShapePath;
286300
}
287301

302+
/**
303+
* Sets the optional routing to the indexed Shape that will be used in the query
304+
*
305+
* @param indexedShapeRouting indexed shape routing
306+
* @return this
307+
*/
308+
public GeoShapeQueryBuilder indexedShapeRouting(String indexedShapeRouting) {
309+
this.indexedShapeRouting = indexedShapeRouting;
310+
return this;
311+
}
312+
313+
314+
/**
315+
* @return the optional routing to the indexed Shape that will be used in the
316+
* Query
317+
*/
318+
public String indexedShapeRouting() {
319+
return indexedShapeRouting;
320+
}
321+
288322
/**
289323
* Sets the relation of query shape and indexed shape.
290324
*
@@ -473,6 +507,9 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
473507
if (indexedShapePath != null) {
474508
builder.field(SHAPE_PATH_FIELD.getPreferredName(), indexedShapePath);
475509
}
510+
if (indexedShapeRouting != null) {
511+
builder.field(SHAPE_ROUTING_FIELD.getPreferredName(), indexedShapeRouting);
512+
}
476513
builder.endObject();
477514
}
478515

@@ -498,6 +535,7 @@ public static GeoShapeQueryBuilder fromXContent(XContentParser parser) throws IO
498535
String type = null;
499536
String index = null;
500537
String shapePath = null;
538+
String shapeRouting = null;
501539

502540
XContentParser.Token token;
503541
String currentFieldName = null;
@@ -544,6 +582,8 @@ public static GeoShapeQueryBuilder fromXContent(XContentParser parser) throws IO
544582
index = parser.text();
545583
} else if (SHAPE_PATH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
546584
shapePath = parser.text();
585+
} else if (SHAPE_ROUTING_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
586+
shapeRouting = parser.text();
547587
}
548588
} else {
549589
throw new ParsingException(parser.getTokenLocation(), "[" + GeoShapeQueryBuilder.NAME +
@@ -581,6 +621,9 @@ public static GeoShapeQueryBuilder fromXContent(XContentParser parser) throws IO
581621
if (shapePath != null) {
582622
builder.indexedShapePath(shapePath);
583623
}
624+
if (shapeRouting != null) {
625+
builder.indexedShapeRouting(shapeRouting);
626+
}
584627
if (shapeRelation != null) {
585628
builder.relation(shapeRelation);
586629
}
@@ -602,6 +645,7 @@ protected boolean doEquals(GeoShapeQueryBuilder other) {
602645
&& Objects.equals(indexedShapeIndex, other.indexedShapeIndex)
603646
&& Objects.equals(indexedShapePath, other.indexedShapePath)
604647
&& Objects.equals(indexedShapeType, other.indexedShapeType)
648+
&& Objects.equals(indexedShapeRouting, other.indexedShapeRouting)
605649
&& Objects.equals(relation, other.relation)
606650
&& Objects.equals(shape, other.shape)
607651
&& Objects.equals(supplier, other.supplier)
@@ -612,7 +656,7 @@ protected boolean doEquals(GeoShapeQueryBuilder other) {
612656
@Override
613657
protected int doHashCode() {
614658
return Objects.hash(fieldName, indexedShapeId, indexedShapeIndex,
615-
indexedShapePath, indexedShapeType, relation, shape, strategy, ignoreUnmapped, supplier);
659+
indexedShapePath, indexedShapeType, indexedShapeRouting, relation, shape, strategy, ignoreUnmapped, supplier);
616660
}
617661

618662
@Override
@@ -629,6 +673,7 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws
629673
SetOnce<ShapeBuilder> supplier = new SetOnce<>();
630674
queryRewriteContext.registerAsyncAction((client, listener) -> {
631675
GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
676+
getRequest.routing(indexedShapeRouting);
632677
fetch(client, getRequest, indexedShapePath, ActionListener.wrap(builder-> {
633678
supplier.set(builder);
634679
listener.onResponse(null);

server/src/test/java/org/elasticsearch/index/query/GeoShapeQueryBuilderTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
5959
private static String indexedShapeType;
6060
private static String indexedShapePath;
6161
private static String indexedShapeIndex;
62+
private static String indexedShapeRouting;
6263
private static ShapeBuilder indexedShapeToReturn;
6364

6465
@Override
@@ -85,6 +86,10 @@ private GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
8586
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
8687
builder.indexedShapePath(indexedShapePath);
8788
}
89+
if (randomBoolean()) {
90+
indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
91+
builder.indexedShapeRouting(indexedShapeRouting);
92+
}
8893
}
8994
if (randomBoolean()) {
9095
SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
@@ -112,6 +117,7 @@ protected GetResponse executeGet(GetRequest getRequest) {
112117
assertThat(indexedShapeType, notNullValue());
113118
assertThat(getRequest.id(), equalTo(indexedShapeId));
114119
assertThat(getRequest.type(), equalTo(indexedShapeType));
120+
assertThat(getRequest.routing(), equalTo(indexedShapeRouting));
115121
String expectedShapeIndex = indexedShapeIndex == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_INDEX_NAME : indexedShapeIndex;
116122
assertThat(getRequest.index(), equalTo(expectedShapeIndex));
117123
String expectedShapePath = indexedShapePath == null ? GeoShapeQueryBuilder.DEFAULT_SHAPE_FIELD_NAME : indexedShapePath;
@@ -136,6 +142,7 @@ public void clearShapeFields() {
136142
indexedShapeType = null;
137143
indexedShapePath = null;
138144
indexedShapeIndex = null;
145+
indexedShapeRouting = null;
139146
}
140147

141148
@Override

server/src/test/java/org/elasticsearch/search/geo/GeoShapeIntegrationIT.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.indices.IndicesService;
3232
import org.elasticsearch.test.ESIntegTestCase;
3333

34+
import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
3435
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
3536
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
3637
import static org.hamcrest.Matchers.equalTo;
@@ -121,6 +122,43 @@ public void testIgnoreMalformed() throws Exception {
121122
assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L));
122123
}
123124

125+
/**
126+
* Test that the indexed shape routing can be provided if it is required
127+
*/
128+
public void testIndexShapeRouting() throws Exception {
129+
String mapping = "{\n" +
130+
" \"_routing\": {\n" +
131+
" \"required\": true\n" +
132+
" },\n" +
133+
" \"properties\": {\n" +
134+
" \"shape\": {\n" +
135+
" \"type\": \"geo_shape\"\n" +
136+
" }\n" +
137+
" }\n" +
138+
" }";
139+
140+
141+
// create index
142+
assertAcked(client().admin().indices().prepareCreate("test").addMapping("doc", mapping, XContentType.JSON).get());
143+
ensureGreen();
144+
145+
String source = "{\n" +
146+
" \"shape\" : {\n" +
147+
" \"type\" : \"circle\",\n" +
148+
" \"coordinates\" : [-45.0, 45.0],\n" +
149+
" \"radius\" : \"100m\"\n" +
150+
" }\n" +
151+
"}";
152+
153+
indexRandom(true, client().prepareIndex("test", "doc", "0").setSource(source, XContentType.JSON).setRouting("ABC"));
154+
155+
SearchResponse searchResponse = client().prepareSearch("test").setQuery(
156+
geoShapeQuery("shape", "0", "doc").indexedShapeIndex("test").indexedShapeRouting("ABC")
157+
).get();
158+
159+
assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L));
160+
}
161+
124162
private String findNodeName(String index) {
125163
ClusterState state = client().admin().cluster().prepareState().get().getState();
126164
IndexShardRoutingTable shard = state.getRoutingTable().index(index).shard(0);

0 commit comments

Comments
 (0)