Skip to content

Commit 46693aa

Browse files
committed
Add geo_centroid aggregation
1 parent cdea256 commit 46693aa

File tree

17 files changed

+480
-5
lines changed

17 files changed

+480
-5
lines changed

docs/aggregations-usage.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ include::aggregations/metric/extended-stats/extended-stats-aggregation-usage.asc
4444

4545
include::aggregations/metric/geo-bounds/geo-bounds-aggregation-usage.asciidoc[]
4646

47+
include::aggregations/metric/geo-centroid/geo-centroid-aggregation-usage.asciidoc[]
48+
4749
include::aggregations/metric/max/max-aggregation-usage.asciidoc[]
4850

4951
include::aggregations/metric/min/min-aggregation-usage.asciidoc[]

docs/aggregations.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ exposes all of the available Aggregation types
5050

5151
* <<geo-bounds-aggregation-usage,Geo Bounds Aggregation Usage>>
5252

53+
* <<geo-centroid-aggregation-usage,Geo Centroid Aggregation Usage>>
54+
5355
* <<max-aggregation-usage,Max Aggregation Usage>>
5456

5557
* <<min-aggregation-usage,Min Aggregation Usage>>
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
:ref_current: https://www.elastic.co/guide/en/elasticsearch/reference/master
2+
3+
:github: https://github.com/elastic/elasticsearch-net
4+
5+
:nuget: https://www.nuget.org/packages
6+
7+
[[geo-centroid-aggregation-usage]]
8+
== Geo Centroid Aggregation Usage
9+
10+
A metric aggregation that computes the weighted centroid from all coordinate values
11+
for a Geo-point datatype field.
12+
13+
Be sure to read the Elasticsearch documentation on {ref_current}/search-aggregations-metrics-geocentroid-aggregation.html[Geo Centroid Aggregation]
14+
15+
=== Fluent DSL Example
16+
17+
[source,csharp]
18+
----
19+
s => s
20+
.Aggregations(a => a
21+
.GeoCentroid("centroid", gb => gb
22+
.Field(p => p.Location)
23+
)
24+
)
25+
----
26+
27+
=== Object Initializer Syntax Example
28+
29+
[source,csharp]
30+
----
31+
new SearchRequest<Project>
32+
{
33+
Aggregations = new GeoCentroidAggregation("centroid", Infer.Field<Project>(p => p.Location))
34+
}
35+
----
36+
37+
[source,javascript]
38+
.Example json output
39+
----
40+
{
41+
"aggs": {
42+
"centroid": {
43+
"geo_centroid": {
44+
"field": "location"
45+
}
46+
}
47+
}
48+
}
49+
----
50+
51+
=== Handling Responses
52+
53+
[source,csharp]
54+
----
55+
response.IsValid.Should().BeTrue();
56+
var centroid = response.Aggs.GeoCentroid("centroid");
57+
centroid.Should().NotBeNull();
58+
centroid.Location.Should().NotBeNull();
59+
centroid.Location.Latitude.Should().NotBe(0);
60+
centroid.Location.Longitude.Should().NotBe(0);
61+
----
62+
63+
[[geo-centroid-sub-aggregation]]
64+
[float]
65+
== Geo Centroid Sub Aggregation
66+
67+
The `geo_centroid` aggregation is more interesting when combined as a sub-aggregation to other bucket aggregations
68+
69+
=== Fluent DSL Example
70+
71+
[source,csharp]
72+
----
73+
s => s
74+
.Aggregations(a => a
75+
.Terms("projects", t => t
76+
.Field(p => p.Name)
77+
.Aggregations(sa => sa
78+
.GeoCentroid("centroid", gb => gb
79+
.Field(p => p.Location)
80+
)
81+
)
82+
)
83+
)
84+
----
85+
86+
=== Object Initializer Syntax Example
87+
88+
[source,csharp]
89+
----
90+
new SearchRequest<Project>
91+
{
92+
Aggregations = new TermsAggregation("projects")
93+
{
94+
Field = Infer.Field<Project>(p => p.Name),
95+
Aggregations = new GeoCentroidAggregation("centroid", Infer.Field<Project>(p => p.Location))
96+
}
97+
}
98+
----
99+
100+
[source,javascript]
101+
.Example json output
102+
----
103+
{
104+
"aggs": {
105+
"projects": {
106+
"terms": {
107+
"field": "name"
108+
},
109+
"aggs": {
110+
"centroid": {
111+
"geo_centroid": {
112+
"field": "location"
113+
}
114+
}
115+
}
116+
}
117+
}
118+
}
119+
----
120+
121+
=== Handling Responses
122+
123+
[source,csharp]
124+
----
125+
response.IsValid.Should().BeTrue();
126+
var projects = response.Aggs.Terms("projects");
127+
128+
foreach (var bucket in projects.Buckets)
129+
{
130+
var centroid = bucket.GeoCentroid("centroid");
131+
centroid.Should().NotBeNull();
132+
centroid.Location.Should().NotBeNull();
133+
134+
centroid.Location.Latitude.Should().NotBe(0);
135+
centroid.Location.Longitude.Should().NotBe(0);
136+
}
137+
----
138+

docs/client-concepts/connection-pooling/sniffing/role-detection.asciidoc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,38 @@ var audit = new Auditor(() => Framework.Cluster
4040
await audit.TraceStartup();
4141
----
4242

43+
[source,csharp]
44+
----
45+
var audit = new Auditor(() => Framework.Cluster
46+
.Nodes(10)
47+
.Sniff(s => s.SucceedAlways()
48+
.Succeeds(Always, Framework.Cluster.Nodes(8).StoresNoData(9200, 9201, 9202).HttpDisabled(9201))
49+
)
50+
.SniffingConnectionPool()
51+
.AllDefaults()
52+
)
53+
{
54+
AssertPoolBeforeCall = (pool) =>
55+
{
56+
pool.Should().NotBeNull();
57+
pool.Nodes.Should().HaveCount(10);
58+
pool.Nodes.Where(n => n.HoldsData).Should().HaveCount(10);
59+
pool.Nodes.Where(n => n.HttpEnabled).Should().HaveCount(10);
60+
pool.Nodes.Should().OnlyContain(n => n.Uri.Host == "localhost");
61+
},
62+
63+
AssertPoolAfterCall = (pool) =>
64+
{
65+
pool.Should().NotBeNull();
66+
pool.Nodes.Should().HaveCount(7, "we filtered the node that stores no data");
67+
pool.Nodes.Should().NotContain(n=>n.Uri.Port == 9201);
68+
pool.Nodes.Where(n => n.HoldsData).Should().HaveCount(5);
69+
}
70+
};
71+
72+
await audit.TraceStartup();
73+
----
74+
4375
[source,csharp]
4476
----
4577
var audit = new Auditor(() => Framework.Cluster

docs/query-dsl/compound/bool/bool-query-usage.asciidoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,17 @@ new BoolQuery()
6767
}
6868
----
6969

70+
[source,csharp]
71+
----
72+
Action query = () => this.Client.Search<Project>(s => s
73+
.Query(q => q
74+
.Bool(b => b
75+
.Filter(f => f
76+
.Term(t => t.Name, null)
77+
)
78+
)
79+
)
80+
);
81+
query.ShouldNotThrow();
82+
----
83+

docs/query-dsl/compound/dismax/dismax-query-usage.asciidoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,17 @@ new DisMaxQuery()
6363
}
6464
----
6565

66+
[source,csharp]
67+
----
68+
Action query = () => this.Client.Search<Project>(s => s
69+
.Query(q => q
70+
.DisMax(dm => dm
71+
.Queries(
72+
dmq => dmq.Term(t => t.Name, null)
73+
)
74+
)
75+
)
76+
);
77+
query.ShouldNotThrow();
78+
----
79+

docs/query-dsl/verbatim/verbatim-and-strict-query-usage.asciidoc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,77 @@ new TermQuery
137137
}
138138
----
139139

140+
=== Fluent DSL Example
141+
142+
[source,csharp]
143+
----
144+
q
145+
.Bool(b => b
146+
.Filter(f => !f
147+
.Term(t => t
148+
.Verbatim()
149+
.Field(p => p.Name)
150+
.Value("")
151+
) && f
152+
.Exists(e => e
153+
.Field(p => p.NumberOfCommits)
154+
)
155+
)
156+
)
157+
----
158+
159+
=== Object Initializer Syntax Example
160+
161+
[source,csharp]
162+
----
163+
new BoolQuery
164+
{
165+
Filter = new QueryContainer[] {
166+
!new TermQuery
167+
{
168+
IsVerbatim = true,
169+
Field = "name",
170+
Value = ""
171+
} &&
172+
new ExistsQuery
173+
{
174+
Field = "numberOfCommits"
175+
}
176+
}
177+
}
178+
----
179+
180+
[source,javascript]
181+
.Example json output
182+
----
183+
{
184+
"bool": {
185+
"filter": [
186+
{
187+
"bool": {
188+
"must": [
189+
{
190+
"exists": {
191+
"field": "numberOfCommits"
192+
}
193+
}
194+
],
195+
"must_not": [
196+
{
197+
"term": {
198+
"name": {
199+
"value": ""
200+
}
201+
}
202+
}
203+
]
204+
}
205+
}
206+
]
207+
}
208+
}
209+
----
210+
140211
[source,csharp]
141212
----
142213
var e = Assert.Throws<ArgumentException>(() =>

src/Nest/Aggregations/AggregateJsonConverter.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ private IAggregate ReadAggregate(JsonReader reader, JsonSerializer serializer)
7474
case "hits":
7575
aggregate = GetTopHitsAggregate(reader, serializer);
7676
break;
77+
case "location":
78+
aggregate = GetGeoCentroidAggregate(reader, serializer);
79+
break;
7780
default:
7881
return null;
7982
}
@@ -146,6 +149,14 @@ private IAggregate GetTopHitsAggregate(JsonReader reader, JsonSerializer seriali
146149
return new TopHitsAggregate(hits, serializer) { Total = total, MaxScore = maxScore };
147150
}
148151

152+
private IAggregate GetGeoCentroidAggregate(JsonReader reader, JsonSerializer serializer)
153+
{
154+
reader.Read();
155+
var geoCentroid = new GeoCentroidAggregate { Location = serializer.Deserialize<GeoLocation>(reader) };
156+
reader.Read();
157+
return geoCentroid;
158+
}
159+
149160
private IAggregate GetGeoBoundsAggregate(JsonReader reader, JsonSerializer serializer)
150161
{
151162
reader.Read();
@@ -586,4 +597,4 @@ private IBucket GetFiltersBucket(JsonReader reader, JsonSerializer serializer)
586597
return filtersBucketItem;
587598
}
588599
}
589-
}
600+
}

src/Nest/Aggregations/AggregationContainer.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ public interface IAggregationContainer
180180
IBucketSelectorAggregation BucketSelector { get; set; }
181181

182182
[JsonProperty("sampler")]
183-
ISamplerAggregation Sampler { get; set; }
183+
ISamplerAggregation Sampler { get; set; }
184+
185+
[JsonProperty("geo_centroid")]
186+
IGeoCentroidAggregation GeoCentroid { get; set; }
184187

185188
[JsonProperty("aggs")]
186189
AggregationDictionary Aggregations { get; set; }
@@ -268,7 +271,9 @@ public class AggregationContainer : IAggregationContainer
268271

269272
public IBucketSelectorAggregation BucketSelector { get; set; }
270273

271-
public ISamplerAggregation Sampler { get; set; }
274+
public ISamplerAggregation Sampler { get; set; }
275+
276+
public IGeoCentroidAggregation GeoCentroid { get; set; }
272277

273278
public AggregationDictionary Aggregations { get; set; }
274279

@@ -381,7 +386,9 @@ public class AggregationContainerDescriptor<T> : DescriptorBase<AggregationConta
381386

382387
IBucketSelectorAggregation IAggregationContainer.BucketSelector { get; set; }
383388

384-
ISamplerAggregation IAggregationContainer.Sampler { get; set; }
389+
ISamplerAggregation IAggregationContainer.Sampler { get; set; }
390+
391+
IGeoCentroidAggregation IAggregationContainer.GeoCentroid { get; set; }
385392

386393
public AggregationContainerDescriptor<T> Average(string name,
387394
Func<AverageAggregationDescriptor<T>, IAverageAggregation> selector) =>
@@ -555,6 +562,10 @@ public AggregationContainerDescriptor<T> Sampler(string name,
555562
Func<SamplerAggregationDescriptor<T>, ISamplerAggregation> selector) =>
556563
_SetInnerAggregation(name, selector, (a, d) => a.Sampler = d);
557564

565+
public AggregationContainerDescriptor<T> GeoCentroid(string name,
566+
Func<GeoCentroidAggregationDescriptor<T>, IGeoCentroidAggregation> selector) =>
567+
_SetInnerAggregation(name, selector, (a, d) => a.GeoCentroid = d);
568+
558569
/// <summary>
559570
/// Fluent methods do not assign to properties on `this` directly but on IAggregationContainers inside `this.Aggregations[string, IContainer]
560571
/// </summary>

src/Nest/Aggregations/AggregationsHelper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ public FiltersAggregate Filters(string key)
9393

9494
public SingleBucketAggregate Children(string key) => this.TryGet<SingleBucketAggregate>(key);
9595

96-
public SingleBucketAggregate Sampler(string key) => this.TryGet<SingleBucketAggregate>(key);
96+
public SingleBucketAggregate Sampler(string key) => this.TryGet<SingleBucketAggregate>(key);
97+
98+
public GeoCentroidAggregate GeoCentroid(string key) => this.TryGet<GeoCentroidAggregate>(key);
9799

98100
public SignificantTermsAggregate SignificantTerms(string key)
99101
{

0 commit comments

Comments
 (0)