Ensure that fields are not removed when set to NULL
This commit is contained in:
parent
9b03178dfd
commit
eb62515a05
5 changed files with 50 additions and 33 deletions
15
db/merge.go
15
db/merge.go
|
@ -108,7 +108,7 @@ func (d *document) delFld(ctx context.Context, met method) (err error) {
|
||||||
// Loop over the allowed keys
|
// Loop over the allowed keys
|
||||||
|
|
||||||
for _, fd := range fds {
|
for _, fd := range fds {
|
||||||
d.current.Walk(func(key string, val interface{}) (err error) {
|
d.current.Walk(func(key string, val interface{}, ok bool) (err error) {
|
||||||
keys[key] = struct{}{}
|
keys[key] = struct{}{}
|
||||||
return
|
return
|
||||||
}, fd.Name.ID)
|
}, fd.Name.ID)
|
||||||
|
@ -291,9 +291,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
for _, fd := range fds {
|
for _, fd := range fds {
|
||||||
|
|
||||||
err = d.current.Walk(func(key string, val interface{}) error {
|
err = d.current.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
|
|
||||||
vars := data.New()
|
|
||||||
|
|
||||||
var old = d.initial.Get(key).Data()
|
var old = d.initial.Get(key).Data()
|
||||||
|
|
||||||
|
@ -313,6 +311,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
// Reset the variables
|
// Reset the variables
|
||||||
|
|
||||||
|
vars := data.New()
|
||||||
vars.Set(val, varKeyValue)
|
vars.Set(val, varKeyValue)
|
||||||
vars.Set(val, varKeyAfter)
|
vars.Set(val, varKeyAfter)
|
||||||
vars.Set(old, varKeyBefore)
|
vars.Set(old, varKeyBefore)
|
||||||
|
@ -332,6 +331,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
// Reset the variables
|
// Reset the variables
|
||||||
|
|
||||||
|
vars := data.New()
|
||||||
vars.Set(val, varKeyValue)
|
vars.Set(val, varKeyValue)
|
||||||
vars.Set(val, varKeyAfter)
|
vars.Set(val, varKeyAfter)
|
||||||
vars.Set(old, varKeyBefore)
|
vars.Set(old, varKeyBefore)
|
||||||
|
@ -354,6 +354,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
// Reset the variables
|
// Reset the variables
|
||||||
|
|
||||||
|
vars := data.New()
|
||||||
vars.Set(val, varKeyValue)
|
vars.Set(val, varKeyValue)
|
||||||
vars.Set(val, varKeyAfter)
|
vars.Set(val, varKeyAfter)
|
||||||
vars.Set(old, varKeyBefore)
|
vars.Set(old, varKeyBefore)
|
||||||
|
@ -398,7 +399,11 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
||||||
|
|
||||||
switch val.(type) {
|
switch val.(type) {
|
||||||
default:
|
default:
|
||||||
d.current.Iff(val, key)
|
if exi {
|
||||||
|
d.current.Set(val, key)
|
||||||
|
} else {
|
||||||
|
d.current.Iff(val, key)
|
||||||
|
}
|
||||||
case *sql.Void:
|
case *sql.Void:
|
||||||
d.current.Del(key)
|
d.current.Del(key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (d *document) perms(ctx context.Context, doc *data.Doc) (err error) {
|
||||||
|
|
||||||
if fd.Perms != nil {
|
if fd.Perms != nil {
|
||||||
|
|
||||||
err = doc.Walk(func(key string, val interface{}) error {
|
err = doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
|
|
||||||
// We are checking the permissions of the field
|
// We are checking the permissions of the field
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,7 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
||||||
break
|
break
|
||||||
case *sql.Ident:
|
case *sql.Ident:
|
||||||
|
|
||||||
out.Walk(func(key string, val interface{}) error {
|
out.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
|
|
||||||
switch res := val.(type) {
|
switch res := val.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
|
|
|
@ -47,6 +47,9 @@ type Fetcher func(key string, val interface{}, path []string) interface{}
|
||||||
// Iterator is used when iterating over items.
|
// Iterator is used when iterating over items.
|
||||||
type Iterator func(key string, val interface{}) error
|
type Iterator func(key string, val interface{}) error
|
||||||
|
|
||||||
|
// Walker is used when walking over items.
|
||||||
|
type Walker func(key string, val interface{}, exi bool) error
|
||||||
|
|
||||||
// New creates a new data object.
|
// New creates a new data object.
|
||||||
func New() *Doc {
|
func New() *Doc {
|
||||||
return &Doc{data: map[string]interface{}{}}
|
return &Doc{data: map[string]interface{}{}}
|
||||||
|
@ -995,7 +998,7 @@ func (d *Doc) each(exec Iterator, prev []string) error {
|
||||||
// --------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Walk walks the value or values at a specified path.
|
// Walk walks the value or values at a specified path.
|
||||||
func (d *Doc) Walk(exec Iterator, path ...string) error {
|
func (d *Doc) Walk(exec Walker, path ...string) error {
|
||||||
|
|
||||||
path = d.path(path...)
|
path = d.path(path...)
|
||||||
|
|
||||||
|
@ -1003,7 +1006,7 @@ func (d *Doc) Walk(exec Iterator, path ...string) error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
func (d *Doc) walk(exec Walker, prev []string, path ...string) error {
|
||||||
|
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1037,7 +1040,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
|
|
||||||
if m, ok := object.(map[string]interface{}); ok {
|
if m, ok := object.(map[string]interface{}); ok {
|
||||||
if object, ok = m[p]; !ok {
|
if object, ok = m[p]; !ok {
|
||||||
return exec(d.join(prev, path), nil)
|
return exec(d.join(prev, path), nil, false)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1051,7 +1054,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
c, i, r := d.what(p, a, choose)
|
c, i, r := d.what(p, a, choose)
|
||||||
|
|
||||||
if r == one && len(c) == 0 {
|
if r == one && len(c) == 0 {
|
||||||
return fmt.Errorf("No item with index %s in array, using path %s", p, path)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == one {
|
if r == one {
|
||||||
|
@ -1060,7 +1063,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, path[:k]...)
|
keep = append(keep, path[:k]...)
|
||||||
keep = append(keep, fmt.Sprintf("[%d]", i[0]))
|
keep = append(keep, fmt.Sprintf("[%d]", i[0]))
|
||||||
return exec(d.join(keep), c[0])
|
return exec(d.join(keep), c[0], true)
|
||||||
} else {
|
} else {
|
||||||
var keep []string
|
var keep []string
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
|
@ -1077,7 +1080,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, path[:k]...)
|
keep = append(keep, path[:k]...)
|
||||||
keep = append(keep, fmt.Sprintf("[%d]", i[j]))
|
keep = append(keep, fmt.Sprintf("[%d]", i[j]))
|
||||||
if err := exec(d.join(keep), v); err != nil {
|
if err := exec(d.join(keep), v, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1098,10 +1101,10 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
// The current path item is not an object or an array
|
// The current path item is not an object or an array
|
||||||
// but there are still other items in the search path.
|
// but there are still other items in the search path.
|
||||||
|
|
||||||
return fmt.Errorf("Can not get path %s from %v", path, object)
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exec(d.join(prev, path), object)
|
return exec(d.join(prev, path), object, true)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -953,16 +953,18 @@ func TestOperations(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk nil", t, func() {
|
Convey("Can walk nil", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
doc.Set(tmp, "none")
|
doc.Set(tmp, "none")
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
So(doc.Exists("none"), ShouldBeFalse)
|
So(doc.Exists("none"), ShouldBeFalse)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array", t, func() {
|
Convey("Can walk array", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldResemble, "the.item.arrays")
|
So(key, ShouldResemble, "the.item.arrays")
|
||||||
|
So(exi, ShouldBeFalse)
|
||||||
doc.Set(tmp, key)
|
doc.Set(tmp, key)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays")
|
}, "the.item.arrays")
|
||||||
|
@ -970,98 +972,105 @@ func TestOperations(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → *", t, func() {
|
Convey("Can walk array → *", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldBeIn, "the.item.arrays.[0]", "the.item.arrays.[1]", "the.item.arrays.[2]")
|
So(key, ShouldBeIn, "the.item.arrays.[0]", "the.item.arrays.[1]", "the.item.arrays.[2]")
|
||||||
So(val, ShouldBeIn, tmp[0], tmp[1], tmp[2])
|
So(val, ShouldBeIn, tmp[0], tmp[1], tmp[2])
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.*")
|
}, "the.item.arrays.*")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → * → object", t, func() {
|
Convey("Can walk array → * → object", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldBeIn, "the.item.arrays.[0].test", "the.item.arrays.[1].test", "the.item.arrays.[2].test")
|
So(key, ShouldBeIn, "the.item.arrays.[0].test", "the.item.arrays.[1].test", "the.item.arrays.[2].test")
|
||||||
So(val, ShouldBeIn, "one", "two", "tre")
|
So(val, ShouldBeIn, "one", "two", "tre")
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.*.test")
|
}, "the.item.arrays.*.test")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → first → object", t, func() {
|
Convey("Can walk array → first → object", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldResemble, "the.item.arrays.[0].test")
|
So(key, ShouldResemble, "the.item.arrays.[0].test")
|
||||||
So(val, ShouldResemble, "one")
|
So(val, ShouldResemble, "one")
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.first.test")
|
}, "the.item.arrays.first.test")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → last → object", t, func() {
|
Convey("Can walk array → last → object", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldResemble, "the.item.arrays.[2].test")
|
So(key, ShouldResemble, "the.item.arrays.[2].test")
|
||||||
So(val, ShouldResemble, "tre")
|
So(val, ShouldResemble, "tre")
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.last.test")
|
}, "the.item.arrays.last.test")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → 0 → value", t, func() {
|
Convey("Can walk array → 0 → value", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldResemble, "the.item.arrays.[0]")
|
So(key, ShouldResemble, "the.item.arrays.[0]")
|
||||||
So(val, ShouldResemble, map[string]interface{}{"test": "one"})
|
So(val, ShouldResemble, map[string]interface{}{"test": "one"})
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.0")
|
}, "the.item.arrays.0")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → 1 → value", t, func() {
|
Convey("Can walk array → 1 → value", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldResemble, "the.item.arrays.[1]")
|
So(key, ShouldResemble, "the.item.arrays.[1]")
|
||||||
So(val, ShouldResemble, map[string]interface{}{"test": "two"})
|
So(val, ShouldResemble, map[string]interface{}{"test": "two"})
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.1")
|
}, "the.item.arrays.1")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → 2 → value", t, func() {
|
Convey("Can walk array → 2 → value", t, func() {
|
||||||
doc.Walk(func(key string, val interface{}) error {
|
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
So(key, ShouldResemble, "the.item.arrays.[2]")
|
So(key, ShouldResemble, "the.item.arrays.[2]")
|
||||||
So(val, ShouldResemble, map[string]interface{}{"test": "tre"})
|
So(val, ShouldResemble, map[string]interface{}{"test": "tre"})
|
||||||
|
So(exi, ShouldBeTrue)
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.2")
|
}, "the.item.arrays.2")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → 3 → value", t, func() {
|
Convey("Can walk array → 3 → value", t, func() {
|
||||||
err := doc.Walk(func(key string, val interface{}) error {
|
err := doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.3")
|
}, "the.item.arrays.3")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → 0 → value → value", t, func() {
|
Convey("Can walk array → 0 → value → value", t, func() {
|
||||||
err := doc.Walk(func(key string, val interface{}) error {
|
err := doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.0.test.value")
|
}, "the.item.arrays.0.test.value")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can walk array → 0 → value → value → value", t, func() {
|
Convey("Can walk array → 0 → value → value → value", t, func() {
|
||||||
err := doc.Walk(func(key string, val interface{}) error {
|
err := doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
return nil
|
return nil
|
||||||
}, "the.item.arrays.0.test.value.value")
|
}, "the.item.arrays.0.test.value.value")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can force error from walk", t, func() {
|
Convey("Can force error from walk", t, func() {
|
||||||
err := doc.Walk(func(key string, val interface{}) error {
|
err := doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
return errors.New("Testing")
|
return errors.New("Testing")
|
||||||
}, "the.item.something")
|
}, "the.item.something")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can force error from walk array → *", t, func() {
|
Convey("Can force error from walk array → *", t, func() {
|
||||||
err := doc.Walk(func(key string, val interface{}) error {
|
err := doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
return errors.New("Testing")
|
return errors.New("Testing")
|
||||||
}, "the.item.arrays.*")
|
}, "the.item.arrays.*")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can force error from walk array → * → value", t, func() {
|
Convey("Can force error from walk array → * → value", t, func() {
|
||||||
err := doc.Walk(func(key string, val interface{}) error {
|
err := doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||||
return errors.New("Testing")
|
return errors.New("Testing")
|
||||||
}, "the.item.arrays.*.test")
|
}, "the.item.arrays.*.test")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
|
Loading…
Reference in a new issue