add query interface
This is a small library to build queries and put the result into the world. Currently it supports building the select clause and converting rows into a list of maps, so that it can be returned as a list.master
parent
1015861af8
commit
ee5acc6ded
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Package query provides functions useful for building database queries.
|
||||
|
||||
It contains functions to build select clauses or where clauses together
|
||||
with the necessary parameter keys.
|
||||
*/
|
||||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dim/types"
|
||||
)
|
||||
|
||||
// FieldListToSelect converts the fieldlist to a select clause.
|
||||
//
|
||||
// It takes a fieldField and tries to find a matching name in the nameMap and
|
||||
// uses the provided name.
|
||||
// Any field that is not found will be converted to an attributes selector, so
|
||||
// that extra attributes can be selected dynamically.
|
||||
// Each field will be selected with the requested name, so be careful to
|
||||
// avoid collisions.
|
||||
func FieldListToSelect(fl types.FieldList, nameMap map[string]string) string {
|
||||
res := []string{}
|
||||
for _, name := range fl.Fields() {
|
||||
if field, found := nameMap[name]; found {
|
||||
res = append(res, fmt.Sprintf(`%s as %s`, field, name))
|
||||
} else {
|
||||
res = append(res, fmt.Sprintf(`%s as %s`, nameToAttrPath(name), name))
|
||||
}
|
||||
}
|
||||
return strings.Join(res, ",")
|
||||
}
|
||||
|
||||
// nameToAttrPath takes a dotted string and converts it into a json field path.
|
||||
func nameToAttrPath(name string) string {
|
||||
if name == "" {
|
||||
return "attributes"
|
||||
}
|
||||
parts := strings.Split(name, ".")
|
||||
for i, part := range parts {
|
||||
parts[i] = fmt.Sprintf(`'%s'`, part)
|
||||
}
|
||||
return fmt.Sprintf("attributes->%s", strings.Join(parts, "->"))
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"dim/types"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFieldListToSelect(t *testing.T) {
|
||||
tests := []struct {
|
||||
names types.FieldList
|
||||
mapping map[string]string
|
||||
out string
|
||||
}{
|
||||
{types.NewFieldList("name"), map[string]string{"name": "name"}, "name as name"},
|
||||
{
|
||||
types.NewFieldList("foo", "bar", "baz"),
|
||||
map[string]string{"foo": "c.foo", "bar": "b.bar"},
|
||||
"b.bar as bar,attributes->'baz' as baz,c.foo as foo",
|
||||
},
|
||||
{
|
||||
types.NewFieldList("subnets", "vlan", "name"),
|
||||
map[string]string{"subnets": "s.subnets", "vlan": "vlan.vlan_id", "name": "p.name"},
|
||||
"p.name as name,s.subnets as subnets,vlan.vlan_id as vlan",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
out := FieldListToSelect(test.names, test.mapping)
|
||||
if out != test.out {
|
||||
t.Errorf("expected `%s`, got `%s`", test.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameToAttrPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"foo", `attributes->'foo'`},
|
||||
{"foo.bar", `attributes->'foo'->'bar'`},
|
||||
{"", `attributes`},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
out := nameToAttrPath(test.in)
|
||||
if test.out != out {
|
||||
t.Errorf("expected `%s`, got `%s`", test.out, out)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RowsToMap converts a query result to a list of maps.
|
||||
func RowsToMap(rows *sql.Rows) ([]map[string]interface{}, error) {
|
||||
empty := []map[string]interface{}{}
|
||||
res := []map[string]interface{}{}
|
||||
cols, err := rows.Columns()
|
||||
if err != nil {
|
||||
return empty, fmt.Errorf("could not get columns: %v", err)
|
||||
}
|
||||
for rows.Next() {
|
||||
if rows.Err() != nil {
|
||||
return empty, fmt.Errorf("could not iterate rows: %v", err)
|
||||
}
|
||||
raw := make([]interface{}, len(cols))
|
||||
for i, _ := range raw {
|
||||
raw[i] = new(interface{})
|
||||
}
|
||||
if err := rows.Scan(raw...); err != nil {
|
||||
return empty, fmt.Errorf("could not scan row: %v", err)
|
||||
}
|
||||
|
||||
row := map[string]interface{}{}
|
||||
for i, col := range cols {
|
||||
row[col] = raw[i]
|
||||
}
|
||||
res = append(res, row)
|
||||
}
|
||||
return res, nil
|
||||
}
|
Loading…
Reference in New Issue