1
- using System ;
1
+ using System ;
2
2
using System . Collections . Generic ;
3
3
using System . Threading . Tasks ;
4
+ using Elastic . Clients . Elasticsearch ;
5
+ using Elastic . Clients . Elasticsearch . Aggregations ;
4
6
using Exceptionless . DateTimeExtensions ;
5
7
using Foundatio . Parsers . ElasticQueries . Visitors ;
6
8
using Foundatio . Parsers . LuceneQueries . Extensions ;
7
9
using Foundatio . Parsers . LuceneQueries . Nodes ;
8
10
using Foundatio . Parsers . LuceneQueries . Visitors ;
9
- using Nest ;
10
11
11
12
namespace Foundatio . Parsers . ElasticQueries . Extensions ;
12
13
@@ -15,7 +16,7 @@ public static class DefaultAggregationNodeExtensions
15
16
// NOTE: We may want to read this dynamically from server settings.
16
17
public const int MAX_BUCKET_SIZE = 10000 ;
17
18
18
- public static Task < AggregationBase > GetDefaultAggregationAsync ( this IQueryNode node , IQueryVisitorContext context )
19
+ public static Task < Aggregation > GetDefaultAggregationAsync ( this IQueryNode node , IQueryVisitorContext context )
19
20
{
20
21
if ( node is GroupNode groupNode )
21
22
return groupNode . GetDefaultAggregationAsync ( context ) ;
@@ -26,7 +27,7 @@ public static Task<AggregationBase> GetDefaultAggregationAsync(this IQueryNode n
26
27
return null ;
27
28
}
28
29
29
- public static async Task < AggregationBase > GetDefaultAggregationAsync ( this GroupNode node , IQueryVisitorContext context )
30
+ public static async Task < Aggregation > GetDefaultAggregationAsync ( this GroupNode node , IQueryVisitorContext context )
30
31
{
31
32
if ( context is not IElasticQueryVisitorContext elasticContext )
32
33
throw new ArgumentException ( "Context must be of type IElasticQueryVisitorContext" , nameof ( context ) ) ;
@@ -47,29 +48,34 @@ public static async Task<AggregationBase> GetDefaultAggregationAsync(this GroupN
47
48
return GetHistogramAggregation ( "histogram_" + originalField , field , node . UnescapedProximity , node . UnescapedBoost , context ) ;
48
49
49
50
case AggregationType . GeoHashGrid :
50
- var precision = GeoHashPrecision . Precision1 ;
51
- if ( ! String . IsNullOrEmpty ( node . UnescapedProximity ) )
52
- Enum . TryParse ( node . UnescapedProximity , out precision ) ;
51
+ var precision = new GeohashPrecision ( 1 ) ;
52
+ if ( ! String . IsNullOrEmpty ( node . UnescapedProximity ) && Double . TryParse ( node . UnescapedProximity , out double parsedPrecision ) )
53
+ {
54
+ if ( parsedPrecision is < 1 or > 12 )
55
+ throw new ArgumentOutOfRangeException ( nameof ( node . UnescapedProximity ) , "Precision must be between 1 and 12" ) ;
56
+
57
+ precision = new GeohashPrecision ( parsedPrecision ) ;
58
+ }
53
59
54
- return new GeoHashGridAggregation ( "geogrid_" + originalField )
60
+ return new Aggregation ( "geogrid_" + originalField , new GeohashGridAggregation
55
61
{
56
62
Field = field ,
57
63
Precision = precision ,
58
64
Aggregations = new AverageAggregation ( "avg_lat" , null )
59
65
{
60
- Script = new InlineScript ( $ "doc['{ node . Field } '].lat")
66
+ Script = new Script { Source = $ "doc['{ node . Field } '].lat" }
61
67
} && new AverageAggregation ( "avg_lon" , null )
62
68
{
63
- Script = new InlineScript ( $ "doc['{ node . Field } '].lon")
69
+ Script = new Script { Source = $ "doc['{ node . Field } '].lon" }
64
70
}
65
- } ;
71
+ } ) ;
66
72
67
73
case AggregationType . Terms :
68
74
var agg = new TermsAggregation ( "terms_" + originalField )
69
75
{
70
76
Field = field ,
71
77
Size = node . GetProximityAsInt32 ( ) ,
72
- MinimumDocumentCount = node . GetBoostAsInt32 ( ) ,
78
+ MinDocCount = node . GetBoostAsInt32 ( ) ,
73
79
Meta = new Dictionary < string , object > { { "@field_type" , property ? . Type } }
74
80
} ;
75
81
@@ -85,7 +91,7 @@ public static async Task<AggregationBase> GetDefaultAggregationAsync(this GroupN
85
91
return null ;
86
92
}
87
93
88
- public static async Task < AggregationBase > GetDefaultAggregationAsync ( this TermNode node , IQueryVisitorContext context )
94
+ public static async Task < Aggregation > GetDefaultAggregationAsync ( this TermNode node , IQueryVisitorContext context )
89
95
{
90
96
if ( context is not IElasticQueryVisitorContext elasticContext )
91
97
throw new ArgumentException ( "Context must be of type IElasticQueryVisitorContext" , nameof ( context ) ) ;
@@ -134,20 +140,25 @@ public static async Task<AggregationBase> GetDefaultAggregationAsync(this TermNo
134
140
return GetPercentilesAggregation ( "percentiles_" + originalField , aggField , node . UnescapedProximity , node . UnescapedBoost , context ) ;
135
141
136
142
case AggregationType . GeoHashGrid :
137
- var precision = GeoHashPrecision . Precision1 ;
138
- if ( ! String . IsNullOrEmpty ( node . UnescapedProximity ) )
139
- Enum . TryParse ( node . UnescapedProximity , out precision ) ;
143
+ var precision = new GeohashPrecision ( 1 ) ;
144
+ if ( ! String . IsNullOrEmpty ( node . UnescapedProximity ) && Double . TryParse ( node . UnescapedProximity , out double parsedPrecision ) )
145
+ {
146
+ if ( parsedPrecision is < 1 or > 12 )
147
+ throw new ArgumentOutOfRangeException ( nameof ( node . UnescapedProximity ) , "Precision must be between 1 and 12" ) ;
148
+
149
+ precision = new GeohashPrecision ( parsedPrecision ) ;
150
+ }
140
151
141
- return new GeoHashGridAggregation ( "geogrid_" + originalField )
152
+ return new GeohashGridAggregation ( "geogrid_" + originalField )
142
153
{
143
154
Field = aggField ,
144
155
Precision = precision ,
145
156
Aggregations = new AverageAggregation ( "avg_lat" , null )
146
157
{
147
- Script = new InlineScript ( $ "doc['{ node . Field } '].lat")
158
+ Script = new Script { Source = $ "doc['{ node . Field } '].lat" }
148
159
} && new AverageAggregation ( "avg_lon" , null )
149
160
{
150
- Script = new InlineScript ( $ "doc['{ node . Field } '].lon")
161
+ Script = new Script { Source = $ "doc['{ node . Field } '].lon" }
151
162
}
152
163
} ;
153
164
@@ -156,7 +167,7 @@ public static async Task<AggregationBase> GetDefaultAggregationAsync(this TermNo
156
167
{
157
168
Field = aggField ,
158
169
Size = node . GetProximityAsInt32 ( ) ,
159
- MinimumDocumentCount = node . GetBoostAsInt32 ( ) ,
170
+ MinDocCount = node . GetBoostAsInt32 ( ) ,
160
171
Meta = new Dictionary < string , object > { { "@field_type" , property ? . Type } }
161
172
} ;
162
173
@@ -169,7 +180,7 @@ public static async Task<AggregationBase> GetDefaultAggregationAsync(this TermNo
169
180
return null ;
170
181
}
171
182
172
- private static AggregationBase GetPercentilesAggregation ( string originalField , string field , string proximity , string boost , IQueryVisitorContext context )
183
+ private static Aggregation GetPercentilesAggregation ( string originalField , string field , string proximity , string boost , IQueryVisitorContext context )
173
184
{
174
185
List < double > percents = null ;
175
186
if ( ! String . IsNullOrWhiteSpace ( proximity ) )
@@ -189,7 +200,7 @@ private static AggregationBase GetPercentilesAggregation(string originalField, s
189
200
} ;
190
201
}
191
202
192
- private static AggregationBase GetHistogramAggregation ( string originalField , string field , string proximity , string boost , IQueryVisitorContext context )
203
+ private static Aggregation GetHistogramAggregation ( string originalField , string field , string proximity , string boost , IQueryVisitorContext context )
193
204
{
194
205
double interval = 50 ;
195
206
if ( Double . TryParse ( proximity , out double prox ) )
@@ -198,25 +209,25 @@ private static AggregationBase GetHistogramAggregation(string originalField, str
198
209
return new HistogramAggregation ( originalField )
199
210
{
200
211
Field = field ,
201
- MinimumDocumentCount = 0 ,
212
+ MinDocCount = 0 ,
202
213
Interval = interval
203
214
} ;
204
215
}
205
216
206
- private static AggregationBase GetDateHistogramAggregation ( string originalField , string field , string proximity , string boost , IQueryVisitorContext context )
217
+ private static Aggregation GetDateHistogramAggregation ( string originalField , string field , string proximity , string boost , IQueryVisitorContext context )
207
218
{
208
219
// NOTE: StartDate and EndDate are set in the Repositories QueryBuilderContext.
209
220
var start = context . GetDate ( "StartDate" ) ;
210
221
var end = context . GetDate ( "EndDate" ) ;
211
222
bool isValidRange = start . HasValue && start . Value > DateTime . MinValue && end . HasValue && end . Value < DateTime . MaxValue && start . Value <= end . Value ;
212
- var bounds = isValidRange ? new ExtendedBounds < DateMath > { Minimum = start . Value , Maximum = end . Value } : null ;
223
+ var bounds = isValidRange ? new ExtendedBoundsDate { Min = start . Value , Max = end . Value } : null ;
213
224
214
225
var interval = GetInterval ( proximity , start , end ) ;
215
226
string timezone = TryConvertTimeUnitToUtcOffset ( boost ) ;
216
227
var agg = new DateHistogramAggregation ( originalField )
217
228
{
218
229
Field = field ,
219
- MinimumDocumentCount = 0 ,
230
+ MinDocCount = 0 ,
220
231
Format = "date_optional_time" ,
221
232
TimeZone = timezone ,
222
233
Meta = ! String . IsNullOrEmpty ( boost ) ? new Dictionary < string , object > { { "@timezone" , boost } } : null ,
@@ -247,55 +258,55 @@ private static string TryConvertTimeUnitToUtcOffset(string boost)
247
258
return "+" + timezoneOffset . Value . ToString ( "hh\\ :mm" ) ;
248
259
}
249
260
250
- private static Union < DateInterval , Time > GetInterval ( string proximity , DateTime ? start , DateTime ? end )
261
+ private static Union < CalendarInterval , Duration > GetInterval ( string proximity , DateTime ? start , DateTime ? end )
251
262
{
252
263
if ( String . IsNullOrEmpty ( proximity ) )
253
264
return GetInterval ( start , end ) ;
254
265
255
266
return proximity . Trim ( ) switch
256
267
{
257
- "s" or "1s" or "second" => DateInterval . Second ,
258
- "m" or "1m" or "minute" => DateInterval . Minute ,
259
- "h" or "1h" or "hour" => DateInterval . Hour ,
260
- "d" or "1d" or "day" => DateInterval . Day ,
261
- "w" or "1w" or "week" => DateInterval . Week ,
262
- "M" or "1M" or "month" => DateInterval . Month ,
263
- "q" or "1q" or "quarter" => DateInterval . Quarter ,
264
- "y" or "1y" or "year" => DateInterval . Year ,
265
- _ => new Union < DateInterval , Time > ( proximity ) ,
268
+ "s" or "1s" or "second" => CalendarInterval . Second ,
269
+ "m" or "1m" or "minute" => CalendarInterval . Minute ,
270
+ "h" or "1h" or "hour" => CalendarInterval . Hour ,
271
+ "d" or "1d" or "day" => CalendarInterval . Day ,
272
+ "w" or "1w" or "week" => CalendarInterval . Week ,
273
+ "M" or "1M" or "month" => CalendarInterval . Month ,
274
+ "q" or "1q" or "quarter" => CalendarInterval . Quarter ,
275
+ "y" or "1y" or "year" => CalendarInterval . Year ,
276
+ _ => new Union < CalendarInterval , Duration > ( proximity ) ,
266
277
} ;
267
278
}
268
279
269
- private static Union < DateInterval , Time > GetInterval ( DateTime ? utcStart , DateTime ? utcEnd , int desiredDataPoints = 100 )
280
+ private static Union < CalendarInterval , Duration > GetInterval ( DateTime ? utcStart , DateTime ? utcEnd , int desiredDataPoints = 100 )
270
281
{
271
282
if ( ! utcStart . HasValue || ! utcEnd . HasValue || utcStart . Value == DateTime . MinValue )
272
- return DateInterval . Day ;
283
+ return CalendarInterval . Day ;
273
284
274
285
var totalTime = utcEnd . Value - utcStart . Value ;
275
286
var timePerBlock = TimeSpan . FromMinutes ( totalTime . TotalMinutes / desiredDataPoints ) ;
276
287
if ( timePerBlock . TotalDays > 1 )
277
288
{
278
289
timePerBlock = timePerBlock . Round ( TimeSpan . FromDays ( 1 ) ) ;
279
- return ( Time ) timePerBlock ;
290
+ return ( Duration ) timePerBlock ;
280
291
}
281
292
282
293
if ( timePerBlock . TotalHours > 1 )
283
294
{
284
295
timePerBlock = timePerBlock . Round ( TimeSpan . FromHours ( 1 ) ) ;
285
- return ( Time ) timePerBlock ;
296
+ return ( Duration ) timePerBlock ;
286
297
}
287
298
288
299
if ( timePerBlock . TotalMinutes > 1 )
289
300
{
290
301
timePerBlock = timePerBlock . Round ( TimeSpan . FromMinutes ( 1 ) ) ;
291
- return ( Time ) timePerBlock ;
302
+ return ( Duration ) timePerBlock ;
292
303
}
293
304
294
305
timePerBlock = timePerBlock . Round ( TimeSpan . FromSeconds ( 15 ) ) ;
295
306
if ( timePerBlock . TotalSeconds < 1 )
296
307
timePerBlock = TimeSpan . FromSeconds ( 15 ) ;
297
308
298
- return ( Time ) timePerBlock ;
309
+ return ( Duration ) timePerBlock ;
299
310
}
300
311
301
312
public static int ? GetProximityAsInt32 ( this IFieldQueryWithProximityAndBoostNode node )
0 commit comments