-
-
Notifications
You must be signed in to change notification settings - Fork 171
/
remote_join.go
158 lines (127 loc) · 3.4 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
158
package core
import (
"bytes"
"context"
"errors"
"fmt"
"sync"
"github.com/dosco/graphjin/core/v3/internal/jsn"
"github.com/dosco/graphjin/core/v3/internal/qcode"
)
func (s *gstate) execRemoteJoin(c context.Context) (err error) {
// 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 := s.parentFieldIds()
if err != nil {
return
}
// fetch the field values of the marked insertion points
// these values contain the id to be used with fetching remote data
from := jsn.Get(s.data, fids)
if len(from) == 0 {
err = errors.New("something wrong no remote ids found in db response")
return
}
to, err := s.resolveRemotes(c, from, sfmap)
if err != nil {
return
}
var ob bytes.Buffer
if err = jsn.Replace(&ob, s.data, from, to); err != nil {
return
}
s.data = ob.Bytes()
return
}
func (s *gstate) resolveRemotes(
ctx context.Context,
from []jsn.Field,
sfmap map[string]*qcode.Select,
) ([]jsn.Field, error) {
selects := s.cs.st.qc.Selects
// 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
sel, ok := sfmap[string(id.Key)]
if !ok {
return nil, fmt.Errorf("invalid remote field key")
}
p := selects[sel.ParentID]
// then use the Table name in the Select and it's parent
// to find the resolver to use for this relationship
r, ok := s.gj.rmap[(sel.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, sel *qcode.Select) {
defer wg.Done()
// st := time.Now()
ctx1, span := s.gj.spanStart(ctx, "Execute Remote Request")
b, err := r.Fn.Resolve(ctx1, ResolverReq{
ID: string(id), Sel: sel, Log: s.gj.log, ReqConfig: s.r.rc,
})
if err != nil {
cerr = fmt.Errorf("%s: %s", sel.Table, err)
span.Error(cerr)
}
span.End()
if err != nil {
return
}
if len(r.Path) != 0 {
b = jsn.Strip(b, r.Path)
}
var ob bytes.Buffer
if len(sel.Fields) != 0 {
err = jsn.Filter(&ob, b, fieldsToList(sel.Fields))
if err != nil {
cerr = fmt.Errorf("%s: %w", sel.Table, err)
return
}
} else {
ob.WriteString("null")
}
to[n] = jsn.Field{Key: []byte(sel.FieldName), Value: ob.Bytes()}
}(i, id, sel)
}
wg.Wait()
return to, cerr
}
func (s *gstate) parentFieldIds() ([][]byte, map[string]*qcode.Select, error) {
selects := s.cs.st.qc.Selects
remotes := s.cs.st.qc.Remotes
// 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, sel := range selects {
if sel.SkipRender != qcode.SkipTypeRemote {
continue
}
p := selects[sel.ParentID]
if r, ok := s.gj.rmap[(sel.Table + p.Table)]; ok {
fm = append(fm, r.IDField)
sm[string(r.IDField)] = &selects[i]
}
}
return fm, sm, nil
}
func fieldsToList(fields []qcode.Field) []string {
var f []string
for _, col := range fields {
f = append(f, col.FieldName)
}
return f
}