diff --git a/quesma/model/bucket_aggregations/dateRange.go b/quesma/model/bucket_aggregations/dateRange.go index 3b17584bf..8c79d4519 100644 --- a/quesma/model/bucket_aggregations/dateRange.go +++ b/quesma/model/bucket_aggregations/dateRange.go @@ -84,7 +84,7 @@ func (query DateRange) IsBucketAggregation() bool { return true } -func (query DateRange) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query DateRange) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) != 1 { logger.ErrorWithCtx(query.ctx).Msgf("unexpected number of rows in date_range aggregation response, len: %d", len(rows)) return nil @@ -104,7 +104,9 @@ func (query DateRange) TranslateSqlResponseToJson(rows []model.QueryResultRow, l response = append(response, responseForInterval) columnIdx = nextColumnIdx } - return response + return model.JsonMap{ + "buckets": response, + } } func (query DateRange) String() string { diff --git a/quesma/model/bucket_aggregations/date_histogram.go b/quesma/model/bucket_aggregations/date_histogram.go index ac82a44e0..ab24a1877 100644 --- a/quesma/model/bucket_aggregations/date_histogram.go +++ b/quesma/model/bucket_aggregations/date_histogram.go @@ -53,7 +53,7 @@ func (query *DateHistogram) IsBucketAggregation() bool { return true } -func (query *DateHistogram) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query *DateHistogram) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) > 0 && len(rows[0].Cols) < 2 { logger.ErrorWithCtx(query.ctx).Msgf( "unexpected number of columns in date_histogram aggregation response, len(rows[0].Cols): "+ @@ -77,7 +77,9 @@ func (query *DateHistogram) TranslateSqlResponseToJson(rows []model.QueryResultR "key_as_string": intervalStart, }) } - return response + return model.JsonMap{ + "buckets": response, + } } func (query *DateHistogram) String() string { diff --git a/quesma/model/bucket_aggregations/date_histogram_test.go b/quesma/model/bucket_aggregations/date_histogram_test.go index d6a7346c3..242da6d39 100644 --- a/quesma/model/bucket_aggregations/date_histogram_test.go +++ b/quesma/model/bucket_aggregations/date_histogram_test.go @@ -14,9 +14,11 @@ func TestTranslateSqlResponseToJson(t *testing.T) { {Cols: []model.QueryResultCol{model.NewQueryResultCol("key", int64(56962370)), model.NewQueryResultCol("doc_count", 14)}}, } interval := "30s" - expectedResponse := []model.JsonMap{ - {"key": int64(56962398) * 30_000, "doc_count": 8, "key_as_string": "2024-02-25T14:39:00.000"}, - {"key": int64(56962370) * 30_000, "doc_count": 14, "key_as_string": "2024-02-25T14:25:00.000"}, + expectedResponse := model.JsonMap{ + "buckets": []model.JsonMap{ + {"key": int64(56962398) * 30_000, "doc_count": 8, "key_as_string": "2024-02-25T14:39:00.000"}, + {"key": int64(56962370) * 30_000, "doc_count": 14, "key_as_string": "2024-02-25T14:25:00.000"}, + }, } response := (&DateHistogram{interval: interval, intervalType: DateHistogramFixedInterval}).TranslateSqlResponseToJson(resultRows, 1) assert.Equal(t, expectedResponse, response) diff --git a/quesma/model/bucket_aggregations/filters.go b/quesma/model/bucket_aggregations/filters.go index 5160db215..7478e5c03 100644 --- a/quesma/model/bucket_aggregations/filters.go +++ b/quesma/model/bucket_aggregations/filters.go @@ -34,7 +34,7 @@ func (query Filters) IsBucketAggregation() bool { return true } -func (query Filters) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Filters) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { var value any = nil if len(rows) > 0 { if len(rows[0].Cols) > 0 { @@ -43,9 +43,9 @@ func (query Filters) TranslateSqlResponseToJson(rows []model.QueryResultRow, lev logger.ErrorWithCtx(query.ctx).Msgf("unexpected number of columns in filters aggregation response, len(rows[0].Cols): %d, level: %d", len(rows[0].Cols), level) } } - return []model.JsonMap{{ + return model.JsonMap{ "doc_count": value, - }} + } } func (query Filters) String() string { diff --git a/quesma/model/bucket_aggregations/geotile_grid.go b/quesma/model/bucket_aggregations/geotile_grid.go index c7b929ce5..f7d614ecc 100644 --- a/quesma/model/bucket_aggregations/geotile_grid.go +++ b/quesma/model/bucket_aggregations/geotile_grid.go @@ -21,7 +21,7 @@ func (query GeoTileGrid) IsBucketAggregation() bool { return true } -func (query GeoTileGrid) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query GeoTileGrid) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) > 0 && len(rows[0].Cols) < 3 { logger.ErrorWithCtx(query.ctx).Msgf( "unexpected number of columns in geotile_grid aggregation response, len(rows[0].Cols): "+ @@ -39,7 +39,9 @@ func (query GeoTileGrid) TranslateSqlResponseToJson(rows []model.QueryResultRow, "doc_count": row.LastColValue(), }) } - return response + return model.JsonMap{ + "buckets": response, + } } func (query GeoTileGrid) String() string { diff --git a/quesma/model/bucket_aggregations/histogram.go b/quesma/model/bucket_aggregations/histogram.go index 8b16fb4bd..2a1301362 100644 --- a/quesma/model/bucket_aggregations/histogram.go +++ b/quesma/model/bucket_aggregations/histogram.go @@ -23,7 +23,7 @@ func (query Histogram) IsBucketAggregation() bool { return true } -func (query Histogram) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Histogram) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) > 0 && len(rows[0].Cols) < 2 { logger.ErrorWithCtx(query.ctx).Msgf( "unexpected number of columns in histogram aggregation response, len(rows[0].Cols): "+ @@ -37,7 +37,9 @@ func (query Histogram) TranslateSqlResponseToJson(rows []model.QueryResultRow, l "doc_count": row.Cols[level].Value, }) } - return response + return model.JsonMap{ + "buckets": response, + } } func (query Histogram) String() string { diff --git a/quesma/model/bucket_aggregations/multi_terms.go b/quesma/model/bucket_aggregations/multi_terms.go index 763caac7d..4cd97ec32 100644 --- a/quesma/model/bucket_aggregations/multi_terms.go +++ b/quesma/model/bucket_aggregations/multi_terms.go @@ -23,12 +23,13 @@ func (query MultiTerms) IsBucketAggregation() bool { return true } -func (query MultiTerms) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) (response []model.JsonMap) { +func (query MultiTerms) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { minimumExpectedColNr := query.fieldsNr + 1 // +1 for doc_count. Can be more, if this MultiTerms has parent aggregations, but never fewer. if len(rows) > 0 && len(rows[0].Cols) < minimumExpectedColNr { logger.ErrorWithCtx(query.ctx).Msgf( "unexpected number of columns in terms aggregation response, len: %d, expected (at least): %d, rows[0]: %v", len(rows[0].Cols), minimumExpectedColNr, rows[0]) } + var response []model.JsonMap const delimiter = '|' // between keys in key_as_string for _, row := range rows { startIndex := len(row.Cols) - query.fieldsNr - 1 @@ -55,7 +56,10 @@ func (query MultiTerms) TranslateSqlResponseToJson(rows []model.QueryResultRow, } response = append(response, bucket) } - return + return model.JsonMap{ + "doc_count_error_upper_bound": 0, + "buckets": response, + } } func (query MultiTerms) String() string { diff --git a/quesma/model/bucket_aggregations/range.go b/quesma/model/bucket_aggregations/range.go index 71e8280d2..318ba2161 100644 --- a/quesma/model/bucket_aggregations/range.go +++ b/quesma/model/bucket_aggregations/range.go @@ -122,17 +122,17 @@ func (query Range) IsBucketAggregation() bool { return true } -func (query Range) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Range) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) != 1 { logger.ErrorWithCtx(query.ctx).Msgf("unexpected %d of rows in range aggregation response. Expected 1.", len(rows)) - return nil + return model.JsonMap{} } startIteration := len(rows[0].Cols) - 1 - len(query.Intervals) endIteration := len(rows[0].Cols) - 1 if startIteration >= endIteration || startIteration < 0 { logger.ErrorWithCtx(query.ctx).Msgf( "unexpected column nr in aggregation response, startIteration: %d, endIteration: %d", startIteration, endIteration) - return nil + return model.JsonMap{} } if query.Keyed { var response = make(model.JsonMap) @@ -140,7 +140,7 @@ func (query Range) TranslateSqlResponseToJson(rows []model.QueryResultRow, level responseForInterval := query.responseForInterval(query.Intervals[i], col.Value) response[query.Intervals[i].String()] = responseForInterval } - return []model.JsonMap{response} + return response } else { var response []model.JsonMap for i, col := range rows[0].Cols[startIteration:endIteration] { @@ -148,7 +148,9 @@ func (query Range) TranslateSqlResponseToJson(rows []model.QueryResultRow, level responseForInterval["key"] = query.Intervals[i].String() response = append(response, responseForInterval) } - return response + return model.JsonMap{ + "buckets": response, + } } } diff --git a/quesma/model/bucket_aggregations/terms.go b/quesma/model/bucket_aggregations/terms.go index 4e6b66b96..344727bc2 100644 --- a/quesma/model/bucket_aggregations/terms.go +++ b/quesma/model/bucket_aggregations/terms.go @@ -21,7 +21,7 @@ func (query Terms) IsBucketAggregation() bool { return true } -func (query Terms) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Terms) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { var response []model.JsonMap if len(rows) > 0 && len(rows[0].Cols) < 2 { logger.ErrorWithCtx(query.ctx).Msgf( @@ -39,7 +39,10 @@ func (query Terms) TranslateSqlResponseToJson(rows []model.QueryResultRow, level } response = append(response, bucket) } - return response + return model.JsonMap{ + "doc_count_error_upper_bound": 0, + "buckets": response, + } } func (query Terms) String() string { diff --git a/quesma/model/metrics_aggregations/avg.go b/quesma/model/metrics_aggregations/avg.go index 726a65414..3e40f232c 100644 --- a/quesma/model/metrics_aggregations/avg.go +++ b/quesma/model/metrics_aggregations/avg.go @@ -21,7 +21,7 @@ func (query Avg) IsBucketAggregation() bool { return false } -func (query Avg) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Avg) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return metricsTranslateSqlResponseToJsonWithFieldTypeCheck(query.ctx, rows, level, query.fieldType) } diff --git a/quesma/model/metrics_aggregations/cardinality.go b/quesma/model/metrics_aggregations/cardinality.go index fd042aa16..679e3bd2e 100644 --- a/quesma/model/metrics_aggregations/cardinality.go +++ b/quesma/model/metrics_aggregations/cardinality.go @@ -19,7 +19,7 @@ func (query Cardinality) IsBucketAggregation() bool { return false } -func (query Cardinality) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Cardinality) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return metricsTranslateSqlResponseToJson(query.ctx, rows, level) } diff --git a/quesma/model/metrics_aggregations/common.go b/quesma/model/metrics_aggregations/common.go index 908bd3308..cf8347e3f 100644 --- a/quesma/model/metrics_aggregations/common.go +++ b/quesma/model/metrics_aggregations/common.go @@ -10,20 +10,20 @@ import ( "time" ) -func metricsTranslateSqlResponseToJson(ctx context.Context, rows []model.QueryResultRow, level int) []model.JsonMap { +func metricsTranslateSqlResponseToJson(ctx context.Context, rows []model.QueryResultRow, level int) model.JsonMap { var value any = nil if resultRowsAreFine(ctx, rows) { value = rows[0].Cols[len(rows[0].Cols)-1].Value } - return []model.JsonMap{{ + return model.JsonMap{ "value": value, - }} + } } // metricsTranslateSqlResponseToJsonWithFieldTypeCheck is the same as metricsTranslateSqlResponseToJson for all types except DateTimes. // With DateTimes, we need to return 2 values, instead of 1, that's the difference. func metricsTranslateSqlResponseToJsonWithFieldTypeCheck( - ctx context.Context, rows []model.QueryResultRow, level int, fieldType clickhouse.DateTimeType) []model.JsonMap { + ctx context.Context, rows []model.QueryResultRow, level int, fieldType clickhouse.DateTimeType) model.JsonMap { if fieldType == clickhouse.Invalid { // if it's not a date, we do just a normal response return metricsTranslateSqlResponseToJson(ctx, rows, level) @@ -45,7 +45,7 @@ func metricsTranslateSqlResponseToJsonWithFieldTypeCheck( if value != nil { response["value_as_string"] = valueAsString } - return []model.JsonMap{response} + return response } func resultRowsAreFine(ctx context.Context, rows []model.QueryResultRow) bool { diff --git a/quesma/model/metrics_aggregations/count.go b/quesma/model/metrics_aggregations/count.go index 8b4011141..d06ce873c 100644 --- a/quesma/model/metrics_aggregations/count.go +++ b/quesma/model/metrics_aggregations/count.go @@ -20,15 +20,15 @@ func (query Count) IsBucketAggregation() bool { return false } -func (query Count) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { - var response []model.JsonMap +func (query Count) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for count aggregation") + return make(model.JsonMap, 0) } - for _, row := range rows { - response = append(response, model.JsonMap{"doc_count": row.Cols[level].Value}) + if len(rows) > 1 { + logger.WarnWithCtx(query.ctx).Msg("More than one row returned for count aggregation") } - return response + return model.JsonMap{"doc_count": rows[0].Cols[level].Value} } func (query Count) String() string { diff --git a/quesma/model/metrics_aggregations/extended_stats.go b/quesma/model/metrics_aggregations/extended_stats.go index 849a507a6..66aabb8f0 100644 --- a/quesma/model/metrics_aggregations/extended_stats.go +++ b/quesma/model/metrics_aggregations/extended_stats.go @@ -25,21 +25,21 @@ func (query ExtendedStats) IsBucketAggregation() bool { return false } -func (query ExtendedStats) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query ExtendedStats) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for stats aggregation") - return []model.JsonMap{{ + return model.JsonMap{ "value": nil, // not completely sure if it's a good return value, but it looks fine to me. We should always get 1 row, not 0 anyway. - }} + } } if len(rows) > 1 { logger.WarnWithCtx(query.ctx).Msgf("more than one row returned for stats aggregation, using only first. rows[0]: %+v, rows[1]: %+v", rows[0], rows[1]) } if len(rows[0].Cols) < selectFieldsNr { logger.WarnWithCtx(query.ctx).Msgf("not enough fields in the response for extended_stats aggregation. Expected at least %d, got %d. Got: %+v. Returning empty result.", selectFieldsNr, len(rows[0].Cols), rows[0]) - return []model.JsonMap{{ + return model.JsonMap{ "value": nil, // not completely sure if it's a good return value, but it looks fine to me. We should always get >= selectFieldsNr columns anyway. - }} + } } row := rows[0] @@ -56,7 +56,7 @@ func (query ExtendedStats) TranslateSqlResponseToJson(rows []model.QueryResultRo lowerSampling = avg - query.sigma*stdDevSampling } - return []model.JsonMap{{ + return model.JsonMap{ "count": query.getValue(row, "count"), "min": query.getValue(row, "min"), "max": query.getValue(row, "max"), @@ -77,7 +77,7 @@ func (query ExtendedStats) TranslateSqlResponseToJson(rows []model.QueryResultRo "upper_sampling": upperSampling, "lower_sampling": lowerSampling, }, - }} + } } func (query ExtendedStats) String() string { diff --git a/quesma/model/metrics_aggregations/geo_cetroid.go b/quesma/model/metrics_aggregations/geo_cetroid.go index b5c6c73bd..330cf8966 100644 --- a/quesma/model/metrics_aggregations/geo_cetroid.go +++ b/quesma/model/metrics_aggregations/geo_cetroid.go @@ -19,16 +19,14 @@ func (query GeoCentroid) IsBucketAggregation() bool { return false } -func (query GeoCentroid) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query GeoCentroid) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { location := model.JsonMap{ "lat": rows[0].Cols[3].Value, "lon": rows[0].Cols[4].Value, } - return []model.JsonMap{ - { - "count": rows[0].Cols[5].Value, - "location": location, - }, + return model.JsonMap{ + "count": rows[0].Cols[5].Value, + "location": location, } } diff --git a/quesma/model/metrics_aggregations/max.go b/quesma/model/metrics_aggregations/max.go index 14620a764..e632c433a 100644 --- a/quesma/model/metrics_aggregations/max.go +++ b/quesma/model/metrics_aggregations/max.go @@ -21,7 +21,7 @@ func (query Max) IsBucketAggregation() bool { return false } -func (query Max) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Max) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return metricsTranslateSqlResponseToJsonWithFieldTypeCheck(query.ctx, rows, level, query.fieldType) } diff --git a/quesma/model/metrics_aggregations/min.go b/quesma/model/metrics_aggregations/min.go index 2da40f6a2..44f9ab1dc 100644 --- a/quesma/model/metrics_aggregations/min.go +++ b/quesma/model/metrics_aggregations/min.go @@ -21,7 +21,7 @@ func (query Min) IsBucketAggregation() bool { return false } -func (query Min) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Min) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return metricsTranslateSqlResponseToJsonWithFieldTypeCheck(query.ctx, rows, level, query.fieldType) } diff --git a/quesma/model/metrics_aggregations/percentile_ranks.go b/quesma/model/metrics_aggregations/percentile_ranks.go index 1967641eb..0f98b8ea5 100644 --- a/quesma/model/metrics_aggregations/percentile_ranks.go +++ b/quesma/model/metrics_aggregations/percentile_ranks.go @@ -25,10 +25,10 @@ func (query PercentileRanks) IsBucketAggregation() bool { return false } -func (query PercentileRanks) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query PercentileRanks) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows in percentile ranks response") - return make([]model.JsonMap, 0) + return make(model.JsonMap, 0) } // I duplicate a lot of code in this if/else below, // but I think it's worth it, as this function might get called a lot of times for a single query. @@ -58,9 +58,9 @@ func (query PercentileRanks) TranslateSqlResponseToJson(rows []model.QueryResult percentileRank.Value, percentileRank.Value) } } - return []model.JsonMap{{ + return model.JsonMap{ "values": valueMap, - }} + } } else { buckets := make([]model.JsonMap, 0) for _, percentileRank := range rows[0].Cols[level:] { @@ -90,9 +90,9 @@ func (query PercentileRanks) TranslateSqlResponseToJson(rows []model.QueryResult percentileRank.Value, percentileRank.Value) } } - return []model.JsonMap{{ + return model.JsonMap{ "values": buckets, - }} + } } } diff --git a/quesma/model/metrics_aggregations/quantile.go b/quesma/model/metrics_aggregations/quantile.go index d391f6e08..f5a4b8642 100644 --- a/quesma/model/metrics_aggregations/quantile.go +++ b/quesma/model/metrics_aggregations/quantile.go @@ -28,7 +28,7 @@ func (query Quantile) IsBucketAggregation() bool { return false } -func (query Quantile) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Quantile) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { valueMap := make(model.JsonMap) valueAsStringMap := make(model.JsonMap) @@ -62,9 +62,9 @@ func (query Quantile) TranslateSqlResponseToJson(rows []model.QueryResultRow, le } if query.keyed { - return []model.JsonMap{{ + return model.JsonMap{ "values": valueMap, - }} + } } else { var values []model.JsonMap keysSorted := util.MapKeysSorted(valueMap) @@ -80,9 +80,9 @@ func (query Quantile) TranslateSqlResponseToJson(rows []model.QueryResultRow, le } values = append(values, responseValue) } - return []model.JsonMap{{ + return model.JsonMap{ "values": values, - }} + } } } @@ -148,9 +148,9 @@ func (query Quantile) processResult(colName string, percentileReturnedByClickhou return percentile, percentileAsString, percentileIsNanOrInvalid } -var emptyPercentilesResult = []model.JsonMap{{ +var emptyPercentilesResult = model.JsonMap{ "values": 0, -}} +} func (query Quantile) PostprocessResults(rowsFromDB []model.QueryResultRow) []model.QueryResultRow { return rowsFromDB diff --git a/quesma/model/metrics_aggregations/stats.go b/quesma/model/metrics_aggregations/stats.go index af6dc5b30..499d5f584 100644 --- a/quesma/model/metrics_aggregations/stats.go +++ b/quesma/model/metrics_aggregations/stats.go @@ -21,12 +21,12 @@ func (query Stats) IsBucketAggregation() bool { return false } -func (query Stats) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Stats) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for stats aggregation") - return []model.JsonMap{{ + return model.JsonMap{ "value": nil, // not completely sure if it's a good return value, but it looks fine to me. We should always get 1 row, not 0 anyway. - }} + } } if len(rows) > 1 { logger.WarnWithCtx(query.ctx).Msgf("more than one row returned for stats aggregation, using only first. rows[0]: %+v, rows[1]: %+v", rows[0], rows[1]) @@ -46,7 +46,7 @@ func (query Stats) TranslateSqlResponseToJson(rows []model.QueryResultRow, level withoutOrNull, _ := strings.CutSuffix(fullName, "OrNull") resultMap[withoutOrNull] = v.Value } - return []model.JsonMap{resultMap} + return resultMap } func (query Stats) String() string { diff --git a/quesma/model/metrics_aggregations/sum.go b/quesma/model/metrics_aggregations/sum.go index 5c17b987d..1947038d9 100644 --- a/quesma/model/metrics_aggregations/sum.go +++ b/quesma/model/metrics_aggregations/sum.go @@ -21,7 +21,7 @@ func (query Sum) IsBucketAggregation() bool { return false } -func (query Sum) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Sum) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return metricsTranslateSqlResponseToJsonWithFieldTypeCheck(query.ctx, rows, level, query.fieldType) } diff --git a/quesma/model/metrics_aggregations/top_hits.go b/quesma/model/metrics_aggregations/top_hits.go index 5eda4f4f9..2081d12eb 100644 --- a/quesma/model/metrics_aggregations/top_hits.go +++ b/quesma/model/metrics_aggregations/top_hits.go @@ -24,7 +24,7 @@ func (query TopHits) IsBucketAggregation() bool { } // TODO implement correct -func (query TopHits) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query TopHits) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { var topElems []any if len(rows) > 0 && level >= len(rows[0].Cols)-1 { // values are [level, len(row.Cols) - 1] @@ -83,9 +83,9 @@ func (query TopHits) TranslateSqlResponseToJson(rows []model.QueryResultRow, lev } topElems = append(topElems, elem) } - return []model.JsonMap{{ + return model.JsonMap{ "hits": topElems, - }} + } } func (query TopHits) String() string { diff --git a/quesma/model/metrics_aggregations/top_metrics.go b/quesma/model/metrics_aggregations/top_metrics.go index 1465449f7..4b348c137 100644 --- a/quesma/model/metrics_aggregations/top_metrics.go +++ b/quesma/model/metrics_aggregations/top_metrics.go @@ -23,7 +23,7 @@ func (query TopMetrics) IsBucketAggregation() bool { return false } -func (query TopMetrics) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query TopMetrics) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { var topElems []any if len(rows) > 0 && level >= len(rows[0].Cols)-1 { // values are [level, len(row.Cols) - 1] @@ -66,9 +66,9 @@ func (query TopMetrics) TranslateSqlResponseToJson(rows []model.QueryResultRow, } topElems = append(topElems, elem) } - return []model.JsonMap{{ + return model.JsonMap{ "top": topElems, - }} + } } func (query TopMetrics) String() string { diff --git a/quesma/model/metrics_aggregations/value_count.go b/quesma/model/metrics_aggregations/value_count.go index 130b158c8..b02a8eefb 100644 --- a/quesma/model/metrics_aggregations/value_count.go +++ b/quesma/model/metrics_aggregations/value_count.go @@ -20,16 +20,16 @@ func (query ValueCount) IsBucketAggregation() bool { return false } -func (query ValueCount) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query ValueCount) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { var value any = nil if len(rows) > 0 { value = rows[0].Cols[level].Value } else { logger.WarnWithCtx(query.ctx).Msg("Nn rows returned for value_count aggregation") } - return []model.JsonMap{{ + return model.JsonMap{ "value": value, - }} + } } func (query ValueCount) String() string { diff --git a/quesma/model/pipeline_aggregations/average_bucket.go b/quesma/model/pipeline_aggregations/average_bucket.go index ca73ddfb4..b8329379a 100644 --- a/quesma/model/pipeline_aggregations/average_bucket.go +++ b/quesma/model/pipeline_aggregations/average_bucket.go @@ -24,7 +24,7 @@ func (query AverageBucket) IsBucketAggregation() bool { return false } -func (query AverageBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query AverageBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return translateSqlResponseToJsonCommon(query.ctx, rows, query.String()) } diff --git a/quesma/model/pipeline_aggregations/bucket_script.go b/quesma/model/pipeline_aggregations/bucket_script.go index b1fad85cd..8ac71f334 100644 --- a/quesma/model/pipeline_aggregations/bucket_script.go +++ b/quesma/model/pipeline_aggregations/bucket_script.go @@ -20,16 +20,18 @@ func (query BucketScript) IsBucketAggregation() bool { return false } -func (query BucketScript) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query BucketScript) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for bucket script aggregation") - return []model.JsonMap{{"value": 0}} + return model.JsonMap{"value": 0} } var response []model.JsonMap for _, row := range rows { response = append(response, model.JsonMap{"value": row.Cols[level].Value}) } - return response + return model.JsonMap{ + "buckets": response, + } } func (query BucketScript) CalculateResultWhenMissing(*model.Query, []model.QueryResultRow) []model.QueryResultRow { diff --git a/quesma/model/pipeline_aggregations/common.go b/quesma/model/pipeline_aggregations/common.go index 0e4bcd631..3274bb7ce 100644 --- a/quesma/model/pipeline_aggregations/common.go +++ b/quesma/model/pipeline_aggregations/common.go @@ -33,16 +33,15 @@ func getKey(ctx context.Context, row model.QueryResultRow, query *model.Query) a // translateSqlResponseToJsonCommon translates rows from DB (maybe postprocessed later), into JSON's format in which // we want to return them. It is common for a lot of pipeline aggregations -func translateSqlResponseToJsonCommon(ctx context.Context, rows []model.QueryResultRow, aggregationName string) []model.JsonMap { +func translateSqlResponseToJsonCommon(ctx context.Context, rows []model.QueryResultRow, aggregationName string) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(ctx).Msgf("no rows returned for %s aggregation", aggregationName) - return []model.JsonMap{{}} + return model.JsonMap{} } - var response []model.JsonMap - for _, row := range rows { - response = append(response, model.JsonMap{"value": row.Cols[len(row.Cols)-1].Value}) + if len(rows) > 1 { + logger.WarnWithCtx(ctx).Msgf("More than one row returned for %s aggregation", aggregationName) } - return response + return model.JsonMap{"value": rows[0].Cols[len(rows[0].Cols)-1].Value} } // calculateResultWhenMissingCommonForDiffAggregations is common for derivative/serial diff aggregations diff --git a/quesma/model/pipeline_aggregations/cumulative_sum.go b/quesma/model/pipeline_aggregations/cumulative_sum.go index 9b1c68464..1d9d8fc87 100644 --- a/quesma/model/pipeline_aggregations/cumulative_sum.go +++ b/quesma/model/pipeline_aggregations/cumulative_sum.go @@ -33,7 +33,7 @@ func (query CumulativeSum) IsBucketAggregation() bool { return false } -func (query CumulativeSum) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query CumulativeSum) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return translateSqlResponseToJsonCommon(query.ctx, rows, query.String()) } diff --git a/quesma/model/pipeline_aggregations/derivative.go b/quesma/model/pipeline_aggregations/derivative.go index 0c06cf2e6..0027c4d09 100644 --- a/quesma/model/pipeline_aggregations/derivative.go +++ b/quesma/model/pipeline_aggregations/derivative.go @@ -27,7 +27,7 @@ func (query Derivative) IsBucketAggregation() bool { return false } -func (query Derivative) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Derivative) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return translateSqlResponseToJsonCommon(query.ctx, rows, query.String()) } diff --git a/quesma/model/pipeline_aggregations/max_bucket.go b/quesma/model/pipeline_aggregations/max_bucket.go index e86646679..f5e98c937 100644 --- a/quesma/model/pipeline_aggregations/max_bucket.go +++ b/quesma/model/pipeline_aggregations/max_bucket.go @@ -28,19 +28,19 @@ func (query MaxBucket) IsBucketAggregation() bool { // FIXME I think we should return all rows, not just 1 // Dunno why it's working, maybe I'm wrong. // Let's wait for this until all pipeline merges, when I'll perform some more thorough tests. -func (query MaxBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query MaxBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for max bucket aggregation") - return []model.JsonMap{nil} + return model.JsonMap{} } if len(rows) > 1 { logger.WarnWithCtx(query.ctx).Msg("more than one row returned for max bucket aggregation") } if returnMap, ok := rows[0].LastColValue().(model.JsonMap); ok { - return []model.JsonMap{returnMap} + return returnMap } else { logger.WarnWithCtx(query.ctx).Msgf("could not convert value to JsonMap: %v, type: %T", rows[0].LastColValue(), rows[0].LastColValue()) - return []model.JsonMap{nil} + return model.JsonMap{} } } diff --git a/quesma/model/pipeline_aggregations/min_bucket.go b/quesma/model/pipeline_aggregations/min_bucket.go index bf2d811ed..a89fc8985 100644 --- a/quesma/model/pipeline_aggregations/min_bucket.go +++ b/quesma/model/pipeline_aggregations/min_bucket.go @@ -25,19 +25,19 @@ func (query MinBucket) IsBucketAggregation() bool { return false } -func (query MinBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query MinBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for min bucket aggregation") - return []model.JsonMap{nil} + return model.JsonMap{} } if len(rows) > 1 { logger.WarnWithCtx(query.ctx).Msg("more than one row returned for min bucket aggregation") } if returnMap, ok := rows[0].LastColValue().(model.JsonMap); ok { - return []model.JsonMap{returnMap} + return returnMap } logger.WarnWithCtx(query.ctx).Msgf("could not convert value to JsonMap: %v, type: %T", rows[0].LastColValue(), rows[0].LastColValue()) - return []model.JsonMap{nil} + return model.JsonMap{} } func (query MinBucket) CalculateResultWhenMissing(qwa *model.Query, parentRows []model.QueryResultRow) []model.QueryResultRow { diff --git a/quesma/model/pipeline_aggregations/serial_diff.go b/quesma/model/pipeline_aggregations/serial_diff.go index 80afd3a98..128789d31 100644 --- a/quesma/model/pipeline_aggregations/serial_diff.go +++ b/quesma/model/pipeline_aggregations/serial_diff.go @@ -29,7 +29,7 @@ func (query SerialDiff) IsBucketAggregation() bool { return false } -func (query SerialDiff) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query SerialDiff) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { return translateSqlResponseToJsonCommon(query.ctx, rows, query.String()) } diff --git a/quesma/model/pipeline_aggregations/sum_bucket.go b/quesma/model/pipeline_aggregations/sum_bucket.go index 7ec2d99a1..bbf67f1b8 100644 --- a/quesma/model/pipeline_aggregations/sum_bucket.go +++ b/quesma/model/pipeline_aggregations/sum_bucket.go @@ -24,19 +24,19 @@ func (query SumBucket) IsBucketAggregation() bool { return false } -func (query SumBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query SumBucket) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { if len(rows) == 0 { logger.WarnWithCtx(query.ctx).Msg("no rows returned for average bucket aggregation") - return []model.JsonMap{nil} + return model.JsonMap{} } if len(rows) > 1 { logger.WarnWithCtx(query.ctx).Msg("more than one row returned for average bucket aggregation") } if returnMap, ok := rows[0].LastColValue().(model.JsonMap); ok { - return []model.JsonMap{returnMap} + return returnMap } logger.WarnWithCtx(query.ctx).Msgf("could not convert value to JsonMap: %v, type: %T", rows[0].LastColValue(), rows[0].LastColValue()) - return []model.JsonMap{nil} + return model.JsonMap{} } func (query SumBucket) CalculateResultWhenMissing(qwa *model.Query, parentRows []model.QueryResultRow) []model.QueryResultRow { diff --git a/quesma/model/pipeline_query_type.go b/quesma/model/pipeline_query_type.go index b30d05212..d36cd55be 100644 --- a/quesma/model/pipeline_query_type.go +++ b/quesma/model/pipeline_query_type.go @@ -9,7 +9,7 @@ type PipelineQueryType interface { // TranslateSqlResponseToJson 'level' - we want to translate [level:] (metrics aggr) or [level-1:] (bucket aggr) columns to JSON // Previous columns are used for bucketing. // For 'bucket' aggregation result is a slice of buckets, for 'metrics' aggregation it's a single bucket (only look at [0]) - TranslateSqlResponseToJson(rows []QueryResultRow, level int) []JsonMap + TranslateSqlResponseToJson(rows []QueryResultRow, level int) JsonMap // IsBucketAggregation if true, result from 'MakeResponse' will be a slice of buckets // if false, it's a metrics aggregation and result from 'MakeResponse' will be a single bucket diff --git a/quesma/model/query.go b/quesma/model/query.go index 855f536f8..f63ae301c 100644 --- a/quesma/model/query.go +++ b/quesma/model/query.go @@ -32,8 +32,8 @@ type ( QueryType interface { // TranslateSqlResponseToJson 'level' - we want to translate [level:] (metrics aggr) or [level-1:] (bucket aggr) columns to JSON // Previous columns are used for bucketing. - // For 'bucket' aggregation result is a slice of buckets, for 'metrics' aggregation it's a single bucket (only look at [0]) - TranslateSqlResponseToJson(rows []QueryResultRow, level int) []JsonMap + // For 'bucket' aggregation result is a map wrapped in 'buckets' key. + TranslateSqlResponseToJson(rows []QueryResultRow, level int) JsonMap PostprocessResults(rowsFromDB []QueryResultRow) (ultimateRows []QueryResultRow) @@ -172,8 +172,8 @@ func (query UnknownAggregationType) IsBucketAggregation() bool { return false } -func (query UnknownAggregationType) TranslateSqlResponseToJson(rows []QueryResultRow, level int) []JsonMap { - return make([]JsonMap, 0) +func (query UnknownAggregationType) TranslateSqlResponseToJson(rows []QueryResultRow, level int) JsonMap { + return make(JsonMap, 0) } func (query UnknownAggregationType) String() string { diff --git a/quesma/model/typical_queries/count.go b/quesma/model/typical_queries/count.go index 6adc3d9b8..7926acdf2 100644 --- a/quesma/model/typical_queries/count.go +++ b/quesma/model/typical_queries/count.go @@ -19,8 +19,8 @@ func (query Count) IsBucketAggregation() bool { return false } -func (query Count) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { - return make([]model.JsonMap, 0) +func (query Count) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { + return make(model.JsonMap, 0) } func (query Count) String() string { diff --git a/quesma/model/typical_queries/facets.go b/quesma/model/typical_queries/facets.go index fdd167798..c74816471 100644 --- a/quesma/model/typical_queries/facets.go +++ b/quesma/model/typical_queries/facets.go @@ -33,8 +33,8 @@ func (query Facets) IsBucketAggregation() bool { return true } -func (query Facets) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { - return []model.JsonMap{facetsTranslateSqlResponseToJson(query.ctx, rows)} +func (query Facets) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { + return facetsTranslateSqlResponseToJson(query.ctx, rows) } func (query Facets) String() string { diff --git a/quesma/model/typical_queries/facets_numeric.go b/quesma/model/typical_queries/facets_numeric.go index 965e2623e..258da2f91 100644 --- a/quesma/model/typical_queries/facets_numeric.go +++ b/quesma/model/typical_queries/facets_numeric.go @@ -37,7 +37,7 @@ func (query FacetsNumeric) IsBucketAggregation() bool { return true } -func (query FacetsNumeric) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query FacetsNumeric) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { aggregations := facetsTranslateSqlResponseToJson(query.ctx, rows) firstNotNullValueIndex := 0 @@ -86,7 +86,7 @@ func (query FacetsNumeric) TranslateSqlResponseToJson(rows []model.QueryResultRo } } - return []model.JsonMap{aggregations} + return aggregations } func (query FacetsNumeric) String() string { diff --git a/quesma/model/typical_queries/hits.go b/quesma/model/typical_queries/hits.go index 14b8c5f5f..438909ab3 100644 --- a/quesma/model/typical_queries/hits.go +++ b/quesma/model/typical_queries/hits.go @@ -45,7 +45,7 @@ func (query Hits) IsBucketAggregation() bool { return false } -func (query Hits) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) []model.JsonMap { +func (query Hits) TranslateSqlResponseToJson(rows []model.QueryResultRow, level int) model.JsonMap { hits := make([]model.SearchHit, 0, len(rows)) for i, row := range rows { hit := model.NewSearchHit(query.table.Name) @@ -71,7 +71,7 @@ func (query Hits) TranslateSqlResponseToJson(rows []model.QueryResultRow, level hits = append(hits, hit) } - return []model.JsonMap{{ + return model.JsonMap{ "hits": model.SearchHits{ Total: &model.Total{ Value: len(rows), @@ -84,7 +84,7 @@ func (query Hits) TranslateSqlResponseToJson(rows []model.QueryResultRow, level Successful: 1, Failed: 0, }, - }} + } } func (query Hits) addAndHighlightHit(hit *model.SearchHit, resultRow *model.QueryResultRow) { diff --git a/quesma/queryparser/aggregation_parser_test.go b/quesma/queryparser/aggregation_parser_test.go index 858221ca9..891805b73 100644 --- a/quesma/queryparser/aggregation_parser_test.go +++ b/quesma/queryparser/aggregation_parser_test.go @@ -751,9 +751,11 @@ func Test2AggregationParserExternalTestcases(t *testing.T) { actualMinusExpected, expectedMinusActual := util.MapDifference(response.Aggregations, expectedAggregationsPart, true, true) // probability and seed are present in random_sampler aggregation. I'd assume they are not needed, thus let's not care about it for now. - acceptableDifference := []string{"doc_count_error_upper_bound", "sum_other_doc_count", "probability", "seed", "bg_count", "doc_count", model.KeyAddedByQuesma} - // pp.Println("ACTUAL", actualMinusExpected) - // pp.Println("EXPECTED", expectedMinusActual) + acceptableDifference := []string{"sum_other_doc_count", "probability", "seed", "bg_count", "doc_count", model.KeyAddedByQuesma} + // pp.Println("ACTUAL diff", actualMinusExpected) + // pp.Println("EXPECTED diff", expectedMinusActual) + // pp.Println("ACTUAL", response.Aggregations) + // pp.Println("EXPECTED", expectedAggregationsPart) assert.True(t, util.AlmostEmpty(actualMinusExpected, acceptableDifference)) assert.True(t, util.AlmostEmpty(expectedMinusActual, acceptableDifference)) if body["track_total_hits"] == true { // FIXME some better check after track_total_hits diff --git a/quesma/queryparser/query_translator.go b/quesma/queryparser/query_translator.go index 2826bc042..88e278b91 100644 --- a/quesma/queryparser/query_translator.go +++ b/quesma/queryparser/query_translator.go @@ -81,14 +81,7 @@ func (cw *ClickhouseQueryTranslator) makeResponseAggregationRecursive(query *mod ResultSet []model.QueryResultRow, aggregatorsLevel, selectLevel int) model.JsonMap { // check if we finish if aggregatorsLevel == len(query.Aggregators) { - result := query.Type.TranslateSqlResponseToJson(ResultSet, selectLevel) - if !query.Type.IsBucketAggregation() || len(result) == 1 { - return result[0] - } else { - return model.JsonMap{ - "buckets": result, - } - } + return query.Type.TranslateSqlResponseToJson(ResultSet, selectLevel) } currentAggregator := query.Aggregators[aggregatorsLevel] @@ -122,20 +115,26 @@ func (cw *ClickhouseQueryTranslator) makeResponseAggregationRecursive(query *mod // normally it's just 1. It used to be just 1 before multi_terms aggregation, where we usually split over > 1 field qp := queryprocessor.NewQueryProcessor(cw.Ctx) weSplitOverHowManyFields := currentAggregator.SplitOverHowManyFields - buckets := qp.SplitResultSetIntoBuckets(ResultSet, selectLevel+weSplitOverHowManyFields) - for _, bucket := range buckets { - potentialNewBuckets := cw.makeResponseAggregationRecursive(query, bucket, aggregatorsLevel+1, selectLevel+weSplitOverHowManyFields) - if array, exist := potentialNewBuckets["buckets"]; exist { - for _, newBucket := range array.([]model.JsonMap) { - newBucket[model.KeyAddedByQuesma] = bucket[0].Cols[selectLevel].Value - bucketsReturnMap = append(bucketsReturnMap, newBucket) + + // leaf bucket aggregation + if aggregatorsLevel == len(query.Aggregators)-1 && query.Type.IsBucketAggregation() { + subResult = cw.makeResponseAggregationRecursive(query, ResultSet, aggregatorsLevel+1, selectLevel+weSplitOverHowManyFields) + if buckets, exist := subResult["buckets"]; exist { + for i, bucket := range buckets.([]model.JsonMap) { + if i < len(ResultSet) { + bucket[model.KeyAddedByQuesma] = ResultSet[i].Cols[selectLevel].Value + } } - } else { + } + } else { // need to split here into buckets + buckets := qp.SplitResultSetIntoBuckets(ResultSet, selectLevel+weSplitOverHowManyFields) + for _, bucket := range buckets { + potentialNewBuckets := cw.makeResponseAggregationRecursive(query, bucket, aggregatorsLevel+1, selectLevel+weSplitOverHowManyFields) potentialNewBuckets[model.KeyAddedByQuesma] = bucket[0].Cols[selectLevel].Value bucketsReturnMap = append(bucketsReturnMap, potentialNewBuckets) } + subResult["buckets"] = bucketsReturnMap } - subResult["buckets"] = bucketsReturnMap } _ = cw.addMetadataIfNeeded(query, subResult, aggregatorsLevel) @@ -207,7 +206,7 @@ func (cw *ClickhouseQueryTranslator) makeHits(queries []*model.Query, results [] } hitsPartOfResponse := hitsQuery.Type.TranslateSqlResponseToJson(hitsResultSet, 0) - hitsResponse := hitsPartOfResponse[0]["hits"].(model.SearchHits) + hitsResponse := hitsPartOfResponse["hits"].(model.SearchHits) return queriesWithoutHits, resultsWithoutHits, &hitsResponse } diff --git a/quesma/queryprocessor/query_processor.go b/quesma/queryprocessor/query_processor.go index 372960c1d..57f6a1f44 100644 --- a/quesma/queryprocessor/query_processor.go +++ b/quesma/queryprocessor/query_processor.go @@ -54,14 +54,12 @@ func (qp *QueryProcessor) SplitResultSetIntoBuckets(ResultSet []model.QueryResul return [][]model.QueryResultRow{{}} } - buckets := [][]model.QueryResultRow{{}} - curBucket := 0 lastRow := ResultSet[0] - for _, row := range ResultSet { + buckets := [][]model.QueryResultRow{{lastRow}} + for _, row := range ResultSet[1:] { if qp.sameGroupByFields(row, lastRow, level) { - buckets[curBucket] = append(buckets[curBucket], row) + buckets[len(buckets)-1] = append(buckets[len(buckets)-1], row) } else { - curBucket++ buckets = append(buckets, []model.QueryResultRow{row}) } lastRow = row diff --git a/quesma/testdata/aggregation_requests.go b/quesma/testdata/aggregation_requests.go index 6bf2d9960..039b07c6d 100644 --- a/quesma/testdata/aggregation_requests.go +++ b/quesma/testdata/aggregation_requests.go @@ -2755,7 +2755,8 @@ var AggregationTests = []AggregationTestCase{ "doc_count": 1757, "key": "User logged in" } - ] + ], + "doc_count_error_upper_bound": 0 } } }`, @@ -4104,7 +4105,8 @@ var AggregationTests = []AggregationTestCase{ "score": 206 } ], - "doc_count": 1608 + "doc_count": 1608, + "doc_count_error_upper_bound": 0 } }, "hits": { diff --git a/quesma/testdata/kibana-visualize/aggregation_requests.go b/quesma/testdata/kibana-visualize/aggregation_requests.go index 664ad739d..0c415f7d9 100644 --- a/quesma/testdata/kibana-visualize/aggregation_requests.go +++ b/quesma/testdata/kibana-visualize/aggregation_requests.go @@ -549,7 +549,7 @@ var AggregationTests = []testdata.AggregationTestCase{ "key_as_string": "critical|fedora" } ], - "doc_count_error_upper_bound": -1, + "doc_count_error_upper_bound": 0, "sum_other_doc_count": 121 } }, diff --git a/quesma/testdata/opensearch-visualize/aggregation_requests.go b/quesma/testdata/opensearch-visualize/aggregation_requests.go index 3de4646a2..7cf786eaf 100644 --- a/quesma/testdata/opensearch-visualize/aggregation_requests.go +++ b/quesma/testdata/opensearch-visualize/aggregation_requests.go @@ -731,7 +731,8 @@ var AggregationTests = []testdata.AggregationTestCase{ "key": "503", "score": 94 } - ] + ], + "doc_count_error_upper_bound": 0 } }, "hits": { @@ -891,7 +892,8 @@ var AggregationTests = []testdata.AggregationTestCase{ "score": 94 } ], - "doc_count": 2786 + "doc_count": 2786, + "doc_count_error_upper_bound": 0 } }, "hits": { @@ -1078,7 +1080,8 @@ var AggregationTests = []testdata.AggregationTestCase{ "score": 2570 } ], - "doc_count": 2786 + "doc_count": 2786, + "doc_count_error_upper_bound": 0 } }, "hits": { diff --git a/quesma/testdata/opensearch-visualize/pipeline_aggregation_requests.go b/quesma/testdata/opensearch-visualize/pipeline_aggregation_requests.go index d8c7bd0fc..68ff7453d 100644 --- a/quesma/testdata/opensearch-visualize/pipeline_aggregation_requests.go +++ b/quesma/testdata/opensearch-visualize/pipeline_aggregation_requests.go @@ -4588,7 +4588,8 @@ var PipelineAggregationTests = []testdata.AggregationTestCase{ "score": 76 } ], - "doc_count": 1865 + "doc_count": 1865, + "doc_count_error_upper_bound": 0 } }, "hits": {