2121import org .apache .lucene .index .LeafReaderContext ;
2222import org .apache .lucene .index .SortedNumericDocValues ;
2323import org .apache .lucene .util .GeoHashUtils ;
24+ import org .elasticsearch .common .ParseField ;
25+ import org .elasticsearch .common .ParseFieldMatcher ;
2426import 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 ;
2530import org .elasticsearch .common .xcontent .XContentParser ;
31+ import org .elasticsearch .common .xcontent .XContentParser .Token ;
2632import org .elasticsearch .index .fielddata .MultiGeoPointValues ;
2733import org .elasticsearch .index .fielddata .SortedBinaryDocValues ;
2834import org .elasticsearch .index .fielddata .SortedNumericDoubleValues ;
2935import org .elasticsearch .index .fielddata .SortingNumericDocValues ;
3036import org .elasticsearch .index .query .GeoBoundingBoxQueryBuilder ;
31- import org .elasticsearch .search .SearchParseException ;
3237import org .elasticsearch .search .aggregations .Aggregator ;
3338import org .elasticsearch .search .aggregations .AggregatorFactory ;
3439import org .elasticsearch .search .aggregations .InternalAggregation ;
3540import org .elasticsearch .search .aggregations .NonCollectingAggregator ;
3641import org .elasticsearch .search .aggregations .bucket .BucketUtils ;
3742import org .elasticsearch .search .aggregations .pipeline .PipelineAggregator ;
43+ import org .elasticsearch .search .aggregations .support .AbstractValuesSourceParser .GeoPointValuesSourceParser ;
3844import org .elasticsearch .search .aggregations .support .AggregationContext ;
45+ import org .elasticsearch .search .aggregations .support .ValueType ;
3946import org .elasticsearch .search .aggregations .support .ValuesSource ;
4047import 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 ;
4349
4450import java .io .IOException ;
4551import java .util .Collections ;
4652import java .util .List ;
4753import java .util .Map ;
54+ import java .util .Objects ;
4855
4956/**
5057 * Aggregates Geo information into cells determined by geohashes of a given precision.
5158 * WARNING - for high-precision geohashes it may prove necessary to use a {@link GeoBoundingBoxQueryBuilder}
5259 * aggregation to focus in on a smaller area to avoid generating too many buckets and using too much RAM
5360 */
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+ }
5569
5670 @ Override
5771 public String type () {
5872 return InternalGeoHashGrid .TYPE .name ();
5973 }
74+ @ Override
75+ public AggregatorFactory getFactoryPrototype () {
76+ return new GeoGridFactory (null );
77+ }
6078
6179 @ 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 );
8687 }
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 );
9191 }
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 ) ;
9595 }
96+ return factory ;
97+ }
9698
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 ;
99112 }
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 ());
104113 }
105-
106- if (shardSize < requiredSize ) {
107- shardSize = requiredSize ;
114+ return false ;
108115 }
109116
110- return new GeoGridFactory ( aggregationName , vsParser . input (), precision , requiredSize , shardSize );
117+ public static class GeoGridFactory extends ValuesSourceAggregatorFactory < ValuesSource . GeoPoint > {
111118
112- }
119+ private int precision = DEFAULT_PRECISION ;
120+ private int requiredSize = DEFAULT_MAX_NUM_CELLS ;
121+ private int shardSize = -1 ;
113122
123+ public GeoGridFactory (String name ) {
124+ super (name , InternalGeoHashGrid .TYPE .name (), ValuesSourceType .GEOPOINT , ValueType .GEOPOINT );
125+ }
114126
115- static class GeoGridFactory extends ValuesSourceAggregatorFactory <ValuesSource .GeoPoint > {
127+ public void precision (int precision ) {
128+ this .precision = GeoHashGridParams .checkPrecision (precision );
129+ }
116130
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+ }
120134
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 ) {
126136 this .shardSize = shardSize ;
127137 }
128138
@@ -143,6 +153,23 @@ public InternalAggregation buildEmptyAggregation() {
143153 protected Aggregator doCreateInternal (final ValuesSource .GeoPoint valuesSource , AggregationContext aggregationContext ,
144154 Aggregator parent , boolean collectsFromSingleBucket , List <PipelineAggregator > pipelineAggregators , Map <String , Object > metaData )
145155 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+ }
146173 if (collectsFromSingleBucket == false ) {
147174 return asMultiBucketAggregator (this , aggregationContext , parent );
148175 }
@@ -152,6 +179,52 @@ protected Aggregator doCreateInternal(final ValuesSource.GeoPoint valuesSource,
152179
153180 }
154181
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+
155228 private static class CellValues extends SortingNumericDocValues {
156229 private MultiGeoPointValues geoValues ;
157230 private int precision ;
@@ -209,10 +282,4 @@ public SortedBinaryDocValues bytesValues(LeafReaderContext ctx) {
209282
210283 }
211284 }
212- // NORELEASE implement this method when refactoring this aggregation
213- @ Override
214- public AggregatorFactory getFactoryPrototype () {
215- return null ;
216- }
217-
218285}
0 commit comments