2019-11-25 08:22:33 +01:00
|
|
|
package serv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/cespare/xxhash/v2"
|
|
|
|
"github.com/dosco/super-graph/jsn"
|
|
|
|
"github.com/dosco/super-graph/qcode"
|
|
|
|
)
|
|
|
|
|
|
|
|
func execRemoteJoin(st *stmt, data []byte, hdr http.Header) ([]byte, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if len(data) == 0 || st.skipped == 0 {
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sel := st.qc.Selects
|
|
|
|
h := xxhash.New()
|
|
|
|
|
|
|
|
// fetch the field name used within the db response json
|
|
|
|
// that are used to mark insertion points and the mapping between
|
|
|
|
// those field names and their select objects
|
|
|
|
fids, sfmap := parentFieldIds(h, sel, st.skipped)
|
|
|
|
|
|
|
|
// fetch the field values of the marked insertion points
|
|
|
|
// these values contain the id to be used with fetching remote data
|
|
|
|
from := jsn.Get(data, fids)
|
|
|
|
var to []jsn.Field
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case len(from) == 1:
|
|
|
|
to, err = resolveRemote(hdr, h, from[0], sel, sfmap)
|
|
|
|
|
|
|
|
case len(from) > 1:
|
|
|
|
to, err = resolveRemotes(hdr, h, from, sel, sfmap)
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, errors.New("something wrong no remote ids found in db response")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ob bytes.Buffer
|
|
|
|
|
|
|
|
err = jsn.Replace(&ob, data, from, to)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ob.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resolveRemote(
|
|
|
|
hdr http.Header,
|
|
|
|
h *xxhash.Digest,
|
|
|
|
field jsn.Field,
|
|
|
|
sel []qcode.Select,
|
|
|
|
sfmap map[uint64]*qcode.Select) ([]jsn.Field, error) {
|
|
|
|
|
|
|
|
// replacement data for the marked insertion points
|
|
|
|
// key and value will be replaced by whats below
|
|
|
|
toA := [1]jsn.Field{}
|
|
|
|
to := toA[:1]
|
|
|
|
|
|
|
|
// use the json key to find the related Select object
|
|
|
|
k1 := xxhash.Sum64(field.Key)
|
|
|
|
|
|
|
|
s, ok := sfmap[k1]
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
p := sel[s.ParentID]
|
|
|
|
|
|
|
|
// then use the Table nme in the Select and it's parent
|
|
|
|
// to find the resolver to use for this relationship
|
2019-11-29 07:38:23 +01:00
|
|
|
k2 := mkkey(h, s.Name, p.Name)
|
2019-11-25 08:22:33 +01:00
|
|
|
|
|
|
|
r, ok := rmap[k2]
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
id := jsn.Value(field.Value)
|
|
|
|
if len(id) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//st := time.Now()
|
|
|
|
|
|
|
|
b, err := r.Fn(hdr, id)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.Path) != 0 {
|
|
|
|
b = jsn.Strip(b, r.Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
var ob bytes.Buffer
|
|
|
|
|
|
|
|
if len(s.Cols) != 0 {
|
|
|
|
err = jsn.Filter(&ob, b, colsToList(s.Cols))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
ob.WriteString("null")
|
|
|
|
}
|
|
|
|
|
|
|
|
to[0] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
|
|
|
|
return to, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resolveRemotes(
|
|
|
|
hdr http.Header,
|
|
|
|
h *xxhash.Digest,
|
|
|
|
from []jsn.Field,
|
|
|
|
sel []qcode.Select,
|
|
|
|
sfmap map[uint64]*qcode.Select) ([]jsn.Field, error) {
|
|
|
|
|
|
|
|
// replacement data for the marked insertion points
|
|
|
|
// key and value will be replaced by whats below
|
|
|
|
to := make([]jsn.Field, len(from))
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(len(from))
|
|
|
|
|
|
|
|
var cerr error
|
|
|
|
|
|
|
|
for i, id := range from {
|
|
|
|
|
|
|
|
// use the json key to find the related Select object
|
|
|
|
k1 := xxhash.Sum64(id.Key)
|
|
|
|
|
|
|
|
s, ok := sfmap[k1]
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
p := sel[s.ParentID]
|
|
|
|
|
|
|
|
// then use the Table nme in the Select and it's parent
|
|
|
|
// to find the resolver to use for this relationship
|
2019-11-29 07:38:23 +01:00
|
|
|
k2 := mkkey(h, s.Name, p.Name)
|
2019-11-25 08:22:33 +01:00
|
|
|
|
|
|
|
r, ok := rmap[k2]
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
id := jsn.Value(id.Value)
|
|
|
|
if len(id) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
go func(n int, id []byte, s *qcode.Select) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
//st := time.Now()
|
|
|
|
|
|
|
|
b, err := r.Fn(hdr, id)
|
|
|
|
if err != nil {
|
2019-11-29 07:38:23 +01:00
|
|
|
cerr = fmt.Errorf("%s: %s", s.Name, err)
|
2019-11-25 08:22:33 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.Path) != 0 {
|
|
|
|
b = jsn.Strip(b, r.Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
var ob bytes.Buffer
|
|
|
|
|
|
|
|
if len(s.Cols) != 0 {
|
|
|
|
err = jsn.Filter(&ob, b, colsToList(s.Cols))
|
|
|
|
if err != nil {
|
2019-11-29 07:38:23 +01:00
|
|
|
cerr = fmt.Errorf("%s: %s", s.Name, err)
|
2019-11-25 08:22:33 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
ob.WriteString("null")
|
|
|
|
}
|
|
|
|
|
|
|
|
to[n] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
|
|
|
|
}(i, id, s)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return to, cerr
|
|
|
|
}
|