add function FieldsToJSON
This function enables the show view to be much less write overhead. By defining which columns to return and automatically merging the attributes into the main view, this can be made so much easier. It doesn't support the recursive view for now, so that is something a client would need to handle, but for now this should be good enough. This also fixes a small issue in the update clause handler by moving the index handler into the handler when a column was found. If that was not done, the index gets moved to the wrong position and the resulting query would be wrongly indexed.
This commit is contained in:
parent
f96f8ba4ed
commit
88c271667b
|
@ -36,6 +36,18 @@ func FieldListToSelect(tableName string, fl types.FieldList, nameMap map[string]
|
|||
return strings.Join(res, ",")
|
||||
}
|
||||
|
||||
// FieldsToJSON converts a map of field names to columns to build a json output.
|
||||
// The attributes column of the table is merged into the resulting JSON.
|
||||
// Fields must contain the name of the resulting field and the column name where the value
|
||||
// is coming from.
|
||||
func FieldsToJSON(table string, fields map[string]string) string {
|
||||
res := []string{}
|
||||
for key, val := range fields {
|
||||
res = append(res, fmt.Sprintf("'%s', %s", key, val))
|
||||
}
|
||||
return fmt.Sprintf("jsonb_build_object(%s) || attributes", strings.Join(res, ","))
|
||||
}
|
||||
|
||||
// nameToAttrPath takes a dotted string and converts it into a json field path.
|
||||
func nameToAttrPath(tabName, name string) string {
|
||||
if name == "" {
|
||||
|
@ -67,8 +79,8 @@ func FieldMapToUpdate(fm types.FieldMap, nameMap map[string]string) (string, []i
|
|||
attrVals := map[string]interface{}{}
|
||||
i := 0
|
||||
for key, val := range fm.Fields() {
|
||||
i++
|
||||
if name, found := nameMap[key]; found {
|
||||
i++
|
||||
setClause = append(setClause, fmt.Sprintf("%s = $%d", name, i))
|
||||
if val == "" {
|
||||
args = append(args, nil)
|
||||
|
|
|
@ -13,18 +13,23 @@ func TestFieldListToSelect(t *testing.T) {
|
|||
mapping map[string]string
|
||||
out string
|
||||
}{
|
||||
{"foo", types.NewFieldList("name"), map[string]string{"name": "name"}, "name as name"},
|
||||
{
|
||||
"foo",
|
||||
types.NewFieldList("name"),
|
||||
map[string]string{"name": "name"},
|
||||
`name as "name"`,
|
||||
},
|
||||
{
|
||||
"foo",
|
||||
types.NewFieldList("foo", "bar", "baz"),
|
||||
map[string]string{"foo": "c.foo", "bar": "b.bar"},
|
||||
"b.bar as bar,foo.attributes->'baz' as baz,c.foo as foo",
|
||||
`b.bar as "bar",foo.attributes->>'baz' as "baz",c.foo as "foo"`,
|
||||
},
|
||||
{
|
||||
"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",
|
||||
`p.name as "name",s.subnets as "subnets",vlan.vlan_id as "vlan"`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -36,14 +41,28 @@ func TestFieldListToSelect(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestJSONSelect(t *testing.T) {
|
||||
tests := []struct {
|
||||
table string
|
||||
fields map[string]string
|
||||
out string
|
||||
}{}
|
||||
for _, test := range tests {
|
||||
out := FieldsToJSON(test.table, test.fields)
|
||||
if out != test.out {
|
||||
t.Errorf("expected `%s`, got `%s`", test.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameToAttrPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
table string
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"zoo", "foo", `zoo.attributes->'foo'`},
|
||||
{"zoo", "foo.bar", `zoo.attributes->'foo'->'bar'`},
|
||||
{"zoo", "foo", `zoo.attributes->>'foo'`},
|
||||
{"zoo", "foo.bar", `zoo.attributes->'foo'->>'bar'`},
|
||||
{"zoo", "", `zoo.attributes`},
|
||||
}
|
||||
|
||||
|
@ -57,37 +76,36 @@ func TestNameToAttrPath(t *testing.T) {
|
|||
|
||||
func TestFieldMapToUpdate(t *testing.T) {
|
||||
tests := []struct {
|
||||
table string
|
||||
vals types.FieldMap
|
||||
mapping map[string]string
|
||||
set string // expected set clause
|
||||
args []interface{} // expected arguments
|
||||
}{
|
||||
{ // check for normal field mapping
|
||||
"zoo",
|
||||
types.NewFieldMap(map[string]interface{}{"key": "value"}),
|
||||
map[string]string{"key": "field"},
|
||||
"zoo.field = $1",
|
||||
"field = $1",
|
||||
[]interface{}{"value"},
|
||||
},
|
||||
{ // generate attributes field
|
||||
"zoo",
|
||||
types.NewFieldMap(map[string]interface{}{"key2": "value"}),
|
||||
map[string]string{"key": "field"},
|
||||
"zoo.attributes->'key2' = $1",
|
||||
[]interface{}{"value"},
|
||||
"attributes = jsonb_strip_nulls(attributes || $1::jsonb)",
|
||||
[]interface{}{`{"key2":"value"}`},
|
||||
},
|
||||
{ // mixed mapped and unmapped field
|
||||
"zoo",
|
||||
types.NewFieldMap(map[string]interface{}{"key2": "value", "key": "value"}),
|
||||
map[string]string{"key": "field"},
|
||||
"zoo.attributes->'key2' = $1,zoo.field = $2",
|
||||
[]interface{}{"value", "value"},
|
||||
"field = $1,attributes = jsonb_strip_nulls(attributes || $2::jsonb)",
|
||||
[]interface{}{"value", `{"key2":"value"}`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
set, args := FieldMapToUpdate(test.table, test.vals, test.mapping)
|
||||
set, args, err := FieldMapToUpdate(test.vals, test.mapping)
|
||||
if err != nil {
|
||||
t.Errorf("could not map to update: %s", err)
|
||||
}
|
||||
if set != test.set {
|
||||
t.Errorf("expected set clause `%s`, got `%s`", test.set, set)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue