Skip to content
This repository was archived by the owner on Nov 7, 2025. It is now read-only.

Commit 3f39643

Browse files
authored
[Grafana] Handle format parameter in date_histogram (#1322)
1 parent c2077a9 commit 3f39643

File tree

5 files changed

+102
-6
lines changed

5 files changed

+102
-6
lines changed

platform/model/bucket_aggregations/date_histogram.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type DateHistogram struct {
3434
field model.Expr // name of the field, e.g. timestamp
3535
interval string
3636
timezone string
37+
defaultFormat bool // how we format "key_as_string". If not default, it's milliseconds
3738
wantedTimezone *time.Location // key is in `timezone` time, and we need it to be UTC
3839
extendedBoundsMin int64
3940
extendedBoundsMax int64
@@ -42,7 +43,7 @@ type DateHistogram struct {
4243
fieldDateTimeType clickhouse.DateTimeType
4344
}
4445

45-
func NewDateHistogram(ctx context.Context, field model.Expr, interval, timezone string, minDocCount int,
46+
func NewDateHistogram(ctx context.Context, field model.Expr, interval, timezone, format string, minDocCount int,
4647
extendedBoundsMin, extendedBoundsMax int64, intervalType DateHistogramIntervalType, fieldDateTimeType clickhouse.DateTimeType) *DateHistogram {
4748

4849
wantedTimezone, err := time.LoadLocation(timezone)
@@ -51,9 +52,11 @@ func NewDateHistogram(ctx context.Context, field model.Expr, interval, timezone
5152
wantedTimezone = time.UTC
5253
}
5354

55+
defaultFormat := format != "epoch_millis"
56+
5457
return &DateHistogram{ctx: ctx, field: field, interval: interval, timezone: timezone, wantedTimezone: wantedTimezone,
5558
minDocCount: minDocCount, extendedBoundsMin: extendedBoundsMin, extendedBoundsMax: extendedBoundsMax,
56-
intervalType: intervalType, fieldDateTimeType: fieldDateTimeType}
59+
intervalType: intervalType, fieldDateTimeType: fieldDateTimeType, defaultFormat: defaultFormat}
5760
}
5861

5962
func (typ DateHistogramIntervalType) String(ctx context.Context) string {
@@ -100,12 +103,17 @@ func (query *DateHistogram) TranslateSqlResponseToJson(rows []model.QueryResultR
100103
}
101104
originalKey := query.getKey(row)
102105
responseKey := query.calculateResponseKey(originalKey)
103-
106+
var keyAsString string
107+
if query.defaultFormat {
108+
keyAsString = query.calculateKeyAsString(responseKey)
109+
} else {
110+
keyAsString = strconv.FormatInt(responseKey, 10)
111+
}
104112
response = append(response, model.JsonMap{
105113
OriginalKeyName: originalKey,
106114
"key": responseKey,
107115
"doc_count": docCount,
108-
"key_as_string": query.calculateKeyAsString(responseKey),
116+
"key_as_string": keyAsString,
109117
})
110118
}
111119

platform/model/bucket_aggregations/date_histogram_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ func TestTranslateSqlResponseToJson(t *testing.T) {
2222
},
2323
}
2424
response := (&DateHistogram{interval: interval, extendedBoundsMax: NoExtendedBound, extendedBoundsMin: NoExtendedBound,
25-
intervalType: DateHistogramFixedInterval, wantedTimezone: time.UTC}).TranslateSqlResponseToJson(resultRows)
25+
defaultFormat: true, intervalType: DateHistogramFixedInterval, wantedTimezone: time.UTC}).TranslateSqlResponseToJson(resultRows)
2626
assert.Equal(t, expectedResponse, response)
2727
}

platform/parsers/elastic_query_dsl/aggregation_parser_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@ func allAggregationTests() []testdata.AggregationTestCase {
658658
add(testdata.AggregationTests, "agg_req")
659659
add(testdata.AggregationTests2, "agg_req_2")
660660
add(testdata.AggregationTestsWithDates, "dates")
661+
add(testdata.GrafanaAggregationTests, "grafana")
661662
add(testdata.KibanaSampleDataLogs, "kibana-sample-data-logs")
662663
add(testdata.PipelineAggregationTests, "pipeline_agg_req")
663664
add(dashboard_1.AggregationTests, "dashboard-1/agg_req")

platform/parsers/elastic_query_dsl/pancake_aggregation_parser_buckets.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ func (cw *ClickhouseQueryTranslator) parseDateHistogram(aggregation *pancakeAggr
135135

136136
minDocCount := cw.parseMinDocCount(params)
137137
timezone := cw.parseStringField(params, "time_zone", "")
138+
format := cw.parseStringField(params, "format", "")
138139
interval, intervalType := cw.extractInterval(params)
139140
// TODO GetDateTimeTypeFromExpr can be moved and it should take cw.Schema as an argument
140141

@@ -143,7 +144,7 @@ func (cw *ClickhouseQueryTranslator) parseDateHistogram(aggregation *pancakeAggr
143144
}
144145

145146
dateHistogram := bucket_aggregations.NewDateHistogram(cw.Ctx,
146-
field, interval, timezone, minDocCount, ebMin, ebMax, intervalType, dateTimeType)
147+
field, interval, timezone, format, minDocCount, ebMin, ebMax, intervalType, dateTimeType)
147148
aggregation.queryType = dateHistogram
148149

149150
columnSql := dateHistogram.GenerateSQL()

platform/testdata/grafana.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright Quesma, licensed under the Elastic License 2.0.
2+
// SPDX-License-Identifier: Elastic-2.0
3+
package testdata
4+
5+
import "github.com/QuesmaOrg/quesma/platform/model"
6+
7+
var GrafanaAggregationTests = []AggregationTestCase{
8+
{ // [0]
9+
TestName: "simple max/min aggregation as 2 siblings",
10+
QueryRequestJson: `
11+
{
12+
"aggs": {
13+
"2": {
14+
"date_histogram": {
15+
"field": "@timestamp",
16+
"fixed_interval": "2000ms",
17+
"min_doc_count": 0,
18+
"extended_bounds": {
19+
"min": 1740930494000,
20+
"max": 1740930500000
21+
},
22+
"format": "epoch_millis"
23+
}
24+
}
25+
},
26+
"size": 0
27+
}`,
28+
ExpectedResponse: `
29+
{
30+
"aggregations": {
31+
"2": {
32+
"buckets": [
33+
{
34+
"doc_count": 0,
35+
"key": 1740930494000,
36+
"key_as_string": "1740930494000"
37+
},
38+
{
39+
"doc_count": 4,
40+
"key": 1740930496000,
41+
"key_as_string": "1740930496000"
42+
},
43+
{
44+
"doc_count": 0,
45+
"key": 1740930498000,
46+
"key_as_string": "1740930498000"
47+
},
48+
{
49+
"doc_count": 1,
50+
"key": 1740930500000,
51+
"key_as_string": "1740930500000"
52+
}
53+
]
54+
}
55+
},
56+
"hits": {
57+
"hits": [],
58+
"max_score": null,
59+
"total": {
60+
"relation": "eq",
61+
"value": 7
62+
}
63+
},
64+
"status": 200,
65+
"timed_out": false,
66+
"took": 30
67+
}`,
68+
ExpectedPancakeResults: []model.QueryResultRow{
69+
{Cols: []model.QueryResultCol{
70+
model.NewQueryResultCol("aggr__2__key_0", int64(1740930496000/2000)),
71+
model.NewQueryResultCol("aggr__2__count", int64(4)),
72+
}},
73+
{Cols: []model.QueryResultCol{
74+
model.NewQueryResultCol("aggr__2__key_0", int64(1740930500000/2000)),
75+
model.NewQueryResultCol("aggr__2__count", int64(1)),
76+
}},
77+
},
78+
ExpectedPancakeSQL: `
79+
SELECT toInt64(toUnixTimestamp64Milli("@timestamp") / 2000) AS "aggr__2__key_0",
80+
count(*) AS "aggr__2__count"
81+
FROM __quesma_table_name
82+
GROUP BY toInt64(toUnixTimestamp64Milli("@timestamp") / 2000) AS
83+
"aggr__2__key_0"
84+
ORDER BY "aggr__2__key_0" ASC`,
85+
},
86+
}

0 commit comments

Comments
 (0)