Skip to content

Commit 96887d7

Browse files
committed
Support geohash_grid aggregation in composite agg sources
1 parent 9989276 commit 96887d7

File tree

8 files changed

+462
-6
lines changed

8 files changed

+462
-6
lines changed

rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml

+90-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ setup:
1212
type: keyword
1313
long:
1414
type: long
15+
geo_point:
16+
type: geo_point
1517
nested:
1618
type: nested
1719
properties:
@@ -38,25 +40,25 @@ setup:
3840
index:
3941
index: test
4042
id: 1
41-
body: { "keyword": "foo", "long": [10, 20], "nested": [{"nested_long": 10}, {"nested_long": 20}] }
43+
body: { "keyword": "foo", "long": [10, 20], "geo_point": "37.2343,-115.8067", "nested": [{"nested_long": 10}, {"nested_long": 20}] }
4244

4345
- do:
4446
index:
4547
index: test
4648
id: 2
47-
body: { "keyword": ["foo", "bar"] }
49+
body: { "keyword": ["foo", "bar"], "geo_point": "41.12,-71.34" }
4850

4951
- do:
5052
index:
5153
index: test
5254
id: 3
53-
body: { "keyword": "bar", "long": [100, 0], "nested": [{"nested_long": 10}, {"nested_long": 0}] }
55+
body: { "keyword": "bar", "long": [100, 0], "geo_point": "90.0,0.0", "nested": [{"nested_long": 10}, {"nested_long": 0}] }
5456

5557
- do:
5658
index:
5759
index: test
5860
id: 4
59-
body: { "keyword": "bar", "long": [1000, 0], "nested": [{"nested_long": 1000}, {"nested_long": 20}] }
61+
body: { "keyword": "bar", "long": [1000, 0], "geo_point": "41.12,-71.34", "nested": [{"nested_long": 1000}, {"nested_long": 20}] }
6062

6163
- do:
6264
index:
@@ -615,3 +617,87 @@ setup:
615617
}
616618
]
617619

620+
---
621+
"Simple Composite aggregation with Geohash grid":
622+
- skip:
623+
version: " - 7.99.99"
624+
reason: geohash_grid is not supported until 8.0.0
625+
- do:
626+
search:
627+
rest_total_hits_as_int: true
628+
index: test
629+
body:
630+
aggregations:
631+
test:
632+
composite:
633+
sources: [
634+
"geo": {
635+
"geohash_grid": {
636+
"field": "geo_point",
637+
"precision": 12
638+
}
639+
},
640+
{
641+
"kw": {
642+
"terms": {
643+
"field": "keyword"
644+
}
645+
}
646+
}
647+
]
648+
649+
- match: {hits.total: 6}
650+
- length: { aggregations.test.buckets: 4 }
651+
- match: { aggregations.test.buckets.0.key.geo: "upbpbpbpbpbp" }
652+
- match: { aggregations.test.buckets.0.key.kw: "bar" }
653+
- match: { aggregations.test.buckets.0.doc_count: 1 }
654+
- match: { aggregations.test.buckets.1.key.geo: "9qteuf21s29g" }
655+
- match: { aggregations.test.buckets.1.key.kw: "foo" }
656+
- match: { aggregations.test.buckets.1.doc_count: 1 }
657+
- match: { aggregations.test.buckets.2.key.geo: "drm3btev3e86" }
658+
- match: { aggregations.test.buckets.2.key.kw: "bar" }
659+
- match: { aggregations.test.buckets.2.doc_count: 2 }
660+
- match: { aggregations.test.buckets.3.key.geo: "drm3btev3e86" }
661+
- match: { aggregations.test.buckets.3.key.kw: "foo" }
662+
- match: { aggregations.test.buckets.3.doc_count: 1 }
663+
---
664+
"Simple Composite aggregation with geohash grid add aggregate after":
665+
- skip:
666+
version: " - 7.99.99"
667+
reason: geohash_grid is not supported until 8.0.0
668+
- do:
669+
search:
670+
rest_total_hits_as_int: true
671+
index: test
672+
body:
673+
aggregations:
674+
test:
675+
composite:
676+
sources: [
677+
"geo": {
678+
"geohash_grid": {
679+
"field": "geo_point",
680+
"precision": 12
681+
}
682+
},
683+
{
684+
"kw": {
685+
"terms": {
686+
"field": "keyword"
687+
}
688+
}
689+
}
690+
]
691+
after: { "geo": "upbpbpbpbpbp", "kw": "bar" }
692+
693+
- match: {hits.total: 6}
694+
- length: { aggregations.test.buckets: 3 }
695+
- match: { aggregations.test.buckets.0.key.geo: "9qteuf21s29g" }
696+
- match: { aggregations.test.buckets.0.key.kw: "foo" }
697+
- match: { aggregations.test.buckets.0.doc_count: 1 }
698+
- match: { aggregations.test.buckets.1.key.geo: "drm3btev3e86" }
699+
- match: { aggregations.test.buckets.1.key.kw: "bar" }
700+
- match: { aggregations.test.buckets.1.doc_count: 2 }
701+
- match: { aggregations.test.buckets.2.key.geo: "drm3btev3e86" }
702+
- match: { aggregations.test.buckets.2.key.kw: "foo" }
703+
- match: { aggregations.test.buckets.2.doc_count: 1 }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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.search.aggregations.bucket.composite;
20+
21+
import org.apache.lucene.index.LeafReaderContext;
22+
import org.apache.lucene.index.SortedNumericDocValues;
23+
import org.elasticsearch.index.fielddata.AbstractSortingNumericDocValues;
24+
import org.elasticsearch.index.fielddata.MultiGeoPointValues;
25+
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
26+
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
27+
import org.elasticsearch.search.aggregations.support.ValuesSource;
28+
29+
import java.io.IOException;
30+
31+
/**
32+
* Wrapper class to help convert {@link MultiGeoPointValues}
33+
* to numeric long values for bucketing.
34+
*/
35+
class CellIdSource extends ValuesSource.Numeric {
36+
private final GeoPoint valuesSource;
37+
private final int precision;
38+
private final GeoPointLongEncoder encoder;
39+
40+
CellIdSource(GeoPoint valuesSource, int precision, GeoPointLongEncoder encoder) {
41+
this.valuesSource = valuesSource;
42+
//different GeoPoints could map to the same or different hashing cells.
43+
this.precision = precision;
44+
this.encoder = encoder;
45+
}
46+
47+
int precision() {
48+
return precision;
49+
}
50+
51+
GeoPointLongEncoder encoder() {
52+
return encoder;
53+
}
54+
55+
@Override
56+
public boolean isFloatingPoint() {
57+
return false;
58+
}
59+
60+
@Override
61+
public SortedNumericDocValues longValues(LeafReaderContext ctx) {
62+
return new CellValues(valuesSource.geoPointValues(ctx), precision, encoder);
63+
}
64+
65+
@Override
66+
public SortedNumericDoubleValues doubleValues(LeafReaderContext ctx) {
67+
throw new UnsupportedOperationException();
68+
}
69+
70+
@Override
71+
public SortedBinaryDocValues bytesValues(LeafReaderContext ctx) {
72+
throw new UnsupportedOperationException();
73+
}
74+
75+
/**
76+
* The encoder to use to convert a geopoint's (lon, lat, precision) into
77+
* a long-encoded bucket key for aggregating.
78+
*/
79+
@FunctionalInterface
80+
public interface GeoPointLongEncoder {
81+
long encode(double lon, double lat, int precision);
82+
}
83+
84+
private static class CellValues extends AbstractSortingNumericDocValues {
85+
private MultiGeoPointValues geoValues;
86+
private int precision;
87+
private GeoPointLongEncoder encoder;
88+
89+
private CellValues(MultiGeoPointValues geoValues, int precision, GeoPointLongEncoder encoder) {
90+
this.geoValues = geoValues;
91+
this.precision = precision;
92+
this.encoder = encoder;
93+
}
94+
95+
@Override
96+
public boolean advanceExact(int docId) throws IOException {
97+
if (geoValues.advanceExact(docId)) {
98+
resize(geoValues.docValueCount());
99+
for (int i = 0; i < docValueCount(); ++i) {
100+
org.elasticsearch.common.geo.GeoPoint target = geoValues.nextValue();
101+
values[i] = encoder.encode(target.getLon(), target.getLat(), precision);
102+
}
103+
sort();
104+
return true;
105+
} else {
106+
return false;
107+
}
108+
}
109+
}
110+
}

server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java

+14
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,20 @@ private SingleDimensionValuesSource<?> createValuesSource(BigArrays bigArrays, I
299299
reverseMul
300300
);
301301

302+
} else if (config.valuesSource() instanceof CellIdSource) {
303+
final CellIdSource cis = (CellIdSource) config.valuesSource();
304+
return new GeohashValuesSource(
305+
bigArrays,
306+
config.fieldType(),
307+
cis::longValues,
308+
LongUnaryOperator.identity(),
309+
config.format(),
310+
config.missingBucket(),
311+
size,
312+
reverseMul,
313+
cis.precision(),
314+
cis.encoder()
315+
);
302316
} else if (config.valuesSource() instanceof ValuesSource.Numeric) {
303317
final ValuesSource.Numeric vs = (ValuesSource.Numeric) config.valuesSource();
304318
if (vs.isFloatingPoint()) {

server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeValuesSourceParserHelper.java

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.search.aggregations.bucket.composite;
2121

22+
import org.elasticsearch.Version;
2223
import org.elasticsearch.common.ParseField;
2324
import org.elasticsearch.common.ParsingException;
2425
import org.elasticsearch.common.io.stream.StreamInput;
@@ -67,6 +68,8 @@ public static void writeTo(CompositeValuesSourceBuilder<?> builder, StreamOutput
6768
code = 1;
6869
} else if (builder.getClass() == HistogramValuesSourceBuilder.class) {
6970
code = 2;
71+
} else if (builder.getClass() == GeoHashGridValuesSourceBuilder.class && out.getVersion().onOrAfter(Version.V_8_0_0)) {
72+
code = 3;
7073
} else {
7174
throw new IOException("invalid builder type: " + builder.getClass().getSimpleName());
7275
}
@@ -83,6 +86,8 @@ public static CompositeValuesSourceBuilder<?> readFrom(StreamInput in) throws IO
8386
return new DateHistogramValuesSourceBuilder(in);
8487
case 2:
8588
return new HistogramValuesSourceBuilder(in);
89+
case 3:
90+
return new GeoHashGridValuesSourceBuilder(in);
8691
default:
8792
throw new IOException("Invalid code " + code);
8893
}
@@ -112,6 +117,9 @@ public static CompositeValuesSourceBuilder<?> fromXContent(XContentParser parser
112117
case HistogramValuesSourceBuilder.TYPE:
113118
builder = HistogramValuesSourceBuilder.parse(name, parser);
114119
break;
120+
case GeoHashGridValuesSourceBuilder.TYPE:
121+
builder = GeoHashGridValuesSourceBuilder.parse(name, parser);
122+
break;
115123
default:
116124
throw new ParsingException(parser.getTokenLocation(), "invalid source type: " + type);
117125
}

0 commit comments

Comments
 (0)