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
|
||||
|
||||
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{}{}
|
||||
return
|
||||
}, fd.Name.ID)
|
||||
|
@ -291,9 +291,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
|||
|
||||
for _, fd := range fds {
|
||||
|
||||
err = d.current.Walk(func(key string, val interface{}) error {
|
||||
|
||||
vars := data.New()
|
||||
err = d.current.Walk(func(key string, val interface{}, exi bool) error {
|
||||
|
||||
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
|
||||
|
||||
vars := data.New()
|
||||
vars.Set(val, varKeyValue)
|
||||
vars.Set(val, varKeyAfter)
|
||||
vars.Set(old, varKeyBefore)
|
||||
|
@ -332,6 +331,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
|||
|
||||
// Reset the variables
|
||||
|
||||
vars := data.New()
|
||||
vars.Set(val, varKeyValue)
|
||||
vars.Set(val, varKeyAfter)
|
||||
vars.Set(old, varKeyBefore)
|
||||
|
@ -354,6 +354,7 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
|||
|
||||
// Reset the variables
|
||||
|
||||
vars := data.New()
|
||||
vars.Set(val, varKeyValue)
|
||||
vars.Set(val, varKeyAfter)
|
||||
vars.Set(old, varKeyBefore)
|
||||
|
@ -398,7 +399,11 @@ func (d *document) mrgFld(ctx context.Context, met method) (err error) {
|
|||
|
||||
switch val.(type) {
|
||||
default:
|
||||
d.current.Iff(val, key)
|
||||
if exi {
|
||||
d.current.Set(val, key)
|
||||
} else {
|
||||
d.current.Iff(val, key)
|
||||
}
|
||||
case *sql.Void:
|
||||
d.current.Del(key)
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func (d *document) perms(ctx context.Context, doc *data.Doc) (err error) {
|
|||
|
||||
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
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
|||
break
|
||||
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) {
|
||||
case []interface{}:
|
||||
|
|
|
@ -47,6 +47,9 @@ type Fetcher func(key string, val interface{}, path []string) interface{}
|
|||
// Iterator is used when iterating over items.
|
||||
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.
|
||||
func New() *Doc {
|
||||
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.
|
||||
func (d *Doc) Walk(exec Iterator, path ...string) error {
|
||||
func (d *Doc) Walk(exec Walker, path ...string) error {
|
||||
|
||||
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 {
|
||||
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 object, ok = m[p]; !ok {
|
||||
return exec(d.join(prev, path), nil)
|
||||
return exec(d.join(prev, path), nil, false)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -1051,7 +1054,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
|||
c, i, r := d.what(p, a, choose)
|
||||
|
||||
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 {
|
||||
|
@ -1060,7 +1063,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
|||
keep = append(keep, prev...)
|
||||
keep = append(keep, path[:k]...)
|
||||
keep = append(keep, fmt.Sprintf("[%d]", i[0]))
|
||||
return exec(d.join(keep), c[0])
|
||||
return exec(d.join(keep), c[0], true)
|
||||
} else {
|
||||
var keep []string
|
||||
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, path[:k]...)
|
||||
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
|
||||
}
|
||||
} 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
|
||||
// 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() {
|
||||
doc.Walk(func(key string, val interface{}) error {
|
||||
doc.Walk(func(key string, val interface{}, exi bool) error {
|
||||
doc.Set(tmp, "none")
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
})
|
||||
So(doc.Exists("none"), ShouldBeFalse)
|
||||
})
|
||||
|
||||
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(exi, ShouldBeFalse)
|
||||
doc.Set(tmp, key)
|
||||
return nil
|
||||
}, "the.item.arrays")
|
||||
|
@ -970,98 +972,105 @@ func TestOperations(t *testing.T) {
|
|||
})
|
||||
|
||||
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(val, ShouldBeIn, tmp[0], tmp[1], tmp[2])
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.*")
|
||||
})
|
||||
|
||||
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(val, ShouldBeIn, "one", "two", "tre")
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.*.test")
|
||||
})
|
||||
|
||||
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(val, ShouldResemble, "one")
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.first.test")
|
||||
})
|
||||
|
||||
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(val, ShouldResemble, "tre")
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.last.test")
|
||||
})
|
||||
|
||||
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(val, ShouldResemble, map[string]interface{}{"test": "one"})
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.0")
|
||||
})
|
||||
|
||||
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(val, ShouldResemble, map[string]interface{}{"test": "two"})
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.1")
|
||||
})
|
||||
|
||||
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(val, ShouldResemble, map[string]interface{}{"test": "tre"})
|
||||
So(exi, ShouldBeTrue)
|
||||
return nil
|
||||
}, "the.item.arrays.2")
|
||||
})
|
||||
|
||||
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
|
||||
}, "the.item.arrays.3")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
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
|
||||
}, "the.item.arrays.0.test.value")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
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
|
||||
}, "the.item.arrays.0.test.value.value")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
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")
|
||||
}, "the.item.something")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
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")
|
||||
}, "the.item.arrays.*")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
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")
|
||||
}, "the.item.arrays.*.test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
|
Loading…
Reference in a new issue