Improve remote array record fetching
This commit is contained in:
parent
abe117b7d3
commit
35047ce04c
4 changed files with 57 additions and 28 deletions
36
db/fetch.go
36
db/fetch.go
|
@ -86,6 +86,9 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
|
|
||||||
doc.Fetch(func(key string, val interface{}) interface{} {
|
doc.Fetch(func(key string, val interface{}) interface{} {
|
||||||
switch res := val.(type) {
|
switch res := val.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
val, _ = e.fetchArray(ctx, res, doc)
|
||||||
|
return val
|
||||||
case *sql.Thing:
|
case *sql.Thing:
|
||||||
val, _ = e.fetchThing(ctx, res, doc)
|
val, _ = e.fetchThing(ctx, res, doc)
|
||||||
return val
|
return val
|
||||||
|
@ -106,6 +109,9 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
|
|
||||||
obj.Fetch(func(key string, val interface{}) interface{} {
|
obj.Fetch(func(key string, val interface{}) interface{} {
|
||||||
switch res := val.(type) {
|
switch res := val.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
val, _ = e.fetchArray(ctx, res, doc)
|
||||||
|
return val
|
||||||
case *sql.Thing:
|
case *sql.Thing:
|
||||||
val, _ = e.fetchThing(ctx, res, doc)
|
val, _ = e.fetchThing(ctx, res, doc)
|
||||||
return val
|
return val
|
||||||
|
@ -328,11 +334,12 @@ func (e *executor) fetchPaths(ctx context.Context, doc *data.Doc, exprs ...sql.E
|
||||||
func (e *executor) fetchThing(ctx context.Context, val *sql.Thing, doc *data.Doc) (interface{}, error) {
|
func (e *executor) fetchThing(ctx context.Context, val *sql.Thing, doc *data.Doc) (interface{}, error) {
|
||||||
|
|
||||||
res, err := e.executeSelect(ctx, &sql.SelectStatement{
|
res, err := e.executeSelect(ctx, &sql.SelectStatement{
|
||||||
KV: cnf.Settings.DB.Base,
|
KV: cnf.Settings.DB.Base,
|
||||||
NS: ctx.Value(ctxKeyNs).(string),
|
NS: ctx.Value(ctxKeyNs).(string),
|
||||||
DB: ctx.Value(ctxKeyDb).(string),
|
DB: ctx.Value(ctxKeyDb).(string),
|
||||||
Expr: []*sql.Field{{Expr: &sql.All{}}},
|
Expr: []*sql.Field{{Expr: &sql.All{}}},
|
||||||
What: []sql.Expr{val},
|
What: []sql.Expr{val},
|
||||||
|
Parallel: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -347,6 +354,25 @@ func (e *executor) fetchThing(ctx context.Context, val *sql.Thing, doc *data.Doc
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *executor) fetchArray(ctx context.Context, val []interface{}, doc *data.Doc) (interface{}, error) {
|
||||||
|
|
||||||
|
res, err := e.executeSelect(ctx, &sql.SelectStatement{
|
||||||
|
KV: cnf.Settings.DB.Base,
|
||||||
|
NS: ctx.Value(ctxKeyNs).(string),
|
||||||
|
DB: ctx.Value(ctxKeyDb).(string),
|
||||||
|
Expr: []*sql.Field{{Expr: &sql.All{}}},
|
||||||
|
What: []sql.Expr{val},
|
||||||
|
Parallel: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (e *executor) fetchLimit(ctx context.Context, val sql.Expr) (int, error) {
|
func (e *executor) fetchLimit(ctx context.Context, val sql.Expr) (int, error) {
|
||||||
|
|
||||||
v, err := e.fetch(ctx, val, nil)
|
v, err := e.fetch(ctx, val, nil)
|
||||||
|
|
|
@ -1050,7 +1050,7 @@ func isThingChar(ch rune) bool {
|
||||||
|
|
||||||
// isExprsChar returns true if the rune is allowed in a IDENT.
|
// isExprsChar returns true if the rune is allowed in a IDENT.
|
||||||
func isExprsChar(ch rune) bool {
|
func isExprsChar(ch rune) bool {
|
||||||
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*' || ch == '[' || ch == ']'
|
return isLetter(ch) || isNumber(ch) || ch == '.' || ch == '_' || ch == '*' || ch == '[' || ch == '$' || ch == ']'
|
||||||
}
|
}
|
||||||
|
|
||||||
// eof represents a marker rune for the end of the reader.
|
// eof represents a marker rune for the end of the reader.
|
||||||
|
|
|
@ -58,6 +58,11 @@ func Consume(input interface{}) *Doc {
|
||||||
return &Doc{data: input}
|
return &Doc{data: input}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Consume converts a GO interface into a data object.
|
||||||
|
func ConsumeWithFetch(input interface{}, fetcher Fetcher) *Doc {
|
||||||
|
return &Doc{data: input, call: fetcher}
|
||||||
|
}
|
||||||
|
|
||||||
// Data returns the internal data object as an interface.
|
// Data returns the internal data object as an interface.
|
||||||
func (d *Doc) Data() interface{} {
|
func (d *Doc) Data() interface{} {
|
||||||
return d.data
|
return d.data
|
||||||
|
@ -428,7 +433,7 @@ func (d *Doc) Exists(path ...string) bool {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil && len(path[k+1:]) > 0 {
|
||||||
c[0] = d.call(p, c[0])
|
c[0] = d.call(p, c[0])
|
||||||
}
|
}
|
||||||
return Consume(c[0]).Exists(path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).Exists(path[k+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == many {
|
if r == many {
|
||||||
|
@ -436,7 +441,7 @@ func (d *Doc) Exists(path ...string) bool {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil && len(path[k+1:]) > 0 {
|
||||||
v = d.call(p, v)
|
v = d.call(p, v)
|
||||||
}
|
}
|
||||||
if !Consume(v).Exists(path[k+1:]...) {
|
if !ConsumeWithFetch(v, d.call).Exists(path[k+1:]...) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -516,7 +521,7 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil && len(path[k+1:]) > 0 {
|
||||||
c[0] = d.call(p, c[0])
|
c[0] = d.call(p, c[0])
|
||||||
}
|
}
|
||||||
return Consume(c[0]).Get(path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).Get(path[k+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == many {
|
if r == many {
|
||||||
|
@ -525,10 +530,8 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil && len(path[k+1:]) > 0 {
|
||||||
v = d.call(p, v)
|
v = d.call(p, v)
|
||||||
}
|
}
|
||||||
res := Consume(v).Get(path[k+1:]...)
|
res := ConsumeWithFetch(v, d.call).Get(path[k+1:]...)
|
||||||
if res.data != nil {
|
out = append(out, res.data)
|
||||||
out = append(out, res.data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return &Doc{data: out}
|
return &Doc{data: out}
|
||||||
}
|
}
|
||||||
|
@ -608,7 +611,7 @@ func (d *Doc) Set(value interface{}, path ...string) (*Doc, error) {
|
||||||
a[i[0]] = value
|
a[i[0]] = value
|
||||||
object = a[i[0]]
|
object = a[i[0]]
|
||||||
} else {
|
} else {
|
||||||
return Consume(a[i[0]]).Set(value, path[k+1:]...)
|
return ConsumeWithFetch(a[i[0]], d.call).Set(value, path[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,7 +622,7 @@ func (d *Doc) Set(value interface{}, path ...string) (*Doc, error) {
|
||||||
a[i[j]] = value
|
a[i[j]] = value
|
||||||
out = append(out, value)
|
out = append(out, value)
|
||||||
} else {
|
} else {
|
||||||
res, _ := Consume(v).Set(value, path[k+1:]...)
|
res, _ := ConsumeWithFetch(v, d.call).Set(value, path[k+1:]...)
|
||||||
if res.data != nil {
|
if res.data != nil {
|
||||||
out = append(out, res.data)
|
out = append(out, res.data)
|
||||||
}
|
}
|
||||||
|
@ -699,7 +702,7 @@ func (d *Doc) Del(path ...string) error {
|
||||||
d.Set(c, path[:len(path)-1]...)
|
d.Set(c, path[:len(path)-1]...)
|
||||||
} else {
|
} else {
|
||||||
if len(c) != 0 {
|
if len(c) != 0 {
|
||||||
return Consume(c[0]).Del(path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).Del(path[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -709,7 +712,7 @@ func (d *Doc) Del(path ...string) error {
|
||||||
d.Set(c, path[:len(path)-1]...)
|
d.Set(c, path[:len(path)-1]...)
|
||||||
} else {
|
} else {
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
Consume(v).Del(path[k+1:]...)
|
ConsumeWithFetch(v, d.call).Del(path[k+1:]...)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -766,7 +769,7 @@ func (d *Doc) ArrayAdd(value interface{}, path ...string) (*Doc, error) {
|
||||||
} else {
|
} else {
|
||||||
for _, v := range a {
|
for _, v := range a {
|
||||||
if reflect.DeepEqual(v, value) {
|
if reflect.DeepEqual(v, value) {
|
||||||
return Consume(a), nil
|
return ConsumeWithFetch(a, d.call), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a = append(a, value)
|
a = append(a, value)
|
||||||
|
@ -974,7 +977,7 @@ func (d *Doc) each(exec Iterator, prev []string) error {
|
||||||
var keep []string
|
var keep []string
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, k)
|
keep = append(keep, k)
|
||||||
Consume(v).each(exec, keep)
|
ConsumeWithFetch(v, d.call).each(exec, keep)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -989,7 +992,7 @@ func (d *Doc) each(exec Iterator, prev []string) error {
|
||||||
var keep []string
|
var keep []string
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, fmt.Sprintf("[%d]", i))
|
keep = append(keep, fmt.Sprintf("[%d]", i))
|
||||||
Consume(v).each(exec, keep)
|
ConsumeWithFetch(v, d.call).each(exec, keep)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1072,7 +1075,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 Consume(c[0]).walk(exec, keep, path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).walk(exec, keep, path[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,7 +1094,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 := Consume(v).walk(exec, keep, path[k+1:]...); err != nil {
|
if err := ConsumeWithFetch(v, d.call).walk(exec, keep, path[k+1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -872,36 +872,36 @@ func TestOperations(t *testing.T) {
|
||||||
err := doc.Del("the.item.arrays.0.id")
|
err := doc.Del("the.item.arrays.0.id")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(doc.Get("the.item.arrays.0.id").Data(), ShouldResemble, nil)
|
So(doc.Get("the.item.arrays.0.id").Data(), ShouldResemble, nil)
|
||||||
So(doc.Get("the.item.arrays.*.id").Data(), ShouldResemble, []interface{}{"ID2"})
|
So(doc.Get("the.item.arrays.*.id").Data(), ShouldResemble, []interface{}{nil, "ID2"})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can't del array → 5 → key", t, func() {
|
Convey("Can't del array → 5 → key", t, func() {
|
||||||
err := doc.Del("the.item.arrays.5.id")
|
err := doc.Del("the.item.arrays.5.id")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(doc.Get("the.item.arrays.*.id").Data(), ShouldResemble, []interface{}{"ID2"})
|
So(doc.Get("the.item.arrays.*.id").Data(), ShouldResemble, []interface{}{nil, "ID2"})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can del array → * → key", t, func() {
|
Convey("Can del array → * → key", t, func() {
|
||||||
err := doc.Del("the.item.arrays.*.id")
|
err := doc.Del("the.item.arrays.*.id")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(doc.Get("the.item.arrays.0.id").Data(), ShouldResemble, nil)
|
So(doc.Get("the.item.arrays.0.id").Data(), ShouldResemble, nil)
|
||||||
So(doc.Get("the.item.arrays.*.id").Data(), ShouldResemble, []interface{}{})
|
So(doc.Get("the.item.arrays.*.id").Data(), ShouldResemble, []interface{}{nil, nil})
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
Convey("Can get object → key", t, func() {
|
Convey("Can get object → key", t, func() {
|
||||||
So(doc.Get("the.item.arrays.*.one").Data(), ShouldResemble, []interface{}{"one"})
|
So(doc.Get("the.item.arrays.*.one").Data(), ShouldResemble, []interface{}{"one", nil})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can get object → key", t, func() {
|
Convey("Can get object → key", t, func() {
|
||||||
So(doc.Get("the.item.arrays.*.two").Data(), ShouldResemble, []interface{}{"two"})
|
So(doc.Get("the.item.arrays.*.two").Data(), ShouldResemble, []interface{}{nil, "two"})
|
||||||
})
|
})
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
Convey("Can get array → * → object → !", t, func() {
|
Convey("Can get array → * → object → !", t, func() {
|
||||||
So(doc.Get("the.item.arrays.*.selected.none").Data(), ShouldResemble, []interface{}{})
|
So(doc.Get("the.item.arrays.*.selected.none").Data(), ShouldResemble, []interface{}{nil, nil})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can set array → * → object → !", t, func() {
|
Convey("Can set array → * → object → !", t, func() {
|
||||||
|
|
Loading…
Reference in a new issue