Skip to content

Commit d74d810

Browse files
committed
internal/cmd/query: unify JSON output for aggregated and non-aggregated results
Remove the special-cased aggregation branch from the -f=json code path. All query results now use tableRowAtIndex() to produce NDJSON objects, regardless of whether the result has aggregation metadata. Previously, aggregated results without groups output only the first field's bare scalar value (table.Columns[0][0]), silently discarding other fields. Aggregated results with groups errored entirely. A server-side optimizer change (axiomhq/axiom#16816) now preserves aggregation metadata on fields that were previously stripped, causing multi-field queries (e.g. summarize ... | project a, b, c) to hit the broken scalar path. The fix deletes the aggregation branch and lets all results flow through the existing, correct NDJSON loop. Table output format is unaffected.
1 parent 4539dde commit d74d810

2 files changed

Lines changed: 81 additions & 12 deletions

File tree

internal/cmd/query/query.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -290,19 +290,9 @@ func run(ctx context.Context, opts *options) error {
290290

291291
table := res.Tables[0]
292292

293-
// Deal with JSON output format. It only works for non-aggregated results OR
294-
// an aggregated result which produces a single value (it has no groups).
293+
// Deal with JSON output format. Outputs NDJSON (one JSON object per row)
294+
// for all results, regardless of whether they are aggregated.
295295
if opts.Format == iofmt.JSON.String() {
296-
if tableHasAggregation(table) {
297-
if len(table.Groups) > 1 || (len(table.Columns) > 1 && len(table.Columns[0]) > 1) {
298-
return errors.New("JSON output format is not supported for aggregated results with groups")
299-
}
300-
if opts.IO.IsStdoutTTY() {
301-
fmt.Fprint(opts.IO.Out(), headerText)
302-
}
303-
return iofmt.FormatToJSON(opts.IO.Out(), table.Columns[0][0], opts.IO.ColorEnabled())
304-
}
305-
306296
if opts.IO.IsStdoutTTY() {
307297
fmt.Fprint(opts.IO.Out(), headerText)
308298
}

internal/cmd/query/query_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package query
33
import (
44
"testing"
55
"time"
6+
7+
"github.com/axiomhq/axiom-go/axiom/query"
68
)
79

810
func TestParseDuration(t *testing.T) {
@@ -54,6 +56,83 @@ func TestParseDuration(t *testing.T) {
5456
}
5557
}
5658

59+
func TestTableRowAtIndex(t *testing.T) {
60+
table := query.Table{
61+
Fields: []query.Field{
62+
{Name: "organizations"},
63+
{Name: "datasets"},
64+
{Name: "spans"},
65+
},
66+
Columns: []query.Column{
67+
{"102 (+2)"},
68+
{"132 (+3)"},
69+
{"49.3M (+2.5M)"},
70+
},
71+
}
72+
73+
row := tableRowAtIndex(table, 0)
74+
75+
if got := row["organizations"]; got != "102 (+2)" {
76+
t.Errorf("organizations = %v, want %q", got, "102 (+2)")
77+
}
78+
if got := row["datasets"]; got != "132 (+3)" {
79+
t.Errorf("datasets = %v, want %q", got, "132 (+3)")
80+
}
81+
if got := row["spans"]; got != "49.3M (+2.5M)" {
82+
t.Errorf("spans = %v, want %q", got, "49.3M (+2.5M)")
83+
}
84+
}
85+
86+
func TestTableRowAtIndexMultipleRows(t *testing.T) {
87+
table := query.Table{
88+
Fields: []query.Field{
89+
{Name: "status"},
90+
{Name: "count_", Aggregation: &query.Aggregation{Op: query.OpCount}},
91+
},
92+
Columns: []query.Column{
93+
{"ok", "err"},
94+
{float64(42), float64(3)},
95+
},
96+
}
97+
98+
row0 := tableRowAtIndex(table, 0)
99+
if got := row0["status"]; got != "ok" {
100+
t.Errorf("row0 status = %v, want %q", got, "ok")
101+
}
102+
if got := row0["count_"]; got != float64(42) {
103+
t.Errorf("row0 count_ = %v, want %v", got, 42)
104+
}
105+
106+
row1 := tableRowAtIndex(table, 1)
107+
if got := row1["status"]; got != "err" {
108+
t.Errorf("row1 status = %v, want %q", got, "err")
109+
}
110+
if got := row1["count_"]; got != float64(3) {
111+
t.Errorf("row1 count_ = %v, want %v", got, 3)
112+
}
113+
}
114+
115+
func TestTableHasAggregation(t *testing.T) {
116+
noAgg := query.Table{
117+
Fields: []query.Field{
118+
{Name: "org"},
119+
{Name: "spans"},
120+
},
121+
}
122+
if tableHasAggregation(noAgg) {
123+
t.Error("expected no aggregation for plain fields")
124+
}
125+
126+
withAgg := query.Table{
127+
Fields: []query.Field{
128+
{Name: "count_", Aggregation: &query.Aggregation{Op: query.OpCount}},
129+
},
130+
}
131+
if !tableHasAggregation(withAgg) {
132+
t.Error("expected aggregation when field has Aggregation set")
133+
}
134+
}
135+
57136
func TestParseDurationErrors(t *testing.T) {
58137
tests := []string{
59138
"",

0 commit comments

Comments
 (0)