Skip to content

Commit 05e0b4d

Browse files
Chris Malekimchy
authored andcommitted
Added ShapeFetchService with support in GeoShapeQueryParser/FilterParser
1 parent df0ff17 commit 05e0b4d

File tree

10 files changed

+413
-13
lines changed

10 files changed

+413
-13
lines changed

src/main/java/org/elasticsearch/index/query/FilterBuilders.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,10 @@ public static GeoShapeFilterBuilder geoShapeFilter(String name, Shape shape) {
349349
return new GeoShapeFilterBuilder(name, shape);
350350
}
351351

352+
public static GeoShapeFilterBuilder geoShapeFilter(String name, String indexedShapeId, String indexedShapeType) {
353+
return new GeoShapeFilterBuilder(name, indexedShapeId, indexedShapeType);
354+
}
355+
352356
/**
353357
* A filter to filter only documents where a field exists in them.
354358
*

src/main/java/org/elasticsearch/index/query/GeoShapeFilterBuilder.java

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,40 @@ public class GeoShapeFilterBuilder extends BaseFilterBuilder {
2323

2424
private String filterName;
2525

26+
private final String indexedShapeId;
27+
private final String indexedShapeType;
28+
29+
private String indexedShapeIndex;
30+
private String indexedShapeFieldName;
31+
2632
/**
2733
* Creates a new GeoShapeFilterBuilder whose Filter will be against the
28-
* given field name
34+
* given field name using the given Shape
2935
*
3036
* @param name Name of the field that will be filtered
3137
* @param shape Shape used in the filter
3238
*/
3339
public GeoShapeFilterBuilder(String name, Shape shape) {
40+
this(name, shape, null, null);
41+
}
42+
43+
/**
44+
* Creates a new GeoShapeFilterBuilder whose Filter will be against the given field name
45+
* and will use the Shape found with the given ID in the given type
46+
*
47+
* @param name Name of the field that will be filtered
48+
* @param indexedShapeId ID of the indexed Shape that will be used in the Filter
49+
* @param indexedShapeType Index type of the indexed Shapes
50+
*/
51+
public GeoShapeFilterBuilder(String name, String indexedShapeId, String indexedShapeType) {
52+
this(name, null, indexedShapeId, indexedShapeType);
53+
}
54+
55+
private GeoShapeFilterBuilder(String name, Shape shape, String indexedShapeId, String indexedShapeType) {
3456
this.name = name;
3557
this.shape = shape;
58+
this.indexedShapeId = indexedShapeId;
59+
this.indexedShapeType = indexedShapeType;
3660
}
3761

3862
/**
@@ -80,16 +104,51 @@ public GeoShapeFilterBuilder filterName(String filterName) {
80104
return this;
81105
}
82106

107+
/**
108+
* Sets the name of the index where the indexed Shape can be found
109+
*
110+
* @param indexedShapeIndex Name of the index where the indexed Shape is
111+
* @return this
112+
*/
113+
public GeoShapeFilterBuilder indexedShapeIndex(String indexedShapeIndex) {
114+
this.indexedShapeIndex = indexedShapeIndex;
115+
return this;
116+
}
117+
118+
/**
119+
* Sets the name of the field in the indexed Shape document that has the Shape itself
120+
*
121+
* @param indexedShapeFieldName Name of the field where the Shape itself is defined
122+
* @return this
123+
*/
124+
public GeoShapeFilterBuilder indexedShapeFieldName(String indexedShapeFieldName) {
125+
this.indexedShapeFieldName = indexedShapeFieldName;
126+
return this;
127+
}
128+
83129
@Override
84130
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
85131
builder.startObject(GeoShapeFilterParser.NAME);
86132

87133
builder.startObject(name);
88134
builder.field("relation", relation.getRelationName());
89135

90-
builder.startObject("shape");
91-
GeoJSONShapeSerializer.serialize(shape, builder);
92-
builder.endObject();
136+
if (shape != null) {
137+
builder.startObject("shape");
138+
GeoJSONShapeSerializer.serialize(shape, builder);
139+
builder.endObject();
140+
} else {
141+
builder.startObject("indexed_shape")
142+
.field("id", indexedShapeId)
143+
.field("type", indexedShapeType);
144+
if (indexedShapeIndex != null) {
145+
builder.field("index", indexedShapeIndex);
146+
}
147+
if (indexedShapeFieldName != null) {
148+
builder.field("shape_field_name", indexedShapeFieldName);
149+
}
150+
builder.endObject();
151+
}
93152

94153
builder.endObject();
95154

src/main/java/org/elasticsearch/index/query/GeoShapeFilterParser.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
import org.apache.lucene.search.Filter;
55
import org.elasticsearch.common.geo.ShapeRelation;
66
import org.elasticsearch.common.geo.GeoJSONShapeParser;
7+
import org.elasticsearch.common.inject.Inject;
8+
import org.elasticsearch.common.inject.internal.Nullable;
79
import org.elasticsearch.common.xcontent.XContentParser;
810
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
911
import org.elasticsearch.index.mapper.FieldMapper;
1012
import org.elasticsearch.index.mapper.MapperService;
1113
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper;
14+
import org.elasticsearch.index.search.shape.ShapeFetchService;
1215

1316
import java.io.IOException;
1417

@@ -36,6 +39,13 @@ public class GeoShapeFilterParser implements FilterParser {
3639

3740
public static final String NAME = "geo_shape";
3841

42+
private ShapeFetchService fetchService;
43+
44+
public static class DEFAULTS {
45+
public static final String INDEX_NAME = "shapes";
46+
public static final String SHAPE_FIELD_NAME = "shape";
47+
}
48+
3949
@Override
4050
public String[] names() {
4151
return new String[]{NAME, "geoShape"};
@@ -52,6 +62,11 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
5262
CacheKeyFilter.Key cacheKey = null;
5363
String filterName = null;
5464

65+
String id = null;
66+
String type = null;
67+
String index = DEFAULTS.INDEX_NAME;
68+
String shapeFieldName = DEFAULTS.SHAPE_FIELD_NAME;
69+
5570
XContentParser.Token token;
5671
String currentFieldName = null;
5772

@@ -73,6 +88,28 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
7388
if (shapeRelation == null) {
7489
throw new QueryParsingException(parseContext.index(), "Unknown shape operation [" + parser.text() + " ]");
7590
}
91+
} else if ("indexed_shape".equals(currentFieldName)) {
92+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
93+
if (token == XContentParser.Token.FIELD_NAME) {
94+
currentFieldName = parser.currentName();
95+
} else if (token.isValue()) {
96+
if ("id".equals(currentFieldName)) {
97+
id = parser.text();
98+
} else if ("type".equals(currentFieldName)) {
99+
type = parser.text();
100+
} else if ("index".equals(currentFieldName)) {
101+
index = parser.text();
102+
} else if ("shape_field_name".equals(currentFieldName)) {
103+
shapeFieldName = parser.text();
104+
}
105+
}
106+
}
107+
if (id == null) {
108+
throw new QueryParsingException(parseContext.index(), "ID for indexed shape not provided");
109+
} else if (type == null) {
110+
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
111+
}
112+
shape = fetchService.fetch(id, type, index, shapeFieldName);
76113
}
77114
}
78115
}
@@ -119,4 +156,9 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
119156

120157
return filter;
121158
}
159+
160+
@Inject(optional = true)
161+
public void setFetchService(@Nullable ShapeFetchService fetchService) {
162+
this.fetchService = fetchService;
163+
}
122164
}

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

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
import java.io.IOException;
99

10+
/**
11+
* {@link QueryBuilder} that builds a GeoShape Query
12+
*/
1013
public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<GeoShapeQueryBuilder> {
1114

1215
private final String name;
@@ -16,32 +19,107 @@ public class GeoShapeQueryBuilder extends BaseQueryBuilder implements BoostableQ
1619

1720
private float boost = -1;
1821

22+
private final String indexedShapeId;
23+
private final String indexedShapeType;
24+
25+
private String indexedShapeIndex;
26+
private String indexedShapeFieldName;
27+
28+
/**
29+
* Creates a new GeoShapeQueryBuilder whose Query will be against the
30+
* given field name using the given Shape
31+
*
32+
* @param name Name of the field that will be queried
33+
* @param shape Shape used in the query
34+
*/
1935
public GeoShapeQueryBuilder(String name, Shape shape) {
36+
this(name, shape, null, null);
37+
}
38+
/**
39+
* Creates a new GeoShapeQueryBuilder whose Query will be against the given field name
40+
* and will use the Shape found with the given ID in the given type
41+
*
42+
* @param name Name of the field that will be queried
43+
* @param indexedShapeId ID of the indexed Shape that will be used in the Query
44+
* @param indexedShapeType Index type of the indexed Shapes
45+
*/
46+
public GeoShapeQueryBuilder(String name, String indexedShapeId, String indexedShapeType) {
47+
this(name, null, indexedShapeId, indexedShapeType);
48+
}
49+
50+
private GeoShapeQueryBuilder(String name, Shape shape, String indexedShapeId, String indexedShapeType) {
2051
this.name = name;
2152
this.shape = shape;
53+
this.indexedShapeId = indexedShapeId;
54+
this.indexedShapeType = indexedShapeType;
2255
}
2356

57+
/**
58+
* Sets the {@link ShapeRelation} that defines how the Shape used in the
59+
* Query must relate to indexed Shapes
60+
*
61+
* @param relation ShapeRelation used in the filter
62+
* @return this
63+
*/
2464
public GeoShapeQueryBuilder relation(ShapeRelation relation) {
2565
this.relation = relation;
2666
return this;
2767
}
2868

69+
/**
70+
* {@inheritDoc}
71+
*/
2972
@Override
3073
public GeoShapeQueryBuilder boost(float boost) {
3174
this.boost = boost;
3275
return this;
3376
}
3477

78+
/**
79+
* Sets the name of the index where the indexed Shape can be found
80+
*
81+
* @param indexedShapeIndex Name of the index where the indexed Shape is
82+
* @return this
83+
*/
84+
public GeoShapeQueryBuilder indexedShapeIndex(String indexedShapeIndex) {
85+
this.indexedShapeIndex = indexedShapeIndex;
86+
return this;
87+
}
88+
89+
/**
90+
* Sets the name of the field in the indexed Shape document that has the Shape itself
91+
*
92+
* @param indexedShapeFieldName Name of the field where the Shape itself is defined
93+
* @return this
94+
*/
95+
public GeoShapeQueryBuilder indexedShapeFieldName(String indexedShapeFieldName) {
96+
this.indexedShapeFieldName = indexedShapeFieldName;
97+
return this;
98+
}
99+
35100
@Override
36101
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
37102
builder.startObject(GeoShapeQueryParser.NAME);
38103

39104
builder.startObject(name);
40105
builder.field("relation", relation.getRelationName());
41106

42-
builder.startObject("shape");
43-
GeoJSONShapeSerializer.serialize(shape, builder);
44-
builder.endObject();
107+
if (shape != null) {
108+
builder.startObject("shape");
109+
GeoJSONShapeSerializer.serialize(shape, builder);
110+
builder.endObject();
111+
} else {
112+
builder.startObject("indexed_shape")
113+
.field("id", indexedShapeId)
114+
.field("type", indexedShapeType);
115+
if (indexedShapeIndex != null) {
116+
builder.field("index", indexedShapeIndex);
117+
}
118+
if (indexedShapeFieldName != null) {
119+
builder.field("shape_field_name", indexedShapeFieldName);
120+
}
121+
builder.endObject();
122+
}
45123

46124
if (boost != -1) {
47125
builder.field("boost", boost);

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,30 @@
22

33
import com.spatial4j.core.shape.Shape;
44
import org.apache.lucene.search.Query;
5+
import org.elasticsearch.common.Nullable;
56
import org.elasticsearch.common.Strings;
67
import org.elasticsearch.common.geo.GeoJSONShapeParser;
78
import org.elasticsearch.common.geo.ShapeRelation;
9+
import org.elasticsearch.common.inject.Inject;
810
import org.elasticsearch.common.xcontent.XContentParser;
911
import org.elasticsearch.index.mapper.FieldMapper;
1012
import org.elasticsearch.index.mapper.MapperService;
1113
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper;
14+
import org.elasticsearch.index.search.shape.ShapeFetchService;
1215

1316
import java.io.IOException;
1417

1518
public class GeoShapeQueryParser implements QueryParser {
1619

1720
public static final String NAME = "geo_shape";
1821

22+
private ShapeFetchService fetchService;
23+
24+
public static class DEFAULTS {
25+
public static final String INDEX_NAME = "shapes";
26+
public static final String SHAPE_FIELD_NAME = "shape";
27+
}
28+
1929
@Override
2030
public String[] names() {
2131
return new String[]{NAME, Strings.toCamelCase(NAME)};
@@ -29,6 +39,11 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
2939
ShapeRelation shapeRelation = null;
3040
Shape shape = null;
3141

42+
String id = null;
43+
String type = null;
44+
String index = DEFAULTS.INDEX_NAME;
45+
String shapeFieldName = DEFAULTS.SHAPE_FIELD_NAME;
46+
3247
XContentParser.Token token;
3348
String currentFieldName = null;
3449
float boost = 1f;
@@ -51,6 +66,28 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
5166
if (shapeRelation == null) {
5267
throw new QueryParsingException(parseContext.index(), "Unknown shape operation [" + parser.text() + " ]");
5368
}
69+
} else if ("indexed_shape".equals(currentFieldName)) {
70+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
71+
if (token == XContentParser.Token.FIELD_NAME) {
72+
currentFieldName = parser.currentName();
73+
} else if (token.isValue()) {
74+
if ("id".equals(currentFieldName)) {
75+
id = parser.text();
76+
} else if ("type".equals(currentFieldName)) {
77+
type = parser.text();
78+
} else if ("index".equals(currentFieldName)) {
79+
index = parser.text();
80+
} else if ("shape_field_name".equals(currentFieldName)) {
81+
shapeFieldName = parser.text();
82+
}
83+
}
84+
}
85+
if (id == null) {
86+
throw new QueryParsingException(parseContext.index(), "ID for indexed shape not provided");
87+
} else if (type == null) {
88+
throw new QueryParsingException(parseContext.index(), "Type for indexed shape not provided");
89+
}
90+
shape = fetchService.fetch(id, type, index, shapeFieldName);
5491
}
5592
}
5693
}
@@ -84,4 +121,9 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
84121
query.setBoost(boost);
85122
return query;
86123
}
124+
125+
@Inject(optional = true)
126+
public void setFetchService(@Nullable ShapeFetchService fetchService) {
127+
this.fetchService = fetchService;
128+
}
87129
}

0 commit comments

Comments
 (0)