Skip to content

Commit

Permalink
Preserve column ordering (#14)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
samjewell authored Aug 8, 2024
1 parent be01dba commit 8ea1ec4
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 1 deletion.
24 changes: 23 additions & 1 deletion duck/duckdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
sdk "github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana-plugin-sdk-go/data/framestruct"
"github.com/hairyhenderson/go-which"
"github.com/iancoleman/orderedmap"
"github.com/scottlepp/go-duck/duck/data"
)

Expand Down Expand Up @@ -181,12 +182,33 @@ func resultsToFrame(name string, res string, f *sdk.Frame, frames []*sdk.Frame)
}
converters := data.Converters(frames)
resultsFrame, err := framestruct.ToDataFrame(name, results, converters...)

if err != nil {
logger.Error("error converting results to frame", "error", err)
return err
}

f.Fields = resultsFrame.Fields
// Order the fields in the same order as the source frame:
// Build a slice of ordered keys

var orderedKeys []string
var temp []orderedmap.OrderedMap
err = json.Unmarshal([]byte(res), &temp)
if err == nil {
orderedKeys = temp[0].Keys()
}

// Create a map of column names to indexes
columnIndex := make(map[string]int)
for i, field := range resultsFrame.Fields {
columnIndex[field.Name] = i
}
// Add columns to the DataFrame
for _, key := range orderedKeys {
i := columnIndex[key]
f.Fields = append(f.Fields, resultsFrame.Fields[i])
}

f.Name = resultsFrame.Name
f.Meta = resultsFrame.Meta
f.RefID = resultsFrame.RefID
Expand Down
27 changes: 27 additions & 0 deletions duck/duckdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,33 @@ func TestQueryFrameIntoFrame(t *testing.T) {
fmt.Printf("GOT: %s", txt)
}

func TestQueryFrameIntoFrameMultipleColumns(t *testing.T) {
db := NewInMemoryDB()

frame := data.NewFrame(
"A",
data.NewField("Z State", nil, []string{"Alaska"}),
data.NewField("Y Lat", nil, []string{"61"}),
data.NewField("X Lng", nil, []string{"32"}),
)
frame.RefID = "A"

frames := []*data.Frame{frame}

model := &data.Frame{}
err := db.QueryFramesInto("B", "select * from A", frames, model)
assert.Nil(t, err)

assert.Equal(t, "Z State", model.Fields[0].Name)
assert.Equal(t, "Y Lat", model.Fields[1].Name)
assert.Equal(t, "X Lng", model.Fields[2].Name)

txt, err := model.StringTable(-1, -1)
assert.Nil(t, err)

fmt.Printf("GOT: %s", txt)
}

func TestMultiFrame(t *testing.T) {
db := NewInMemoryDB()

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/apache/arrow/go/v15 v15.0.2
github.com/grafana/grafana-plugin-sdk-go v0.234.0
github.com/hairyhenderson/go-which v0.2.0
github.com/iancoleman/orderedmap v0.3.0
github.com/stretchr/testify v1.9.0
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOs
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down

0 comments on commit 8ea1ec4

Please sign in to comment.