@@ -22,6 +22,8 @@ import zio.json.ast.Json
2222import zio .json .ast .Json .Obj
2323import zio .json .{DeriveJsonDecoder , JsonDecoder , jsonField }
2424
25+ private [elasticsearch] sealed trait AggregationBucket
26+
2527sealed trait AggregationResponse
2628
2729object AggregationResponse {
@@ -68,6 +70,13 @@ object AggregationResponse {
6870 lowerSampling = stdDeviationBoundsResponse.lowerSampling
6971 )
7072 )
73+ case FilterAggregationResponse (docCount, subAggregations) =>
74+ FilterAggregationResult (
75+ docCount = docCount,
76+ subAggregations = subAggregations.fold(Map [String , AggregationResult ]())(_.map { case (key, response) =>
77+ (key, toResult(response))
78+ })
79+ )
7180 case MaxAggregationResponse (value) =>
7281 MaxAggregationResult (value)
7382 case MinAggregationResponse (value) =>
@@ -142,6 +151,123 @@ private[elasticsearch] object ExtendedStatsAggregationResponse {
142151 DeriveJsonDecoder .gen[ExtendedStatsAggregationResponse ]
143152}
144153
154+ private [elasticsearch] final case class FilterAggregationResponse (
155+ @ jsonField(" doc_count" )
156+ docCount : Int ,
157+ subAggregations : Option [Map [String , AggregationResponse ]] = None
158+ ) extends AggregationResponse
159+
160+ private [elasticsearch] object FilterAggregationResponse extends JsonDecoderOps {
161+ implicit val decoder : JsonDecoder [FilterAggregationResponse ] = Obj .decoder.mapOrFail { case Obj (fields) =>
162+ val allFields = fields.flatMap { case (field, data) =>
163+ field match {
164+ case " doc_count" =>
165+ Some (field -> data.unsafeAs[Int ])
166+ case _ =>
167+ val objFields = data.unsafeAs[Obj ].fields.toMap
168+
169+ (field : @ unchecked) match {
170+ case str if str.contains(" weighted_avg#" ) =>
171+ Some (field -> WeightedAvgAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
172+ case str if str.contains(" avg#" ) =>
173+ Some (field -> AvgAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
174+ case str if str.contains(" cardinality#" ) =>
175+ Some (field -> CardinalityAggregationResponse (value = objFields(" value" ).unsafeAs[Int ]))
176+ case str if str.contains(" extended_stats#" ) =>
177+ Some (
178+ field -> ExtendedStatsAggregationResponse (
179+ count = objFields(" count" ).unsafeAs[Int ],
180+ min = objFields(" min" ).unsafeAs[Double ],
181+ max = objFields(" max" ).unsafeAs[Double ],
182+ avg = objFields(" avg" ).unsafeAs[Double ],
183+ sum = objFields(" sum" ).unsafeAs[Double ],
184+ sumOfSquares = objFields(" sum_of_squares" ).unsafeAs[Double ],
185+ variance = objFields(" variance" ).unsafeAs[Double ],
186+ variancePopulation = objFields(" variance_population" ).unsafeAs[Double ],
187+ varianceSampling = objFields(" variance_sampling" ).unsafeAs[Double ],
188+ stdDeviation = objFields(" std_deviation" ).unsafeAs[Double ],
189+ stdDeviationPopulation = objFields(" std_deviation_population" ).unsafeAs[Double ],
190+ stdDeviationSampling = objFields(" std_deviation_sampling" ).unsafeAs[Double ],
191+ stdDeviationBoundsResponse = objFields(" std_deviation_sampling" ).unsafeAs[StdDeviationBoundsResponse ](
192+ StdDeviationBoundsResponse .decoder
193+ )
194+ )
195+ )
196+ case str if str.contains(" filter#" ) =>
197+ Some (field -> data.unsafeAs[FilterAggregationResponse ](FilterAggregationResponse .decoder))
198+ case str if str.contains(" max#" ) =>
199+ Some (field -> MaxAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
200+ case str if str.contains(" min#" ) =>
201+ Some (field -> MinAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
202+ case str if str.contains(" missing#" ) =>
203+ Some (field -> MissingAggregationResponse (docCount = objFields(" doc_count" ).unsafeAs[Int ]))
204+ case str if str.contains(" percentiles#" ) =>
205+ Some (field -> PercentilesAggregationResponse (values = objFields(" values" ).unsafeAs[Map [String , Double ]]))
206+ case str if str.contains(" stats#" ) =>
207+ Some (
208+ field -> StatsAggregationResponse (
209+ count = objFields(" count" ).unsafeAs[Int ],
210+ min = objFields(" min" ).unsafeAs[Double ],
211+ max = objFields(" max" ).unsafeAs[Double ],
212+ avg = objFields(" avg" ).unsafeAs[Double ],
213+ sum = objFields(" sum" ).unsafeAs[Double ]
214+ )
215+ )
216+ case str if str.contains(" sum#" ) =>
217+ Some (field -> SumAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
218+ case str if str.contains(" terms#" ) =>
219+ Some (field -> data.unsafeAs[TermsAggregationResponse ](TermsAggregationResponse .decoder))
220+ case str if str.contains(" value_count#" ) =>
221+ Some (field -> ValueCountAggregationResponse (value = objFields(" value" ).unsafeAs[Int ]))
222+ }
223+ }
224+ }.toMap
225+
226+ val docCount = allFields(" doc_count" ).asInstanceOf [Int ]
227+ val subAggs = allFields.collect {
228+ case (field, data) if field != " doc_count" =>
229+ (field : @ unchecked) match {
230+ case str if str.contains(" weighted_avg#" ) =>
231+ (field.split(" #" )(1 ), data.asInstanceOf [WeightedAvgAggregationResponse ])
232+ case str if str.contains(" avg#" ) =>
233+ (field.split(" #" )(1 ), data.asInstanceOf [AvgAggregationResponse ])
234+ case str if str.contains(" cardinality#" ) =>
235+ (field.split(" #" )(1 ), data.asInstanceOf [CardinalityAggregationResponse ])
236+ case str if str.contains(" extended_stats#" ) =>
237+ (field.split(" #" )(1 ), data.asInstanceOf [ExtendedStatsAggregationResponse ])
238+ case str if str.contains(" filter#" ) =>
239+ (field.split(" #" )(1 ), data.asInstanceOf [FilterAggregationResponse ])
240+ case str if str.contains(" max#" ) =>
241+ (field.split(" #" )(1 ), data.asInstanceOf [MaxAggregationResponse ])
242+ case str if str.contains(" min#" ) =>
243+ (field.split(" #" )(1 ), data.asInstanceOf [MinAggregationResponse ])
244+ case str if str.contains(" missing#" ) =>
245+ (field.split(" #" )(1 ), data.asInstanceOf [MissingAggregationResponse ])
246+ case str if str.contains(" percentiles#" ) =>
247+ (field.split(" #" )(1 ), data.asInstanceOf [PercentilesAggregationResponse ])
248+ case str if str.contains(" stats#" ) =>
249+ (field.split(" #" )(1 ), data.asInstanceOf [StatsAggregationResponse ])
250+ case str if str.contains(" sum#" ) =>
251+ (field.split(" #" )(1 ), data.asInstanceOf [SumAggregationResponse ])
252+ case str if str.contains(" terms#" ) =>
253+ (field.split(" #" )(1 ), data.asInstanceOf [TermsAggregationResponse ])
254+ case str if str.contains(" value_count#" ) =>
255+ (field.split(" #" )(1 ), data.asInstanceOf [ValueCountAggregationResponse ])
256+ }
257+ }
258+ Right (FilterAggregationResponse .apply(docCount, Option (subAggs).filter(_.nonEmpty)))
259+ }
260+ }
261+
262+ private [elasticsearch] sealed trait JsonDecoderOps {
263+ implicit class JsonDecoderOps (json : Json ) {
264+ def unsafeAs [A ](implicit decoder : JsonDecoder [A ]): A =
265+ (json.as[A ]: @ unchecked) match {
266+ case Right (decoded) => decoded
267+ }
268+ }
269+ }
270+
145271private [elasticsearch] final case class MaxAggregationResponse (value : Double ) extends AggregationResponse
146272
147273private [elasticsearch] object MaxAggregationResponse {
@@ -217,16 +343,14 @@ private[elasticsearch] object TermsAggregationResponse {
217343 implicit val decoder : JsonDecoder [TermsAggregationResponse ] = DeriveJsonDecoder .gen[TermsAggregationResponse ]
218344}
219345
220- private [elasticsearch] sealed trait AggregationBucket
221-
222346private [elasticsearch] final case class TermsAggregationBucket (
223347 key : String ,
224348 @ jsonField(" doc_count" )
225349 docCount : Int ,
226350 subAggregations : Option [Map [String , AggregationResponse ]] = None
227351) extends AggregationBucket
228352
229- private [elasticsearch] object TermsAggregationBucket {
353+ private [elasticsearch] object TermsAggregationBucket extends JsonDecoderOps {
230354 implicit val decoder : JsonDecoder [TermsAggregationBucket ] = Obj .decoder.mapOrFail { case Obj (fields) =>
231355 val allFields = fields.flatMap { case (field, data) =>
232356 field match {
@@ -264,6 +388,8 @@ private[elasticsearch] object TermsAggregationBucket {
264388 )
265389 )
266390 )
391+ case str if str.contains(" filter#" ) =>
392+ Some (field -> data.unsafeAs[FilterAggregationResponse ](FilterAggregationResponse .decoder))
267393 case str if str.contains(" max#" ) =>
268394 Some (field -> MaxAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
269395 case str if str.contains(" min#" ) =>
@@ -285,15 +411,7 @@ private[elasticsearch] object TermsAggregationBucket {
285411 case str if str.contains(" sum#" ) =>
286412 Some (field -> SumAggregationResponse (value = objFields(" value" ).unsafeAs[Double ]))
287413 case str if str.contains(" terms#" ) =>
288- Some (
289- field -> TermsAggregationResponse (
290- docErrorCount = objFields(" doc_count_error_upper_bound" ).unsafeAs[Int ],
291- sumOtherDocCount = objFields(" sum_other_doc_count" ).unsafeAs[Int ],
292- buckets = objFields(" buckets" )
293- .unsafeAs[Chunk [Json ]]
294- .map(_.unsafeAs[TermsAggregationBucket ](TermsAggregationBucket .decoder))
295- )
296- )
414+ Some (field -> data.unsafeAs[TermsAggregationResponse ](TermsAggregationResponse .decoder))
297415 case str if str.contains(" value_count#" ) =>
298416 Some (field -> ValueCountAggregationResponse (value = objFields(" value" ).unsafeAs[Int ]))
299417 }
@@ -313,6 +431,8 @@ private[elasticsearch] object TermsAggregationBucket {
313431 (field.split(" #" )(1 ), data.asInstanceOf [CardinalityAggregationResponse ])
314432 case str if str.contains(" extended_stats#" ) =>
315433 (field.split(" #" )(1 ), data.asInstanceOf [ExtendedStatsAggregationResponse ])
434+ case str if str.contains(" filter#" ) =>
435+ (field.split(" #" )(1 ), data.asInstanceOf [FilterAggregationResponse ])
316436 case str if str.contains(" max#" ) =>
317437 (field.split(" #" )(1 ), data.asInstanceOf [MaxAggregationResponse ])
318438 case str if str.contains(" min#" ) =>
@@ -331,16 +451,8 @@ private[elasticsearch] object TermsAggregationBucket {
331451 (field.split(" #" )(1 ), data.asInstanceOf [ValueCountAggregationResponse ])
332452 }
333453 }
334-
335454 Right (TermsAggregationBucket .apply(key, docCount, Option (subAggs).filter(_.nonEmpty)))
336455 }
337-
338- final implicit class JsonDecoderOps (json : Json ) {
339- def unsafeAs [A ](implicit decoder : JsonDecoder [A ]): A =
340- (json.as[A ]: @ unchecked) match {
341- case Right (decoded) => decoded
342- }
343- }
344456}
345457
346458private [elasticsearch] final case class ValueCountAggregationResponse (value : Int ) extends AggregationResponse
0 commit comments