21
21
import org .apache .lucene .index .LeafReaderContext ;
22
22
import org .apache .lucene .index .SortedNumericDocValues ;
23
23
import org .apache .lucene .util .GeoHashUtils ;
24
+ import org .elasticsearch .common .ParseField ;
25
+ import org .elasticsearch .common .ParseFieldMatcher ;
24
26
import org .elasticsearch .common .geo .GeoPoint ;
27
+ import org .elasticsearch .common .io .stream .StreamInput ;
28
+ import org .elasticsearch .common .io .stream .StreamOutput ;
29
+ import org .elasticsearch .common .xcontent .XContentBuilder ;
25
30
import org .elasticsearch .common .xcontent .XContentParser ;
31
+ import org .elasticsearch .common .xcontent .XContentParser .Token ;
26
32
import org .elasticsearch .index .fielddata .MultiGeoPointValues ;
27
33
import org .elasticsearch .index .fielddata .SortedBinaryDocValues ;
28
34
import org .elasticsearch .index .fielddata .SortedNumericDoubleValues ;
29
35
import org .elasticsearch .index .fielddata .SortingNumericDocValues ;
30
36
import org .elasticsearch .index .query .GeoBoundingBoxQueryBuilder ;
31
- import org .elasticsearch .search .SearchParseException ;
32
37
import org .elasticsearch .search .aggregations .Aggregator ;
33
38
import org .elasticsearch .search .aggregations .AggregatorFactory ;
34
39
import org .elasticsearch .search .aggregations .InternalAggregation ;
35
40
import org .elasticsearch .search .aggregations .NonCollectingAggregator ;
36
41
import org .elasticsearch .search .aggregations .bucket .BucketUtils ;
37
42
import org .elasticsearch .search .aggregations .pipeline .PipelineAggregator ;
43
+ import org .elasticsearch .search .aggregations .support .AbstractValuesSourceParser .GeoPointValuesSourceParser ;
38
44
import org .elasticsearch .search .aggregations .support .AggregationContext ;
45
+ import org .elasticsearch .search .aggregations .support .ValueType ;
39
46
import org .elasticsearch .search .aggregations .support .ValuesSource ;
40
47
import org .elasticsearch .search .aggregations .support .ValuesSourceAggregatorFactory ;
41
- import org .elasticsearch .search .aggregations .support .ValuesSourceParser ;
42
- import org .elasticsearch .search .internal .SearchContext ;
48
+ import org .elasticsearch .search .aggregations .support .ValuesSourceType ;
43
49
44
50
import java .io .IOException ;
45
51
import java .util .Collections ;
46
52
import java .util .List ;
47
53
import java .util .Map ;
54
+ import java .util .Objects ;
48
55
49
56
/**
50
57
* Aggregates Geo information into cells determined by geohashes of a given precision.
51
58
* WARNING - for high-precision geohashes it may prove necessary to use a {@link GeoBoundingBoxQueryBuilder}
52
59
* aggregation to focus in on a smaller area to avoid generating too many buckets and using too much RAM
53
60
*/
54
- public class GeoHashGridParser implements Aggregator .Parser {
61
+ public class GeoHashGridParser extends GeoPointValuesSourceParser {
62
+
63
+ public static final int DEFAULT_PRECISION = 5 ;
64
+ public static final int DEFAULT_MAX_NUM_CELLS = 10000 ;
65
+
66
+ public GeoHashGridParser () {
67
+ super (false , false );
68
+ }
55
69
56
70
@ Override
57
71
public String type () {
58
72
return InternalGeoHashGrid .TYPE .name ();
59
73
}
74
+ @ Override
75
+ public AggregatorFactory getFactoryPrototype () {
76
+ return new GeoGridFactory (null );
77
+ }
60
78
61
79
@ Override
62
- public AggregatorFactory parse (String aggregationName , XContentParser parser , SearchContext context ) throws IOException {
63
-
64
- ValuesSourceParser <ValuesSource .GeoPoint > vsParser = ValuesSourceParser
65
- .geoPoint (aggregationName , InternalGeoHashGrid .TYPE , context ).build ();
66
-
67
- int precision = GeoHashGridParams .DEFAULT_PRECISION ;
68
- int requiredSize = GeoHashGridParams .DEFAULT_MAX_NUM_CELLS ;
69
- int shardSize = -1 ;
70
-
71
- XContentParser .Token token ;
72
- String currentFieldName = null ;
73
- while ((token = parser .nextToken ()) != XContentParser .Token .END_OBJECT ) {
74
- if (token == XContentParser .Token .FIELD_NAME ) {
75
- currentFieldName = parser .currentName ();
76
- } else if (vsParser .token (currentFieldName , token , parser )) {
77
- continue ;
78
- } else if (token == XContentParser .Token .VALUE_NUMBER ||
79
- token == XContentParser .Token .VALUE_STRING ) { //Be lenient and also allow numbers enclosed in quotes
80
- if (context .parseFieldMatcher ().match (currentFieldName , GeoHashGridParams .FIELD_PRECISION )) {
81
- precision = GeoHashGridParams .checkPrecision (parser .intValue ());
82
- } else if (context .parseFieldMatcher ().match (currentFieldName , GeoHashGridParams .FIELD_SIZE )) {
83
- requiredSize = parser .intValue ();
84
- } else if (context .parseFieldMatcher ().match (currentFieldName , GeoHashGridParams .FIELD_SHARD_SIZE )) {
85
- shardSize = parser .intValue ();
80
+ protected ValuesSourceAggregatorFactory <org .elasticsearch .search .aggregations .support .ValuesSource .GeoPoint > createFactory (
81
+ String aggregationName , ValuesSourceType valuesSourceType ,
82
+ ValueType targetValueType , Map <ParseField , Object > otherOptions ) {
83
+ GeoGridFactory factory = new GeoGridFactory (aggregationName );
84
+ Integer precision = (Integer ) otherOptions .get (GeoHashGridParams .FIELD_PRECISION );
85
+ if (precision != null ) {
86
+ factory .precision (precision );
86
87
}
87
- } else if (token != XContentParser .Token .START_OBJECT ) {
88
- throw new SearchParseException (context , "Unexpected token " + token + " in [" + aggregationName + "]." ,
89
- parser .getTokenLocation ());
90
- }
88
+ Integer size = (Integer ) otherOptions .get (GeoHashGridParams .FIELD_SIZE );
89
+ if (size != null ) {
90
+ factory .size (size );
91
91
}
92
-
93
- if (shardSize == 0 ) {
94
- shardSize = Integer . MAX_VALUE ;
92
+ Integer shardSize = ( Integer ) otherOptions . get ( GeoHashGridParams . FIELD_SHARD_SIZE );
93
+ if (shardSize != null ) {
94
+ factory . shardSize ( shardSize ) ;
95
95
}
96
+ return factory ;
97
+ }
96
98
97
- if (requiredSize == 0 ) {
98
- requiredSize = Integer .MAX_VALUE ;
99
+ @ Override
100
+ protected boolean token (String aggregationName , String currentFieldName , Token token , XContentParser parser ,
101
+ ParseFieldMatcher parseFieldMatcher , Map <ParseField , Object > otherOptions ) throws IOException {
102
+ if (token == XContentParser .Token .VALUE_NUMBER || token == XContentParser .Token .VALUE_STRING ) {
103
+ if (parseFieldMatcher .match (currentFieldName , GeoHashGridParams .FIELD_PRECISION )) {
104
+ otherOptions .put (GeoHashGridParams .FIELD_PRECISION , parser .intValue ());
105
+ return true ;
106
+ } else if (parseFieldMatcher .match (currentFieldName , GeoHashGridParams .FIELD_SIZE )) {
107
+ otherOptions .put (GeoHashGridParams .FIELD_SIZE , parser .intValue ());
108
+ return true ;
109
+ } else if (parseFieldMatcher .match (currentFieldName , GeoHashGridParams .FIELD_SHARD_SIZE )) {
110
+ otherOptions .put (GeoHashGridParams .FIELD_SHARD_SIZE , parser .intValue ());
111
+ return true ;
99
112
}
100
-
101
- if (shardSize < 0 ) {
102
- //Use default heuristic to avoid any wrong-ranking caused by distributed counting
103
- shardSize = BucketUtils .suggestShardSideQueueSize (requiredSize , context .numberOfShards ());
104
113
}
105
-
106
- if (shardSize < requiredSize ) {
107
- shardSize = requiredSize ;
114
+ return false ;
108
115
}
109
116
110
- return new GeoGridFactory ( aggregationName , vsParser . input (), precision , requiredSize , shardSize );
117
+ public static class GeoGridFactory extends ValuesSourceAggregatorFactory < ValuesSource . GeoPoint > {
111
118
112
- }
119
+ private int precision = DEFAULT_PRECISION ;
120
+ private int requiredSize = DEFAULT_MAX_NUM_CELLS ;
121
+ private int shardSize = -1 ;
113
122
123
+ public GeoGridFactory (String name ) {
124
+ super (name , InternalGeoHashGrid .TYPE .name (), ValuesSourceType .GEOPOINT , ValueType .GEOPOINT );
125
+ }
114
126
115
- static class GeoGridFactory extends ValuesSourceAggregatorFactory <ValuesSource .GeoPoint > {
127
+ public void precision (int precision ) {
128
+ this .precision = GeoHashGridParams .checkPrecision (precision );
129
+ }
116
130
117
- private final int precision ;
118
- private final int requiredSize ;
119
- private final int shardSize ;
131
+ public void size ( int size ) {
132
+ this . requiredSize = size ;
133
+ }
120
134
121
- public GeoGridFactory (String name , ValuesSourceParser .Input <ValuesSource .GeoPoint > input , int precision , int requiredSize ,
122
- int shardSize ) {
123
- super (name , InternalGeoHashGrid .TYPE .name (), input );
124
- this .precision = precision ;
125
- this .requiredSize = requiredSize ;
135
+ public void shardSize (int shardSize ) {
126
136
this .shardSize = shardSize ;
127
137
}
128
138
@@ -143,6 +153,23 @@ public InternalAggregation buildEmptyAggregation() {
143
153
protected Aggregator doCreateInternal (final ValuesSource .GeoPoint valuesSource , AggregationContext aggregationContext ,
144
154
Aggregator parent , boolean collectsFromSingleBucket , List <PipelineAggregator > pipelineAggregators , Map <String , Object > metaData )
145
155
throws IOException {
156
+ if (shardSize == 0 ) {
157
+ shardSize = Integer .MAX_VALUE ;
158
+ }
159
+
160
+ if (requiredSize == 0 ) {
161
+ requiredSize = Integer .MAX_VALUE ;
162
+ }
163
+
164
+ if (shardSize < 0 ) {
165
+ // Use default heuristic to avoid any wrong-ranking caused by
166
+ // distributed counting
167
+ shardSize = BucketUtils .suggestShardSideQueueSize (requiredSize , aggregationContext .searchContext ().numberOfShards ());
168
+ }
169
+
170
+ if (shardSize < requiredSize ) {
171
+ shardSize = requiredSize ;
172
+ }
146
173
if (collectsFromSingleBucket == false ) {
147
174
return asMultiBucketAggregator (this , aggregationContext , parent );
148
175
}
@@ -152,6 +179,52 @@ protected Aggregator doCreateInternal(final ValuesSource.GeoPoint valuesSource,
152
179
153
180
}
154
181
182
+ @ Override
183
+ protected ValuesSourceAggregatorFactory <org .elasticsearch .search .aggregations .support .ValuesSource .GeoPoint > innerReadFrom (
184
+ String name , ValuesSourceType valuesSourceType ,
185
+ ValueType targetValueType , StreamInput in ) throws IOException {
186
+ GeoGridFactory factory = new GeoGridFactory (name );
187
+ factory .precision = in .readVInt ();
188
+ factory .requiredSize = in .readVInt ();
189
+ factory .shardSize = in .readVInt ();
190
+ return factory ;
191
+ }
192
+
193
+ @ Override
194
+ protected void innerWriteTo (StreamOutput out ) throws IOException {
195
+ out .writeVInt (precision );
196
+ out .writeVInt (requiredSize );
197
+ out .writeVInt (shardSize );
198
+ }
199
+
200
+ @ Override
201
+ protected XContentBuilder doXContentBody (XContentBuilder builder , Params params ) throws IOException {
202
+ builder .field (GeoHashGridParams .FIELD_PRECISION .getPreferredName (), precision );
203
+ builder .field (GeoHashGridParams .FIELD_SIZE .getPreferredName (), requiredSize );
204
+ builder .field (GeoHashGridParams .FIELD_SHARD_SIZE .getPreferredName (), shardSize );
205
+ return builder ;
206
+ }
207
+
208
+ @ Override
209
+ protected boolean innerEquals (Object obj ) {
210
+ GeoGridFactory other = (GeoGridFactory ) obj ;
211
+ if (precision != other .precision ) {
212
+ return false ;
213
+ }
214
+ if (requiredSize != other .requiredSize ) {
215
+ return false ;
216
+ }
217
+ if (shardSize != other .shardSize ) {
218
+ return false ;
219
+ }
220
+ return true ;
221
+ }
222
+
223
+ @ Override
224
+ protected int innerHashCode () {
225
+ return Objects .hash (precision , requiredSize , shardSize );
226
+ }
227
+
155
228
private static class CellValues extends SortingNumericDocValues {
156
229
private MultiGeoPointValues geoValues ;
157
230
private int precision ;
@@ -209,10 +282,4 @@ public SortedBinaryDocValues bytesValues(LeafReaderContext ctx) {
209
282
210
283
}
211
284
}
212
- // NORELEASE implement this method when refactoring this aggregation
213
- @ Override
214
- public AggregatorFactory getFactoryPrototype () {
215
- return null ;
216
- }
217
-
218
285
}
0 commit comments