Add SQL FETCH functionality to SELECT statements
This commit is contained in:
parent
35047ce04c
commit
1f30035899
10 changed files with 240 additions and 36 deletions
42
db/fetch.go
42
db/fetch.go
|
@ -84,17 +84,18 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
return val, queryIdentFailed
|
return val, queryIdentFailed
|
||||||
case doc != nil:
|
case doc != nil:
|
||||||
|
|
||||||
doc.Fetch(func(key string, val interface{}) interface{} {
|
doc.Fetch(func(key string, val interface{}, path []string) interface{} {
|
||||||
switch res := val.(type) {
|
if len(path) > 0 {
|
||||||
case []interface{}:
|
switch res := val.(type) {
|
||||||
val, _ = e.fetchArray(ctx, res, doc)
|
case []interface{}:
|
||||||
return val
|
val, _ = e.fetchArray(ctx, res, doc)
|
||||||
case *sql.Thing:
|
return val
|
||||||
val, _ = e.fetchThing(ctx, res, doc)
|
case *sql.Thing:
|
||||||
return val
|
val, _ = e.fetchThing(ctx, res, doc)
|
||||||
default:
|
return val
|
||||||
return val
|
}
|
||||||
}
|
}
|
||||||
|
return val
|
||||||
})
|
})
|
||||||
|
|
||||||
return e.fetch(ctx, doc.Get(val.ID).Data(), doc)
|
return e.fetch(ctx, doc.Get(val.ID).Data(), doc)
|
||||||
|
@ -107,17 +108,18 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
|
|
||||||
if obj, ok := ctx.Value(s).(*data.Doc); ok {
|
if obj, ok := ctx.Value(s).(*data.Doc); ok {
|
||||||
|
|
||||||
obj.Fetch(func(key string, val interface{}) interface{} {
|
obj.Fetch(func(key string, val interface{}, path []string) interface{} {
|
||||||
switch res := val.(type) {
|
if len(path) > 0 {
|
||||||
case []interface{}:
|
switch res := val.(type) {
|
||||||
val, _ = e.fetchArray(ctx, res, doc)
|
case []interface{}:
|
||||||
return val
|
val, _ = e.fetchArray(ctx, res, doc)
|
||||||
case *sql.Thing:
|
return val
|
||||||
val, _ = e.fetchThing(ctx, res, doc)
|
case *sql.Thing:
|
||||||
return val
|
val, _ = e.fetchThing(ctx, res, doc)
|
||||||
default:
|
return val
|
||||||
return val
|
}
|
||||||
}
|
}
|
||||||
|
return val
|
||||||
})
|
})
|
||||||
|
|
||||||
if res := obj.Get(val.ID).Data(); res != nil {
|
if res := obj.Get(val.ID).Data(); res != nil {
|
||||||
|
|
|
@ -1766,6 +1766,58 @@ func TestSelect(t *testing.T) {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Fetch records using a fetchplan to fetch remote records easily", t, func() {
|
||||||
|
|
||||||
|
setupDB()
|
||||||
|
|
||||||
|
txt := `
|
||||||
|
USE NS test DB test;
|
||||||
|
CREATE person:test SET
|
||||||
|
one=tester:test,
|
||||||
|
mult=[],
|
||||||
|
mult+=temper:one,
|
||||||
|
mult+=temper:two,
|
||||||
|
mult+=temper:tre
|
||||||
|
;
|
||||||
|
CREATE tester:test SET tags=["some","tags"];
|
||||||
|
CREATE temper:one SET tester=tester:test;
|
||||||
|
CREATE temper:two SET tester=tester:test;
|
||||||
|
CREATE temper:tre SET tester=tester:test;
|
||||||
|
SELECT * FROM person:test FETCH one, mult, mult.*.tester;
|
||||||
|
`
|
||||||
|
|
||||||
|
res, err := Execute(setupKV(), txt, nil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldHaveLength, 7)
|
||||||
|
So(res[1].Result, ShouldHaveLength, 1)
|
||||||
|
So(res[2].Result, ShouldHaveLength, 1)
|
||||||
|
So(res[3].Result, ShouldHaveLength, 1)
|
||||||
|
So(res[4].Result, ShouldHaveLength, 1)
|
||||||
|
So(res[5].Result, ShouldHaveLength, 1)
|
||||||
|
So(res[6].Result, ShouldHaveLength, 1)
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("meta.id").Data(), ShouldEqual, "test")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("meta.tb").Data(), ShouldEqual, "person")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("one.meta.id").Data(), ShouldEqual, "test")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("one.meta.tb").Data(), ShouldEqual, "tester")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("one.tags").Data(), ShouldResemble, []interface{}{"some", "tags"})
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[0].meta.id").Data(), ShouldEqual, "one")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[0].meta.tb").Data(), ShouldEqual, "temper")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[0].tester.meta.id").Data(), ShouldEqual, "test")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[0].tester.meta.tb").Data(), ShouldEqual, "tester")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[0].tester.tags").Data(), ShouldResemble, []interface{}{"some", "tags"})
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[1].meta.id").Data(), ShouldEqual, "two")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[1].meta.tb").Data(), ShouldEqual, "temper")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[1].tester.meta.id").Data(), ShouldEqual, "test")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[1].tester.meta.tb").Data(), ShouldEqual, "tester")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[1].tester.tags").Data(), ShouldResemble, []interface{}{"some", "tags"})
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[2].meta.id").Data(), ShouldEqual, "tre")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[2].meta.tb").Data(), ShouldEqual, "temper")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[2].tester.meta.id").Data(), ShouldEqual, "test")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[2].tester.meta.tb").Data(), ShouldEqual, "tester")
|
||||||
|
So(data.Consume(res[6].Result[0]).Get("mult[2].tester.tags").Data(), ShouldResemble, []interface{}{"some", "tags"})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Version records using a datetime", t, func() {
|
Convey("Version records using a datetime", t, func() {
|
||||||
|
|
||||||
setupDB()
|
setupDB()
|
||||||
|
|
46
db/yield.go
46
db/yield.go
|
@ -88,6 +88,7 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
||||||
|
|
||||||
var exps sql.Fields
|
var exps sql.Fields
|
||||||
var grps sql.Groups
|
var grps sql.Groups
|
||||||
|
var fchs sql.Fetchs
|
||||||
|
|
||||||
switch stm := stm.(type) {
|
switch stm := stm.(type) {
|
||||||
case *sql.LiveStatement:
|
case *sql.LiveStatement:
|
||||||
|
@ -95,6 +96,7 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
||||||
case *sql.SelectStatement:
|
case *sql.SelectStatement:
|
||||||
exps = stm.Expr
|
exps = stm.Expr
|
||||||
grps = stm.Group
|
grps = stm.Group
|
||||||
|
fchs = stm.Fetch
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are no field expressions
|
// If there are no field expressions
|
||||||
|
@ -170,6 +172,10 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First of all, check to see if an ALL
|
||||||
|
// expression has been specified, and if
|
||||||
|
// it has then use the full document.
|
||||||
|
|
||||||
for _, e := range exps {
|
for _, e := range exps {
|
||||||
if _, ok := e.Expr.(*sql.All); ok {
|
if _, ok := e.Expr.(*sql.All); ok {
|
||||||
out = doc
|
out = doc
|
||||||
|
@ -177,6 +183,10 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next let's see the field expressions
|
||||||
|
// which have been requested, and add
|
||||||
|
// these to the output document.
|
||||||
|
|
||||||
for _, e := range exps {
|
for _, e := range exps {
|
||||||
|
|
||||||
switch v := e.Expr.(type) {
|
switch v := e.Expr.(type) {
|
||||||
|
@ -204,22 +214,52 @@ func (d *document) yield(ctx context.Context, stm sql.Statement, output sql.Toke
|
||||||
// calculate the value to be inserted into
|
// calculate the value to be inserted into
|
||||||
// the final output document.
|
// the final output document.
|
||||||
|
|
||||||
v, err := d.i.e.fetch(ctx, v, doc)
|
o, err := d.i.e.fetch(ctx, v, doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v {
|
switch o {
|
||||||
case doc:
|
case doc:
|
||||||
out.Set(nil, e.Field)
|
out.Set(nil, e.Field)
|
||||||
default:
|
default:
|
||||||
out.Set(v, e.Field)
|
out.Set(o, e.Field)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finally let's see if there are any
|
||||||
|
// FETCH expressions, so that we can
|
||||||
|
// follow links to other records.
|
||||||
|
|
||||||
|
for _, e := range fchs {
|
||||||
|
|
||||||
|
switch v := e.Expr.(type) {
|
||||||
|
case *sql.All:
|
||||||
|
break
|
||||||
|
case *sql.Ident:
|
||||||
|
|
||||||
|
out.Walk(func(key string, val interface{}) error {
|
||||||
|
|
||||||
|
switch res := val.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
val, _ = d.i.e.fetchArray(ctx, res, doc)
|
||||||
|
out.Set(val, key)
|
||||||
|
case *sql.Thing:
|
||||||
|
val, _ = d.i.e.fetchThing(ctx, res, doc)
|
||||||
|
out.Set(val, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}, v.ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return out.Data(), nil
|
return out.Data(), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,6 +173,7 @@ type SelectStatement struct {
|
||||||
Order Orders `cork:"order" codec:"order"`
|
Order Orders `cork:"order" codec:"order"`
|
||||||
Limit Expr `cork:"limit" codec:"limit"`
|
Limit Expr `cork:"limit" codec:"limit"`
|
||||||
Start Expr `cork:"start" codec:"start"`
|
Start Expr `cork:"start" codec:"start"`
|
||||||
|
Fetch Fetchs `cork:"fetch" codec:"fetch"`
|
||||||
Version Expr `cork:"version" codec:"version"`
|
Version Expr `cork:"version" codec:"version"`
|
||||||
Timeout time.Duration `cork:"timeout" codec:"timeout"`
|
Timeout time.Duration `cork:"timeout" codec:"timeout"`
|
||||||
Parallel int `cork:"parallel" codec:"parallel"`
|
Parallel int `cork:"parallel" codec:"parallel"`
|
||||||
|
@ -524,6 +525,14 @@ type Order struct {
|
||||||
// Orders represents multiple ORDER BY clauses.
|
// Orders represents multiple ORDER BY clauses.
|
||||||
type Orders []*Order
|
type Orders []*Order
|
||||||
|
|
||||||
|
// Fetch represents a FETCH AS clause.
|
||||||
|
type Fetch struct {
|
||||||
|
Expr Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetchs represents multiple FETCH AS clauses.
|
||||||
|
type Fetchs []*Fetch
|
||||||
|
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
// Expressions
|
// Expressions
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
22
sql/cork.go
22
sql/cork.go
|
@ -234,6 +234,28 @@ func (this *Order) UnmarshalCORK(r *cork.Reader) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// FETCH
|
||||||
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cork.Register(&Fetch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Fetch) ExtendCORK() byte {
|
||||||
|
return 0x11
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Fetch) MarshalCORK(w *cork.Writer) (dst []byte, err error) {
|
||||||
|
w.EncodeAny(this.Expr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Fetch) UnmarshalCORK(r *cork.Reader) (err error) {
|
||||||
|
r.DecodeAny(&this.Expr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// ##################################################
|
// ##################################################
|
||||||
// ##################################################
|
// ##################################################
|
||||||
// ##################################################
|
// ##################################################
|
||||||
|
|
|
@ -55,6 +55,10 @@ func (p *parser) parseSelectStatement() (stmt *SelectStatement, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stmt.Fetch, err = p.parseFetch(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if stmt.Version, err = p.parseVersion(); err != nil {
|
if stmt.Version, err = p.parseVersion(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -313,6 +317,52 @@ func (p *parser) parseStart() (Expr, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseFetch() (mul Fetchs, err error) {
|
||||||
|
|
||||||
|
// The next token that we expect to see is a
|
||||||
|
// GROUP token, and if we don't find one then
|
||||||
|
// return nil, with no error.
|
||||||
|
|
||||||
|
if _, _, exi := p.mightBe(FETCH); !exi {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
var tok Token
|
||||||
|
var lit string
|
||||||
|
|
||||||
|
one := &Fetch{}
|
||||||
|
|
||||||
|
tok, lit, err = p.shouldBe(IDENT, EXPR)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
one.Expr, err = p.declare(tok, lit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the single expression to the array
|
||||||
|
// of return statement expressions.
|
||||||
|
|
||||||
|
mul = append(mul, one)
|
||||||
|
|
||||||
|
// Check to see if the next token is a comma
|
||||||
|
// and if not, then break out of the loop,
|
||||||
|
// otherwise repeat until we find no comma.
|
||||||
|
|
||||||
|
if _, _, exi := p.mightBe(COMMA); !exi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (p *parser) parseVersion() (Expr, error) {
|
func (p *parser) parseVersion() (Expr, error) {
|
||||||
|
|
||||||
if _, _, exi := p.mightBe(VERSION, ON); !exi {
|
if _, _, exi := p.mightBe(VERSION, ON); !exi {
|
||||||
|
|
|
@ -214,7 +214,7 @@ func (this KillStatement) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this SelectStatement) String() string {
|
func (this SelectStatement) String() string {
|
||||||
return print("SELECT %v FROM %v%v%v%v%v%v%v%v",
|
return print("SELECT %v FROM %v%v%v%v%v%v%v%v%v",
|
||||||
this.Expr,
|
this.Expr,
|
||||||
this.What,
|
this.What,
|
||||||
maybe(this.Cond != nil, print(" WHERE %v", this.Cond)),
|
maybe(this.Cond != nil, print(" WHERE %v", this.Cond)),
|
||||||
|
@ -222,6 +222,7 @@ func (this SelectStatement) String() string {
|
||||||
this.Order,
|
this.Order,
|
||||||
maybe(this.Limit != nil, print(" LIMIT %v", this.Limit)),
|
maybe(this.Limit != nil, print(" LIMIT %v", this.Limit)),
|
||||||
maybe(this.Start != nil, print(" START %v", this.Start)),
|
maybe(this.Start != nil, print(" START %v", this.Start)),
|
||||||
|
this.Fetch,
|
||||||
maybe(this.Version != nil, print(" VERSION %v", this.Version)),
|
maybe(this.Version != nil, print(" VERSION %v", this.Version)),
|
||||||
maybe(this.Timeout > 0, print(" TIMEOUT %v", this.Timeout.String())),
|
maybe(this.Timeout > 0, print(" TIMEOUT %v", this.Timeout.String())),
|
||||||
)
|
)
|
||||||
|
@ -526,6 +527,29 @@ func (this Order) String() string {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------
|
||||||
|
// Fetch
|
||||||
|
// ---------------------------------------------
|
||||||
|
|
||||||
|
func (this Fetchs) String() string {
|
||||||
|
if len(this) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
m := make([]string, len(this))
|
||||||
|
for k, v := range this {
|
||||||
|
m[k] = v.String()
|
||||||
|
}
|
||||||
|
return print(" FETCH %v",
|
||||||
|
strings.Join(m, ", "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this Fetch) String() string {
|
||||||
|
return print("%v",
|
||||||
|
this.Expr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
// Model
|
// Model
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
|
|
|
@ -133,6 +133,7 @@ const (
|
||||||
EVENT
|
EVENT
|
||||||
EXPUNGE
|
EXPUNGE
|
||||||
FALSE
|
FALSE
|
||||||
|
FETCH
|
||||||
FIELD
|
FIELD
|
||||||
FOR
|
FOR
|
||||||
FROM
|
FROM
|
||||||
|
@ -304,6 +305,7 @@ var tokens = [...]string{
|
||||||
EVENT: "EVENT",
|
EVENT: "EVENT",
|
||||||
EXPUNGE: "EXPUNGE",
|
EXPUNGE: "EXPUNGE",
|
||||||
FALSE: "FALSE",
|
FALSE: "FALSE",
|
||||||
|
FETCH: "FETCH",
|
||||||
FIELD: "FIELD",
|
FIELD: "FIELD",
|
||||||
FOR: "FOR",
|
FOR: "FOR",
|
||||||
FROM: "FROM",
|
FROM: "FROM",
|
||||||
|
|
|
@ -43,7 +43,7 @@ type Doc struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetcher is used when fetching values.
|
// Fetcher is used when fetching values.
|
||||||
type Fetcher func(key string, val interface{}) interface{}
|
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
|
||||||
|
@ -430,16 +430,16 @@ func (d *Doc) Exists(path ...string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == one {
|
if r == one {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil {
|
||||||
c[0] = d.call(p, c[0])
|
c[0] = d.call(p, c[0], path[k+1:])
|
||||||
}
|
}
|
||||||
return ConsumeWithFetch(c[0], d.call).Exists(path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).Exists(path[k+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == many {
|
if r == many {
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil {
|
||||||
v = d.call(p, v)
|
v = d.call(p, v, path[k+1:])
|
||||||
}
|
}
|
||||||
if !ConsumeWithFetch(v, d.call).Exists(path[k+1:]...) {
|
if !ConsumeWithFetch(v, d.call).Exists(path[k+1:]...) {
|
||||||
return false
|
return false
|
||||||
|
@ -494,8 +494,8 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
if m, ok := object.(map[string]interface{}); ok {
|
if m, ok := object.(map[string]interface{}); ok {
|
||||||
switch p {
|
switch p {
|
||||||
default:
|
default:
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil {
|
||||||
object = d.call(p, m[p])
|
object = d.call(p, m[p], path[k+1:])
|
||||||
} else {
|
} else {
|
||||||
object = m[p]
|
object = m[p]
|
||||||
}
|
}
|
||||||
|
@ -518,8 +518,8 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == one {
|
if r == one {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil {
|
||||||
c[0] = d.call(p, c[0])
|
c[0] = d.call(p, c[0], path[k+1:])
|
||||||
}
|
}
|
||||||
return ConsumeWithFetch(c[0], d.call).Get(path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).Get(path[k+1:]...)
|
||||||
}
|
}
|
||||||
|
@ -527,8 +527,8 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
if r == many {
|
if r == many {
|
||||||
out := []interface{}{}
|
out := []interface{}{}
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
if d.call != nil && len(path[k+1:]) > 0 {
|
if d.call != nil {
|
||||||
v = d.call(p, v)
|
v = d.call(p, v, path[k+1:])
|
||||||
}
|
}
|
||||||
res := ConsumeWithFetch(v, d.call).Get(path[k+1:]...)
|
res := ConsumeWithFetch(v, d.call).Get(path[k+1:]...)
|
||||||
out = append(out, res.data)
|
out = append(out, res.data)
|
||||||
|
@ -610,6 +610,7 @@ func (d *Doc) Set(value interface{}, path ...string) (*Doc, error) {
|
||||||
if k == len(path)-1 {
|
if k == len(path)-1 {
|
||||||
a[i[0]] = value
|
a[i[0]] = value
|
||||||
object = a[i[0]]
|
object = a[i[0]]
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
return ConsumeWithFetch(a[i[0]], d.call).Set(value, path[k+1:]...)
|
return ConsumeWithFetch(a[i[0]], d.call).Set(value, path[k+1:]...)
|
||||||
}
|
}
|
||||||
|
@ -700,6 +701,7 @@ func (d *Doc) Del(path ...string) error {
|
||||||
if r == one {
|
if r == one {
|
||||||
if k == len(path)-1 {
|
if k == len(path)-1 {
|
||||||
d.Set(c, path[:len(path)-1]...)
|
d.Set(c, path[:len(path)-1]...)
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
if len(c) != 0 {
|
if len(c) != 0 {
|
||||||
return ConsumeWithFetch(c[0], d.call).Del(path[k+1:]...)
|
return ConsumeWithFetch(c[0], d.call).Del(path[k+1:]...)
|
||||||
|
@ -710,6 +712,7 @@ func (d *Doc) Del(path ...string) error {
|
||||||
if r == many {
|
if r == many {
|
||||||
if k == len(path)-1 {
|
if k == len(path)-1 {
|
||||||
d.Set(c, path[:len(path)-1]...)
|
d.Set(c, path[:len(path)-1]...)
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
ConsumeWithFetch(v, d.call).Del(path[k+1:]...)
|
ConsumeWithFetch(v, d.call).Del(path[k+1:]...)
|
||||||
|
|
|
@ -303,7 +303,7 @@ func TestOperations(t *testing.T) {
|
||||||
// ----------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
Convey("Can set fetcher function", t, func() {
|
Convey("Can set fetcher function", t, func() {
|
||||||
doc.Fetch(func(key string, val interface{}) interface{} {
|
doc.Fetch(func(key string, val interface{}, path []string) interface{} {
|
||||||
return val
|
return val
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue