Skip to content

Commit 8ea1ec4

Browse files
authored
Preserve column ordering (#14)
* Add failing test * Test still fails, even when `WithColumn0` used This confirms that we need a new approach to creating the fields in the dataframe * Order columns using orderedmap when parsing JSON record The JSON records that DuckDB dumps are actually ordered maps. The object keys (and values) appear in the order that they were requested in the SELECT statement. Although JSON has no concept of ordering within objects, this is the simplest and quickest way to fix this problem. I'll try to investigate some other avenues for fixing this one day in the future. @ryantxu suggested a couple of things we could try: - We can implement a JSON parser that goes straight to data frame (like the one in the sdk) - or better may be using parquet output. if named based on a hash this could also be a cache system 'for free' Also note, there's another orderedmap Go package we could have used: https://github.com/wk8/go-ordered-map They seem to have a similar number of forks and stars on Github, and I just happened across the iancoleman one first.
1 parent be01dba commit 8ea1ec4

File tree

4 files changed

+53
-1
lines changed

4 files changed

+53
-1
lines changed

duck/duckdb.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
sdk "github.com/grafana/grafana-plugin-sdk-go/data"
1313
"github.com/grafana/grafana-plugin-sdk-go/data/framestruct"
1414
"github.com/hairyhenderson/go-which"
15+
"github.com/iancoleman/orderedmap"
1516
"github.com/scottlepp/go-duck/duck/data"
1617
)
1718

@@ -181,12 +182,33 @@ func resultsToFrame(name string, res string, f *sdk.Frame, frames []*sdk.Frame)
181182
}
182183
converters := data.Converters(frames)
183184
resultsFrame, err := framestruct.ToDataFrame(name, results, converters...)
185+
184186
if err != nil {
185187
logger.Error("error converting results to frame", "error", err)
186188
return err
187189
}
188190

189-
f.Fields = resultsFrame.Fields
191+
// Order the fields in the same order as the source frame:
192+
// Build a slice of ordered keys
193+
194+
var orderedKeys []string
195+
var temp []orderedmap.OrderedMap
196+
err = json.Unmarshal([]byte(res), &temp)
197+
if err == nil {
198+
orderedKeys = temp[0].Keys()
199+
}
200+
201+
// Create a map of column names to indexes
202+
columnIndex := make(map[string]int)
203+
for i, field := range resultsFrame.Fields {
204+
columnIndex[field.Name] = i
205+
}
206+
// Add columns to the DataFrame
207+
for _, key := range orderedKeys {
208+
i := columnIndex[key]
209+
f.Fields = append(f.Fields, resultsFrame.Fields[i])
210+
}
211+
190212
f.Name = resultsFrame.Name
191213
f.Meta = resultsFrame.Meta
192214
f.RefID = resultsFrame.RefID

duck/duckdb_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,33 @@ func TestQueryFrameIntoFrame(t *testing.T) {
149149
fmt.Printf("GOT: %s", txt)
150150
}
151151

152+
func TestQueryFrameIntoFrameMultipleColumns(t *testing.T) {
153+
db := NewInMemoryDB()
154+
155+
frame := data.NewFrame(
156+
"A",
157+
data.NewField("Z State", nil, []string{"Alaska"}),
158+
data.NewField("Y Lat", nil, []string{"61"}),
159+
data.NewField("X Lng", nil, []string{"32"}),
160+
)
161+
frame.RefID = "A"
162+
163+
frames := []*data.Frame{frame}
164+
165+
model := &data.Frame{}
166+
err := db.QueryFramesInto("B", "select * from A", frames, model)
167+
assert.Nil(t, err)
168+
169+
assert.Equal(t, "Z State", model.Fields[0].Name)
170+
assert.Equal(t, "Y Lat", model.Fields[1].Name)
171+
assert.Equal(t, "X Lng", model.Fields[2].Name)
172+
173+
txt, err := model.StringTable(-1, -1)
174+
assert.Nil(t, err)
175+
176+
fmt.Printf("GOT: %s", txt)
177+
}
178+
152179
func TestMultiFrame(t *testing.T) {
153180
db := NewInMemoryDB()
154181

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/apache/arrow/go/v15 v15.0.2
77
github.com/grafana/grafana-plugin-sdk-go v0.234.0
88
github.com/hairyhenderson/go-which v0.2.0
9+
github.com/iancoleman/orderedmap v0.3.0
910
github.com/stretchr/testify v1.9.0
1011
)
1112

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs
7474
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
7575
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
7676
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
77+
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
78+
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
7779
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
7880
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
7981
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=

0 commit comments

Comments
 (0)