Skip to content

Commit 26b078f

Browse files
committed
Add CONTAINS relation to geo_shape query
At the time of geo_shape query conception, CONTAINS was not yet a supported spatial operation in Lucene. Since it is now available this commit adds ShapeRelation.CONTAINS to GeoShapeQuery. Randomized testing is included and documentation is updated.
1 parent 635b0e9 commit 26b078f

File tree

4 files changed

+117
-50
lines changed

4 files changed

+117
-50
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class GeoShapeQueryBuilder extends QueryBuilder implements BoostableQuery
4848
private ShapeRelation relation = null;
4949

5050
private float boost = -1;
51-
51+
5252
/**
5353
* Creates a new GeoShapeQueryBuilder whose Filter will be against the
5454
* given field name using the given Shape

core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryParser.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ public static SpatialArgs getArgs(ShapeBuilder shape, ShapeRelation relation) {
188188
return new SpatialArgs(SpatialOperation.Intersects, shape.build());
189189
case WITHIN:
190190
return new SpatialArgs(SpatialOperation.IsWithin, shape.build());
191+
case CONTAINS:
192+
return new SpatialArgs(SpatialOperation.Contains, shape.build());
191193
default:
192194
throw new IllegalArgumentException("");
193195

core/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.elasticsearch.index.query.QueryBuilders;
3434
import org.elasticsearch.test.ESSingleNodeTestCase;
3535
import org.elasticsearch.test.geo.RandomShapeGenerator;
36-
import org.junit.Test;
3736

3837
import java.io.IOException;
3938
import java.util.Locale;
@@ -64,7 +63,6 @@ public void testNullShape() throws Exception {
6463
assertThat(result.getField("location"), nullValue());
6564
}
6665

67-
@Test
6866
public void testIndexPointsFilterRectangle() throws Exception {
6967
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")
7068
.startObject("properties").startObject("location")
@@ -112,9 +110,7 @@ public void testIndexPointsFilterRectangle() throws Exception {
112110
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
113111
}
114112

115-
@Test
116113
public void testEdgeCases() throws Exception {
117-
118114
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")
119115
.startObject("properties").startObject("location")
120116
.field("type", "geo_shape")
@@ -150,7 +146,6 @@ public void testEdgeCases() throws Exception {
150146
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("blakely"));
151147
}
152148

153-
@Test
154149
public void testIndexedShapeReference() throws Exception {
155150
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")
156151
.startObject("properties").startObject("location")
@@ -175,7 +170,8 @@ public void testIndexedShapeReference() throws Exception {
175170
.endObject()).setRefresh(true).execute().actionGet();
176171

177172
SearchResponse searchResponse = client().prepareSearch("test").setTypes("type1")
178-
.setQuery(geoIntersectionQuery("location", "Big_Rectangle", "shape_type")).execute().actionGet();
173+
.setQuery(geoIntersectionQuery("location", "Big_Rectangle", "shape_type"))
174+
.execute().actionGet();
179175

180176
assertSearchResponse(searchResponse);
181177
assertThat(searchResponse.getHits().getTotalHits(), equalTo(1l));
@@ -192,7 +188,6 @@ public void testIndexedShapeReference() throws Exception {
192188
assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1"));
193189
}
194190

195-
@Test
196191
public void testReusableBuilder() throws IOException {
197192
ShapeBuilder polygon = ShapeBuilder.newPolygon()
198193
.point(170, -10).point(190, -10).point(190, 10).point(170, 10)
@@ -212,44 +207,6 @@ private void assertUnmodified(ShapeBuilder builder) throws IOException {
212207
assertThat(before, equalTo(after));
213208
}
214209

215-
@Test
216-
public void testParsingMultipleShapes() throws Exception {
217-
String mapping = XContentFactory.jsonBuilder()
218-
.startObject()
219-
.startObject("type1")
220-
.startObject("properties")
221-
.startObject("location1")
222-
.field("type", "geo_shape")
223-
.endObject()
224-
.startObject("location2")
225-
.field("type", "geo_shape")
226-
.endObject()
227-
.endObject()
228-
.endObject()
229-
.endObject()
230-
.string();
231-
232-
client().admin().indices().prepareCreate("test").addMapping("type1", mapping).execute().actionGet();
233-
234-
String p1 = "\"location1\" : {\"type\":\"polygon\", \"coordinates\":[[[-10,-10],[10,-10],[10,10],[-10,10],[-10,-10]]]}";
235-
String p2 = "\"location2\" : {\"type\":\"polygon\", \"coordinates\":[[[-20,-20],[20,-20],[20,20],[-20,20],[-20,-20]]]}";
236-
String o1 = "{" + p1 + ", " + p2 + "}";
237-
238-
client().prepareIndex("test", "type1", "1").setSource(o1).setRefresh(true).execute().actionGet();
239-
240-
String filter = "{\"geo_shape\": {\"location2\": {\"indexed_shape\": {"
241-
+ "\"id\": \"1\","
242-
+ "\"type\": \"type1\","
243-
+ "\"index\": \"test\","
244-
+ "\"path\": \"location2\""
245-
+ "}}}}";
246-
247-
SearchResponse result = client().prepareSearch("test").setTypes("type1").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).execute().actionGet();
248-
assertSearchResponse(result);
249-
assertHitCount(result, 1);
250-
}
251-
252-
@Test
253210
public void testShapeFetchingPath() throws Exception {
254211
createIndex("shapes");
255212
client().admin().indices().prepareCreate("test").addMapping("type", "location", "type=geo_shape").execute().actionGet();
@@ -274,28 +231,28 @@ public void testShapeFetchingPath() throws Exception {
274231
.endArray().endArray()
275232
.endObject().endObject()).setRefresh(true).execute().actionGet();
276233

277-
GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS)
234+
GeoShapeQueryBuilder filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
278235
.indexedShapeIndex("shapes")
279236
.indexedShapePath("location");
280237
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
281238
.setPostFilter(filter).get();
282239
assertSearchResponse(result);
283240
assertHitCount(result, 1);
284-
filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS)
241+
filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
285242
.indexedShapeIndex("shapes")
286243
.indexedShapePath("1.location");
287244
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
288245
.setPostFilter(filter).get();
289246
assertSearchResponse(result);
290247
assertHitCount(result, 1);
291-
filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS)
248+
filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
292249
.indexedShapeIndex("shapes")
293250
.indexedShapePath("1.2.location");
294251
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
295252
.setPostFilter(filter).get();
296253
assertSearchResponse(result);
297254
assertHitCount(result, 1);
298-
filter = QueryBuilders.geoShapeQuery("location", "1", "type", ShapeRelation.INTERSECTS)
255+
filter = QueryBuilders.geoShapeQuery("location", "1", "type").relation(ShapeRelation.INTERSECTS)
299256
.indexedShapeIndex("shapes")
300257
.indexedShapePath("1.2.3.location");
301258
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.messy.tests;
20+
21+
import org.elasticsearch.cluster.ClusterState;
22+
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
23+
import org.elasticsearch.common.geo.builders.ShapeBuilder;
24+
import org.elasticsearch.common.xcontent.XContentFactory;
25+
import org.elasticsearch.index.IndexService;
26+
import org.elasticsearch.index.mapper.MappedFieldType;
27+
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper;
28+
import org.elasticsearch.indices.IndicesService;
29+
import org.elasticsearch.plugins.Plugin;
30+
import org.elasticsearch.script.groovy.GroovyPlugin;
31+
import org.elasticsearch.test.ESIntegTestCase;
32+
33+
import java.util.Collection;
34+
import java.util.Collections;
35+
36+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
37+
import static org.hamcrest.Matchers.equalTo;
38+
import static org.hamcrest.Matchers.instanceOf;
39+
40+
/**
41+
*/
42+
public class GeoShapeIntegrationTests extends ESIntegTestCase {
43+
44+
@Override
45+
protected Collection<Class<? extends Plugin>> nodePlugins() {
46+
return Collections.singleton(GroovyPlugin.class);
47+
}
48+
49+
/**
50+
* Test that orientation parameter correctly persists across cluster restart
51+
*/
52+
public void testOrientationPersistence() throws Exception {
53+
String idxName = "orientation";
54+
String mapping = XContentFactory.jsonBuilder().startObject().startObject("shape")
55+
.startObject("properties").startObject("location")
56+
.field("type", "geo_shape")
57+
.field("orientation", "left")
58+
.endObject().endObject()
59+
.endObject().endObject().string();
60+
61+
// create index
62+
assertAcked(prepareCreate(idxName).addMapping("shape", mapping));
63+
64+
mapping = XContentFactory.jsonBuilder().startObject().startObject("shape")
65+
.startObject("properties").startObject("location")
66+
.field("type", "geo_shape")
67+
.field("orientation", "right")
68+
.endObject().endObject()
69+
.endObject().endObject().string();
70+
71+
assertAcked(prepareCreate(idxName+"2").addMapping("shape", mapping));
72+
ensureGreen(idxName, idxName+"2");
73+
74+
internalCluster().fullRestart();
75+
ensureGreen(idxName, idxName+"2");
76+
77+
// left orientation test
78+
IndicesService indicesService = internalCluster().getInstance(IndicesService.class, findNodeName(idxName));
79+
IndexService indexService = indicesService.indexService(idxName);
80+
MappedFieldType fieldType = indexService.mapperService().smartNameFieldType("location");
81+
assertThat(fieldType, instanceOf(GeoShapeFieldMapper.GeoShapeFieldType.class));
82+
83+
GeoShapeFieldMapper.GeoShapeFieldType gsfm = (GeoShapeFieldMapper.GeoShapeFieldType)fieldType;
84+
ShapeBuilder.Orientation orientation = gsfm.orientation();
85+
assertThat(orientation, equalTo(ShapeBuilder.Orientation.CLOCKWISE));
86+
assertThat(orientation, equalTo(ShapeBuilder.Orientation.LEFT));
87+
assertThat(orientation, equalTo(ShapeBuilder.Orientation.CW));
88+
89+
// right orientation test
90+
indicesService = internalCluster().getInstance(IndicesService.class, findNodeName(idxName+"2"));
91+
indexService = indicesService.indexService(idxName+"2");
92+
fieldType = indexService.mapperService().smartNameFieldType("location");
93+
assertThat(fieldType, instanceOf(GeoShapeFieldMapper.GeoShapeFieldType.class));
94+
95+
gsfm = (GeoShapeFieldMapper.GeoShapeFieldType)fieldType;
96+
orientation = gsfm.orientation();
97+
assertThat(orientation, equalTo(ShapeBuilder.Orientation.COUNTER_CLOCKWISE));
98+
assertThat(orientation, equalTo(ShapeBuilder.Orientation.RIGHT));
99+
assertThat(orientation, equalTo(ShapeBuilder.Orientation.CCW));
100+
}
101+
102+
private String findNodeName(String index) {
103+
ClusterState state = client().admin().cluster().prepareState().get().getState();
104+
IndexShardRoutingTable shard = state.getRoutingTable().index(index).shard(0);
105+
String nodeId = shard.assignedShards().get(0).currentNodeId();
106+
return state.getNodes().get(nodeId).name();
107+
}
108+
}

0 commit comments

Comments
 (0)