-
-
Notifications
You must be signed in to change notification settings - Fork 179
/
remote_join.go
157 lines (121 loc) · 3.27 KB
/
remote_join.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package core
import (
"bytes"
"errors"
"fmt"
"sync"
"github.com/dosco/graphjin/core/internal/qcode"
"github.com/dosco/graphjin/internal/jsn"
)
func (c *scontext) execRemoteJoin(res qres) (qres, error) {
var err error
sel := res.q.st.qc.Selects
// 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, err := c.parentFieldIds(sel, res.q.st.qc.Remotes)
if err != nil {
return res, err
}
// fetch the field values of the marked insertion points
// these values contain the id to be used with fetching remote data
from := jsn.Get(res.data, fids)
var to []jsn.Field
if len(from) == 0 {
return res, errors.New("something wrong no remote ids found in db response")
}
to, err = c.resolveRemotes(from, sel, sfmap)
if err != nil {
return res, err
}
var ob bytes.Buffer
err = jsn.Replace(&ob, res.data, from, to)
if err != nil {
return res, err
}
res.data = ob.Bytes()
return res, nil
}
func (c *scontext) resolveRemotes(
from []jsn.Field,
sel []qcode.Select,
sfmap map[string]*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
s, ok := sfmap[string(id.Key)]
if !ok {
return nil, fmt.Errorf("invalid remote field key")
}
p := sel[s.ParentID]
// then use the Table name in the Select and it's parent
// to find the resolver to use for this relationship
r, ok := c.gj.rmap[(s.Table + p.Table)]
if !ok {
return nil, fmt.Errorf("no resolver found")
}
id := jsn.Value(id.Value)
if len(id) == 0 {
return nil, fmt.Errorf("invalid remote field id")
}
go func(n int, id []byte, s *qcode.Select) {
defer wg.Done()
//st := time.Now()
b, err := r.Fn.Resolve(ResolverReq{
ID: string(id), Sel: s, Log: c.gj.log, ReqConfig: c.rc})
if err != nil {
cerr = fmt.Errorf("%s: %s", s.Table, err)
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 {
cerr = fmt.Errorf("%s: %w", s.Table, err)
return
}
} else {
ob.WriteString("null")
}
to[n] = jsn.Field{Key: []byte(s.FieldName), Value: ob.Bytes()}
}(i, id, s)
}
wg.Wait()
return to, cerr
}
func (c *scontext) parentFieldIds(sel []qcode.Select, remotes int32) (
[][]byte, map[string]*qcode.Select, error) {
// list of keys (and it's related value) to extract from
// the db json response
fm := make([][]byte, 0, remotes)
// mapping between the above extracted key and a Select
// object
sm := make(map[string]*qcode.Select, remotes)
for i := range sel {
s := &sel[i]
if s.SkipRender != qcode.SkipTypeRemote {
continue
}
p := sel[s.ParentID]
if r, ok := c.gj.rmap[(s.Table + p.Table)]; ok {
fm = append(fm, r.IDField)
sm[string(r.IDField)] = s
}
}
return fm, sm, nil
}
func colsToList(cols []qcode.Column) []string {
var f []string
for _, col := range cols {
f = append(f, col.FieldName)
}
return f
}