• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dgraph-io / dgraph / 8399783456

23 Mar 2024 05:41AM UTC coverage: 65.174% (-1.5%) from 66.714%
8399783456

push

web-flow
upgrade(go): update go version to 1.22 (#9058)

fixes tests to use `docker compose` instead of `docker-compose`

---------

Co-authored-by: Aman Mangal <aman@dgraph.io>
Co-authored-by: Harshil Goel <harshil@dgraph.io>

0 of 5 new or added lines in 1 file covered. (0.0%)

7452 existing lines in 20 files now uncovered.

59310 of 91002 relevant lines covered (65.17%)

1775304.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.93
/query/outputnode.go
1
/*
2
 * Copyright 2017-2023 Dgraph Labs, Inc. and Contributors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package query
18

19
import (
20
        "bytes"
21
        "context"
22
        "encoding/binary"
23
        "encoding/json"
24
        "fmt"
25
        "math"
26
        "strconv"
27
        "strings"
28
        "sync"
29
        "time"
30
        "unicode/utf8"
31
        "unsafe"
32

33
        "github.com/golang/glog"
34
        "github.com/pkg/errors"
35
        "github.com/twpayne/go-geom"
36
        "github.com/twpayne/go-geom/encoding/geojson"
37

38
        "github.com/dgraph-io/dgo/v230/protos/api"
39
        "github.com/dgraph-io/dgraph/algo"
40
        gqlSchema "github.com/dgraph-io/dgraph/graphql/schema"
41
        "github.com/dgraph-io/dgraph/protos/pb"
42
        "github.com/dgraph-io/dgraph/task"
43
        "github.com/dgraph-io/dgraph/types"
44
        "github.com/dgraph-io/dgraph/types/facets"
45
        "github.com/dgraph-io/dgraph/x"
46
        "github.com/dgraph-io/ristretto/z"
47
)
48

49
// ToJson converts the list of subgraph into a JSON response by calling toFastJSON.
50
func ToJson(ctx context.Context, l *Latency, sgl []*SubGraph, field gqlSchema.Field) ([]byte,
51
        error) {
39,292✔
52
        sgr := &SubGraph{}
39,292✔
53
        for _, sg := range sgl {
86,425✔
54
                if sg.Params.Alias == "var" || sg.Params.Alias == "shortest" {
64,216✔
55
                        continue
17,083✔
56
                }
57
                if sg.Params.GetUid {
30,060✔
58
                        sgr.Params.GetUid = true
10✔
59
                }
10✔
60
                sgr.Children = append(sgr.Children, sg)
30,050✔
61
        }
62
        data, err := sgr.toFastJSON(ctx, l, field)
39,292✔
63

39,292✔
64
        // don't log or wrap GraphQL errors
39,292✔
65
        if x.IsGqlErrorList(err) {
39,303✔
66
                return data, err
11✔
67
        }
11✔
68
        if err != nil {
39,283✔
69
                glog.Errorf("while running ToJson: %v\n", err)
2✔
70
        }
2✔
71
        return data, errors.Wrapf(err, "while running ToJson")
39,281✔
72
}
73

74
// We are capping maxEncoded size to 4GB, as grpc encoding fails
75
// for a response size > math.MaxUint32.
76
const maxEncodedSize = uint64(4 << 30)
77

78
type encoder struct {
79
        // attrMap has mapping of string predicates to uint16 ids.
80
        // For each predicate one unique id is assigned to save space.
81
        attrMap map[string]uint16
82
        // idSlice contains mapping from predicate id to predicate.
83
        idSlice []string
84
        // arena is used to store scalarVal for fastJsonNodes. Offset of scalarVal inside arena buffer
85
        // is stored in fastJsonNode meta.
86
        arena *arena
87
        // curSize is current estimated size of the encoded response. It should be less than actual
88
        // response size after encoding. If curSize exceeds a threshold size(maxEncodedSize), we return
89
        // query response with error saying response is too big. Currently curSize tracking has been
90
        // kept very simple. curSize is crossing threshold value or not is only checked at leaf(scalar)
91
        // nodes as of now. curSize is updated in following cases:
92
        // 1. By adding predicate len, while expanding it for an uid in preTraverse().
93
        // 2. By adding scalarVal len in setScalarVal function for a leaf(scalar) node.
94
        // TODO(Ashish): currently we are not including facets/groupby/aggregations fields in curSize
95
        // for simplicity. curSize can be made more accurate by adding these fields.
96
        curSize uint64
97

98
        // Allocator for nodes.
99
        alloc *z.Allocator
100

101
        // Cache uid attribute, which is very commonly used.
102
        uidAttr uint16
103

104
        // buf is the buffer which stores the JSON encoded response
105
        buf *bytes.Buffer
106
}
107

108
type node struct {
109
        // meta stores meta information for a fastJsonNode in an uint64. Layout is as follows.
110
        // Bytes 4-1 contains offset(uint32) for Arena.
111
        // Bytes 7-6 contains attr.
112
        // Bit MSB(first bit in Byte-8) contains list field value.
113
        // Bit SecondMSB(second bit in Byte-8) contains facetsParent field value.
114
        // Bit ThirdMSB(third bit in Byte-8) stores if the node contains uid value
115
        // Bit FourthMSB(fourth bit in Byte-8) stores if the order of node's children has been fixed.
116
        // Bit FifthMSB(fifth bit in Byte-8) stores if node contains value for a @custom GraphQL field.
117
        // Byte-5 is not getting used as of now.
118
        // |-----------------------------------------------------------------------------|
119
        // |             8              |    7   |    6   |    5   |  4  |  3  |  2 |  1 |
120
        // |-----------------------------------------------------------------------------|
121
        // | MSB - list                 |                 | Unused |                     |
122
        // | SecondMSB - facetsParent   |     Attr ID     | For    | Offset inside Arena |
123
        // | ThirdMSB - uid             |                 | Now    |                     |
124
        // | FourthMSB - Order Info     |                 |        |                     |
125
        // | FifthMSB - @custom GraphQL |                 |        |                     |
126
        // |-----------------------------------------------------------------------------|
127
        meta uint64
128

129
        next  *node
130
        child *node
131
}
132

133
var nodeSize = int(unsafe.Sizeof(node{}))
134

135
func newEncoder() *encoder {
39,296✔
136
        idSlice := make([]string, 1)
39,296✔
137

39,296✔
138
        a := (arenaPool.Get()).(*arena)
39,296✔
139
        a.reset()
39,296✔
140

39,296✔
141
        e := &encoder{
39,296✔
142
                attrMap: make(map[string]uint16),
39,296✔
143
                idSlice: idSlice,
39,296✔
144
                arena:   a,
39,296✔
145
                alloc:   z.NewAllocator(4<<10, "OutputNode.Encoder"),
39,296✔
146
                buf:     &bytes.Buffer{},
39,296✔
147
        }
39,296✔
148
        e.uidAttr = e.idForAttr("uid")
39,296✔
149
        return e
39,296✔
150
}
39,296✔
151

152
// Sort the given fastJson list
153
func (enc *encoder) MergeSort(headRef *fastJsonNode) {
506✔
154
        if headRef == nil || (*headRef).next == nil {
811✔
155
                return
305✔
156
        }
305✔
157

158
        var a, b fastJsonNode
201✔
159
        frontBackSplit(*headRef, &a, &b)
201✔
160
        enc.MergeSort(&a)
201✔
161
        enc.MergeSort(&b)
201✔
162
        *headRef = enc.mergeSortedLists(a, b)
201✔
163
}
164

165
func (enc *encoder) mergeSortedLists(a fastJsonNode, b fastJsonNode) fastJsonNode {
500✔
166
        var result fastJsonNode
500✔
167

500✔
168
        if a == nil {
575✔
169
                return b
75✔
170
        } else if b == nil {
626✔
171
                return a
126✔
172
        }
126✔
173

174
        if enc.less(a, b) {
453✔
175
                result = a
154✔
176
                result.next = enc.mergeSortedLists(a.next, b)
154✔
177
        } else {
299✔
178
                result = b
145✔
179
                result.next = enc.mergeSortedLists(a, b.next)
145✔
180
        }
145✔
181
        return result
299✔
182
}
183

184
func (enc *encoder) less(i fastJsonNode, j fastJsonNode) bool {
299✔
185
        attri := enc.getAttr(i)
299✔
186
        attrj := enc.getAttr(j)
299✔
187
        return strings.Compare(enc.attrForID(attri), enc.attrForID(attrj)) <= 0
299✔
188
}
299✔
189

190
func frontBackSplit(source fastJsonNode,
191
        frontRef *fastJsonNode, backRef *fastJsonNode) {
201✔
192
        slow := source
201✔
193
        fast := source.next
201✔
194

201✔
195
        for fast != nil {
435✔
196
                fast = fast.next
234✔
197
                if fast != nil {
314✔
198
                        slow = slow.next
80✔
199
                        fast = fast.next
80✔
200
                }
80✔
201
        }
202

203
        *frontRef = source
201✔
204
        *backRef = slow.next
201✔
205
        slow.next = nil
201✔
206
}
207

208
func (enc *encoder) idForAttr(attr string) uint16 {
590,222✔
209
        if attr == "uid" && enc.uidAttr > 0 {
597,105✔
210
                return enc.uidAttr
6,883✔
211
        }
6,883✔
212
        if id, ok := enc.attrMap[attr]; ok {
1,006,967✔
213
                return id
423,628✔
214
        }
423,628✔
215

216
        enc.idSlice = append(enc.idSlice, attr)
159,711✔
217
        enc.attrMap[attr] = uint16(len(enc.idSlice) - 1) // TODO(Ashish): check for overflow.
159,711✔
218
        return uint16(len(enc.idSlice) - 1)
159,711✔
219
}
220

221
func (enc *encoder) attrForID(id uint16) string {
99,822✔
222
        // For now we are not returning error from here.
99,822✔
223
        if id == 0 || id >= uint16(len(enc.idSlice)) {
99,822✔
224
                return ""
×
225
        }
×
226

227
        return enc.idSlice[id]
99,822✔
228
}
229

230
// makeScalarNode returns a fastJsonNode with all of its meta data, scalarVal populated.
231
func (enc *encoder) makeScalarNode(attr uint16, val []byte, list bool) (fastJsonNode, error) {
349,734✔
232
        fj := enc.newNode(attr)
349,734✔
233
        if err := enc.setScalarVal(fj, val); err != nil {
349,734✔
234
                return nil, err
×
235
        }
×
236
        enc.setList(fj, list)
349,734✔
237

349,734✔
238
        return fj, nil
349,734✔
239
}
240

241
func (enc *encoder) makeUidNode(attr uint16, uid uint64) (*node, error) {
11,146✔
242
        fj := enc.newNode(attr)
11,146✔
243
        fj.meta |= uidNodeBit
11,146✔
244

11,146✔
245
        var tmp [8]byte
11,146✔
246
        binary.BigEndian.PutUint64(tmp[:], uid)
11,146✔
247

11,146✔
248
        if err := enc.setScalarVal(fj, tmp[:]); err != nil {
11,146✔
249
                return nil, err
×
250
        }
×
251
        return fj, nil
11,146✔
252
}
253

254
// makeCustomNode returns a fastJsonNode that stores the given val for a @custom GraphQL field.
255
func (enc *encoder) makeCustomNode(attr uint16, val []byte) (fastJsonNode, error) {
285✔
256
        fj := enc.newNode(attr)
285✔
257
        if err := enc.setScalarVal(fj, val); err != nil {
285✔
258
                return nil, err
×
259
        }
×
260
        enc.setCustom(fj)
285✔
261

285✔
262
        return fj, nil
285✔
263
}
264

265
const (
266
        // Value with most significant bit set to 1.
267
        listBit = 1 << 63
268
        // Value with second most significant bit set to 1.
269
        facetsBit = 1 << 62
270
        // Value with third most significant bit set to 1.
271
        uidNodeBit = 1 << 61
272
        // Node has been visited for fixing the children order.
273
        visitedBit = 1 << 60
274
        // customBit is a value with fifth most significant bit set to 1. If a node has customBit set
275
        // in its meta, it means that node stores the value for a @custom GraphQL field.
276
        customBit = 1 << 59
277

278
        // Value with all bits set to 1 for bytes 7 and 6.
279
        setBytes76 = uint64(0x00FFFF0000000000)
280
        // Compliment value of setBytes76.
281
        unsetBytes76 = ^setBytes76
282
        // Value with all bits set to 1 for bytes 4 to 1.
283
        setBytes4321 = 0x00000000FFFFFFFF
284
)
285

286
// fastJsonNode represents node of a tree, which is formed to convert a subgraph into json response
287
// for a query. A fastJsonNode has following meta data:
288
//  1. Attr => predicate associated with this node.
289
//  2. ScalarVal => Any value associated with node, if it is a leaf node.
290
//  3. List => Stores boolean value, true if this node is part of list.
291
//  4. FacetsParent => Stores boolean value, true if this node is a facetsParent. facetsParent is
292
//     node which is parent for facets values for a scalar list predicate. Eg: node "city|country"
293
//     will have FacetsParent value as true.
294
//     {
295
//     "city": ["Bengaluru", "San Francisco"],
296
//     "city|country": {
297
//     "0": "india",
298
//     "1": "US"
299
//     }
300
//     }
301
//  5. Children(Attrs) => List of all children.
302
//  6. Visited => Stores boolen values, true if node has been visited for fixing children's order.
303
//
304
// All of the data for fastJsonNode tree is stored in encoder to optimise memory usage. fastJsonNode
305
// struct is pointer to node object. node object stores below information.
306
// 1. meta information.
307
// 2. Pointer to its first child.
308
// 3. Pointer to its sibling.
309
type fastJsonNode *node
310

311
// newNode returns a fastJsonNode with its attr set to attr,
312
// and all other meta set to their default value.
313
func (enc *encoder) newNode(attr uint16) fastJsonNode {
474,787✔
314
        b := enc.alloc.AllocateAligned(nodeSize)
474,787✔
315
        n := (*node)(unsafe.Pointer(&b[0]))
474,787✔
316
        enc.setAttr(n, attr)
474,787✔
317
        return n
474,787✔
318
}
474,787✔
319

320
func (enc *encoder) setAttr(fj fastJsonNode, attr uint16) {
528,372✔
321
        // There can be some cases where we change name of attr for fastJsoNode and
528,372✔
322
        // hence first clear the existing attr, then store new one.
528,372✔
323
        fj.meta &= unsetBytes76
528,372✔
324
        fj.meta |= (uint64(attr) << 40)
528,372✔
325
}
528,372✔
326

327
func (enc *encoder) setScalarVal(fj fastJsonNode, sv []byte) error {
361,167✔
328
        offset, err := enc.arena.put(sv)
361,167✔
329
        if err != nil {
361,167✔
330
                return err
×
331
        }
×
332
        fj.meta |= uint64(offset)
361,167✔
333

361,167✔
334
        // Also increase curSize.
361,167✔
335
        enc.curSize += uint64(len(sv))
361,167✔
336
        if size := uint64(enc.alloc.Size()) + enc.curSize; size > maxEncodedSize {
361,167✔
337
                return fmt.Errorf("estimated response size: %d is bigger than threshold: %d",
×
338
                        size, maxEncodedSize)
×
339
        }
×
340
        return nil
361,167✔
341
}
342

343
func (enc *encoder) setList(fj fastJsonNode, list bool) {
398,296✔
344
        if list {
448,350✔
345
                fj.meta |= listBit
50,054✔
346
        } else {
398,296✔
347
                fj.meta &^= listBit
348,242✔
348
        }
348,242✔
349
}
350

351
func (enc *encoder) setVisited(fj fastJsonNode, visited bool) {
145,638✔
352
        if visited {
291,276✔
353
                fj.meta |= visitedBit
145,638✔
354
        } else {
145,638✔
355
                fj.meta &^= visitedBit
×
356
        }
×
357
}
358

359
func (enc *encoder) setFacetsParent(fj fastJsonNode) {
29✔
360
        fj.meta |= facetsBit
29✔
361
}
29✔
362

363
func (enc *encoder) setCustom(fj fastJsonNode) {
285✔
364
        fj.meta |= customBit
285✔
365
}
285✔
366

367
//nolint:unused // appendAttrs is used in outputnode_test.go as a helper function
368
func (enc *encoder) appendAttrs(fj, child fastJsonNode) {
1✔
369
        enc.addChildren(fj, child)
1✔
370
}
1✔
371

372
// addChildren appends attrs to existing fj's attrs.
373
func (enc *encoder) addChildren(fj, head fastJsonNode) {
410,431✔
374
        if fj.child == nil {
485,030✔
375
                fj.child = head
74,599✔
376
                return
74,599✔
377
        }
74,599✔
378

379
        tail := head
335,832✔
380
        for tail.next != nil {
335,841✔
381
                tail = tail.next
9✔
382
        }
9✔
383

384
        // We're inserting the node in between. This would need to be fixed later via fixOrder.
385
        // Single child additions:
386
        // Child 1
387
        // Child 2 -> 1
388
        // Child 3 -> 2 -> 1
389
        // Child 4 -> 3 -> 2 -> 1
390
        // Child 5 -> 4 -> 3 -> 2 -> 1
391
        //
392
        // If child has siblings, then it could look like this.
393
        // addChildren(13 -> 12 -> 11)
394
        // Child 5 -> 4 -> 3 -> 2 -> 1
395
        //
396
        // What we want:
397
        // 13 -> 12 -> 11 -> 5 -> 4 -> 3 -> 2 -> 1
398
        fj.child, tail.next = head, fj.child
335,832✔
399
}
400

401
// fixOrder would recursively fix the ordering issue caused by addChildren, across the entire
402
// tree.
403
// fixOrder would fix the order from
404
// 5 -> 4 -> 3 -> 2 -> 1 to
405
// 1 -> 2 -> 3 -> 4 -> 5
406
func (enc *encoder) fixOrder(fj fastJsonNode) {
145,639✔
407
        // If you call this again on the same fastJsonNode, then this would become wrong.  Due to
145,639✔
408
        // children being copied over, the same node can be referenced by multiple nodes. Thus, the node
145,639✔
409
        // would be visited again, it would be fixed multiple times, causing ordering issue.
145,639✔
410
        // To avoid this, we keep track of the node by marking it.
145,639✔
411
        if (fj.meta & visitedBit) > 0 {
145,841✔
412
                return
202✔
413
        }
202✔
414
        enc.setVisited(fj, true)
145,437✔
415

145,437✔
416
        tail := fj.child // This is node 5 in the chain mentioned above.
145,437✔
417
        // Edge cases: Child is nil, or only child.
145,437✔
418
        if tail == nil {
219,498✔
419
                return
74,061✔
420
        }
74,061✔
421

422
        if tail.next == nil {
122,141✔
423
                enc.fixOrder(tail)
50,765✔
424
                return
50,765✔
425
        }
50,765✔
426

427
        left, right := tail, tail.next // Left is 5, right is 4.
20,611✔
428
        left.next = nil                // Make left the last child.
20,611✔
429
        for right != nil {
55,441✔
430
                next := right.next        // right of ptr2 (points to 3)
34,830✔
431
                right.next = left         // ptr2 now points left to ptr1 (4 -> 5)
34,830✔
432
                left, right = right, next // Advance both pointers (left = 4, right = 3 and so on)
34,830✔
433
        }
34,830✔
434
        // left is now pointing to 1.
435
        fj.child = left // Child is now pointed to 1.
20,611✔
436

20,611✔
437
        // Now recurse to fix up all children.
20,611✔
438
        child := fj.child
20,611✔
439
        for child != nil {
76,052✔
440
                enc.fixOrder(child)
55,441✔
441
                child = child.next
55,441✔
442
        }
55,441✔
443
}
444

445
func (enc *encoder) getAttr(fj fastJsonNode) uint16 {
170,614✔
446
        return uint16((fj.meta & setBytes76) >> 40)
170,614✔
447
}
170,614✔
448

449
func (enc *encoder) getScalarVal(fj fastJsonNode) ([]byte, error) {
61,650✔
450
        offset := uint32(fj.meta & setBytes4321)
61,650✔
451
        data, err := enc.arena.get(offset)
61,650✔
452
        if err != nil {
61,650✔
453
                return nil, err
×
454
        }
×
455
        if (fj.meta & uidNodeBit) > 0 {
70,911✔
456
                uid := binary.BigEndian.Uint64(data)
9,261✔
457
                return x.ToHex(uid, false), nil
9,261✔
458
        }
9,261✔
459
        return data, nil
52,389✔
460
}
461

462
func (enc *encoder) getList(fj fastJsonNode) bool {
176,856✔
463
        return (fj.meta & listBit) > 0
176,856✔
464
}
176,856✔
465

466
func (enc *encoder) getFacetsParent(fj fastJsonNode) bool {
6,250✔
467
        return (fj.meta & facetsBit) > 0
6,250✔
468
}
6,250✔
469

470
func (enc *encoder) getCustom(fj fastJsonNode) bool {
6,650✔
471
        return (fj.meta & customBit) > 0
6,650✔
472
}
6,650✔
473

474
func (enc *encoder) children(fj fastJsonNode) fastJsonNode {
186,386✔
475
        // Return nil if no attrs are found.
186,386✔
476
        return fj.child
186,386✔
477
}
186,386✔
478

479
func (enc *encoder) AddValue(fj fastJsonNode, attr uint16, v types.Val) error {
303,328✔
480
        return enc.AddListValue(fj, attr, v, false)
303,328✔
481
}
303,328✔
482

483
func (enc *encoder) AddListValue(fj fastJsonNode, attr uint16, v types.Val, list bool) error {
349,724✔
484
        if v.Tid == types.VFloatID {
349,728✔
485
                for _, f := range v.Value.([]float32) {
20✔
486
                        bs := []byte(strconv.FormatFloat(float64(f), 'E', -1, 32))
16✔
487
                        sn, err := enc.makeScalarNode(attr, bs, true)
16✔
488
                        if err != nil {
16✔
UNCOV
489
                                return err
×
490
                        }
×
491

492
                        enc.addChildren(fj, sn)
16✔
493
                }
494
                return nil
4✔
495
        }
496
        bs, err := valToBytes(v)
349,720✔
497
        if err != nil {
349,722✔
498
                return nil // Ignore this.
2✔
499
        }
2✔
500
        sn, err := enc.makeScalarNode(attr, bs, list)
349,718✔
501
        if err != nil {
349,718✔
UNCOV
502
                return err
×
UNCOV
503
        }
×
504

505
        enc.addChildren(fj, sn)
349,718✔
506
        return nil
349,718✔
507
}
508

509
func (enc *encoder) AddMapChild(fj, val fastJsonNode) {
766✔
510
        var childNode fastJsonNode
766✔
511
        child := enc.children(fj)
766✔
512
        for child != nil {
2,383✔
513
                if enc.getAttr(child) == enc.getAttr(val) {
1,628✔
514
                        childNode = child
11✔
515
                        break
11✔
516
                }
517
                child = child.next
1,606✔
518
        }
519

520
        if childNode == nil {
1,521✔
521
                enc.addChildren(fj, val)
755✔
522
        } else {
766✔
523
                enc.addChildren(childNode, enc.children(val))
11✔
524
        }
11✔
525
}
526

527
func (enc *encoder) AddListChild(fj, child fastJsonNode) {
48,560✔
528
        enc.setList(child, true)
48,560✔
529
        enc.addChildren(fj, child)
48,560✔
530
}
48,560✔
531

532
func (enc *encoder) SetUID(fj fastJsonNode, uid uint64, attr uint16) error {
11,150✔
533
        // if we're in debug mode, uid may be added second time, skip this
11,150✔
534
        if attr == enc.uidAttr {
18,340✔
535
                fjAttrs := enc.children(fj)
7,190✔
536
                for fjAttrs != nil {
7,486✔
537
                        if enc.getAttr(fjAttrs) == attr {
300✔
538
                                return nil
4✔
539
                        }
4✔
540
                        fjAttrs = fjAttrs.next
292✔
541
                }
542
        }
543

544
        un, err := enc.makeUidNode(attr, uid)
11,146✔
545
        if err != nil {
11,146✔
UNCOV
546
                return err
×
UNCOV
547
        }
×
548
        enc.addChildren(fj, un)
11,146✔
549
        return nil
11,146✔
550
}
551

552
func (enc *encoder) IsEmpty(fj fastJsonNode) bool {
65,318✔
553
        return fj.child == nil
65,318✔
554
}
65,318✔
555

556
var (
557
        boolTrue  = []byte("true")
558
        boolFalse = []byte("false")
559

560
        // Below variables are used in stringJsonMarshal function.
561
        bufferPool = sync.Pool{
562
                New: func() interface{} {
4,116✔
563
                        return new(bytes.Buffer)
4,116✔
564
                },
4,116✔
565
        }
566

567
        hex        = "0123456789abcdef"
568
        escapeHTML = true
569
)
570

571
// stringJsonMarshal is replacement for json.Marshal() function only for string type.
572
// This function is encodeState.string(string, escapeHTML) in "encoding/json/encode.go".
573
// It should be in sync with encodeState.string function.
574
func stringJsonMarshal(s string) []byte {
340,541✔
575
        e := bufferPool.Get().(*bytes.Buffer)
340,541✔
576
        e.Reset()
340,541✔
577

340,541✔
578
        e.WriteByte('"')
340,541✔
579
        start := 0
340,541✔
580
        for i := 0; i < len(s); {
633,067✔
581
                if b := s[i]; b < utf8.RuneSelf {
584,853✔
582
                        if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
584,482✔
583
                                i++
292,155✔
584
                                continue
292,155✔
585
                        }
586
                        if start < i {
233✔
587
                                e.WriteString(s[start:i])
61✔
588
                        }
61✔
589
                        e.WriteByte('\\')
172✔
590
                        switch b {
172✔
591
                        case '\\', '"':
14✔
592
                                e.WriteByte(b)
14✔
593
                        case '\n':
51✔
594
                                e.WriteByte('n')
51✔
UNCOV
595
                        case '\r':
×
UNCOV
596
                                e.WriteByte('r')
×
597
                        case '\t':
94✔
598
                                e.WriteByte('t')
94✔
599
                        default:
13✔
600
                                // This encodes bytes < 0x20 except for \t, \n and \r.
13✔
601
                                // If escapeHTML is set, it also escapes <, >, and &
13✔
602
                                // because they can lead to security holes when
13✔
603
                                // user-controlled strings are rendered into JSON
13✔
604
                                // and served to some browsers.
13✔
605
                                e.WriteString(`u00`)
13✔
606
                                e.WriteByte(hex[b>>4])
13✔
607
                                e.WriteByte(hex[b&0xF])
13✔
608
                        }
609
                        i++
172✔
610
                        start = i
172✔
611
                        continue
172✔
612
                }
613
                c, size := utf8.DecodeRuneInString(s[i:])
199✔
614
                if c == utf8.RuneError && size == 1 {
199✔
UNCOV
615
                        if start < i {
×
UNCOV
616
                                e.WriteString(s[start:i])
×
UNCOV
617
                        }
×
UNCOV
618
                        e.WriteString(`\ufffd`)
×
619
                        i += size
×
620
                        start = i
×
621
                        continue
×
622
                }
623
                // U+2028 is LINE SEPARATOR.
624
                // U+2029 is PARAGRAPH SEPARATOR.
625
                // They are both technically valid characters in JSON strings,
626
                // but don't work in JSONP, which has to be evaluated as JavaScript,
627
                // and can lead to security holes there. It is valid JSON to
628
                // escape them, so we do so unconditionally.
629
                // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
630
                if c == '\u2028' || c == '\u2029' {
199✔
UNCOV
631
                        if start < i {
×
UNCOV
632
                                e.WriteString(s[start:i])
×
UNCOV
633
                        }
×
UNCOV
634
                        e.WriteString(`\u202`)
×
UNCOV
635
                        e.WriteByte(hex[c&0xF])
×
UNCOV
636
                        i += size
×
UNCOV
637
                        start = i
×
UNCOV
638
                        continue
×
639
                }
640
                i += size
199✔
641
        }
642
        if start < len(s) {
379,997✔
643
                e.WriteString(s[start:])
39,456✔
644
        }
39,456✔
645
        e.WriteByte('"')
340,541✔
646
        buf := append([]byte(nil), e.Bytes()...)
340,541✔
647
        bufferPool.Put(e)
340,541✔
648
        return buf
340,541✔
649
}
650

651
func valToBytes(v types.Val) ([]byte, error) {
349,757✔
652
        switch v.Tid {
349,757✔
653
        case types.StringID, types.DefaultID:
340,546✔
654
                switch str := v.Value.(type) {
340,546✔
655
                case string:
340,533✔
656
                        return stringJsonMarshal(str), nil
340,533✔
657
                default:
13✔
658
                        return json.Marshal(str)
13✔
659
                }
660
        case types.BinaryID:
×
UNCOV
661
                return []byte(fmt.Sprintf("%q", v.Value)), nil
×
662
        case types.IntID:
7,035✔
663
                // In types.Convert(), we always convert to int64 for IntID type. fmt.Sprintf is slow
7,035✔
664
                // and hence we are using strconv.FormatInt() here. Since int64 and int are most common int
7,035✔
665
                // types we are using FormatInt for those.
7,035✔
666
                switch num := v.Value.(type) {
7,035✔
667
                case int64:
7,034✔
668
                        return []byte(strconv.FormatInt(num, 10)), nil
7,034✔
669
                case int:
1✔
670
                        return []byte(strconv.FormatInt(int64(num), 10)), nil
1✔
UNCOV
671
                default:
×
UNCOV
672
                        return []byte(fmt.Sprintf("%d", v.Value)), nil
×
673
                }
674
        case types.FloatID:
487✔
675
                f, fOk := v.Value.(float64)
487✔
676

487✔
677
                // +Inf, -Inf and NaN are not representable in JSON.
487✔
678
                // Please see https://golang.org/src/encoding/json/encode.go?s=6458:6501#L573
487✔
679
                if !fOk || math.IsInf(f, 0) || math.IsNaN(f) {
489✔
680
                        return nil, errors.New("Unsupported floating point number in float field")
2✔
681
                }
2✔
682

683
                return []byte(fmt.Sprintf("%f", f)), nil
485✔
684
        case types.BoolID:
992✔
685
                if v.Value.(bool) {
1,876✔
686
                        return boolTrue, nil
884✔
687
                }
884✔
688
                return boolFalse, nil
108✔
689
        case types.DateTimeID:
655✔
690
                t := v.Value.(time.Time)
655✔
691
                return marshalTimeJson(t)
655✔
692
        case types.GeoID:
29✔
693
                return geojson.Marshal(v.Value.(geom.T))
29✔
694
        case types.UidID:
13✔
695
                return []byte(fmt.Sprintf("\"%#x\"", v.Value)), nil
13✔
UNCOV
696
        case types.PasswordID:
×
UNCOV
697
                return []byte(fmt.Sprintf("%q", v.Value.(string))), nil
×
UNCOV
698
        case types.VFloatID:
×
UNCOV
699
                return json.Marshal(v.Value.([]float32))
×
UNCOV
700
        default:
×
UNCOV
701
                return nil, errors.New("Unsupported types.Val.Tid")
×
702
        }
703
}
704

705
// marshalTimeJson does what time.MarshalJson does along with supporting RFC3339 non compliant
706
// time zones in a timestamp. While go 1.20 changes the behaviour of time.MarshalJSON, we do
707
// not want to throw error suddenly because we can't marshal the stored data correctly any more.
708
func marshalTimeJson(t time.Time) ([]byte, error) {
670✔
709
        _, offset := t.Zone()
670✔
710
        // normal case
670✔
711
        if types.GoodTimeZone(offset) {
1,330✔
712
                return t.MarshalJSON()
660✔
713
        }
660✔
714

715
        // If zone >23 or <-23, we need to handle this case ourselves.
716
        // This is because, in go1.20, MarshalJSON fails for invalid zones.
717
        // We, for now, call MarshalJSON for timestamp without the zone (or making it UTC zone).
718
        b, err := t.Add(time.Duration(offset) * time.Second).UTC().MarshalJSON()
10✔
719
        if err != nil {
10✔
UNCOV
720
                return nil, err
×
UNCOV
721
        }
×
722

723
        // we will get a byte slice that has Z appended at the end along with a quote (")
724
        // e.g.: []byte("2018-05-28T14:41:57Z"). We replace Z with -/+.
725
        zone := offset / 60
10✔
726
        if zone < 0 {
13✔
727
                b[len(b)-2] = '-'
3✔
728
                zone = -zone
3✔
729
        } else {
10✔
730
                b[len(b)-2] = '+'
7✔
731
        }
7✔
732
        return append(b[:len(b)-1], []byte(fmt.Sprintf("%02d:%02d\"", zone/60, zone%60))...), nil
10✔
733
}
734

735
func (enc *encoder) writeKey(fj fastJsonNode) error {
85,305✔
736
        if _, err := enc.buf.WriteRune('"'); err != nil {
85,305✔
UNCOV
737
                return err
×
UNCOV
738
        }
×
739
        attrID := enc.getAttr(fj)
85,305✔
740
        if _, err := enc.buf.WriteString(enc.attrForID(attrID)); err != nil {
85,305✔
UNCOV
741
                return err
×
UNCOV
742
        }
×
743
        if _, err := enc.buf.WriteRune('"'); err != nil {
85,305✔
UNCOV
744
                return err
×
UNCOV
745
        }
×
746
        if _, err := enc.buf.WriteRune(':'); err != nil {
85,305✔
747
                return err
×
UNCOV
748
        }
×
749
        return nil
85,305✔
750
}
751

752
func (enc *encoder) attachFacets(fj fastJsonNode, fieldName string, isList bool,
753
        fList []*api.Facet, facetIdx int) error {
504✔
754

504✔
755
        idxFieldID := enc.idForAttr(strconv.Itoa(facetIdx))
504✔
756
        for _, f := range fList {
965✔
757
                fName := facetName(fieldName, f)
461✔
758
                fVal, err := facets.ValFor(f)
461✔
759
                if err != nil {
461✔
UNCOV
760
                        return err
×
UNCOV
761
                }
×
762

763
                if !isList {
893✔
764
                        if err := enc.AddValue(fj, enc.idForAttr(fName), fVal); err != nil {
432✔
UNCOV
765
                                return err
×
UNCOV
766
                        }
×
767
                } else {
29✔
768
                        facetNode := enc.newNode(enc.idForAttr(fName))
29✔
769
                        err := enc.AddValue(facetNode, idxFieldID, fVal)
29✔
770
                        if err != nil {
29✔
UNCOV
771
                                return err
×
UNCOV
772
                        }
×
773
                        // Mark this node as facetsParent.
774
                        enc.setFacetsParent(facetNode)
29✔
775
                        enc.AddMapChild(fj, facetNode)
29✔
776
                }
777
        }
778

779
        return nil
504✔
780
}
781

782
func (enc *encoder) encode(fj fastJsonNode) error {
118,165✔
783
        child := enc.children(fj)
118,165✔
784
        // This is a scalar value.
118,165✔
785
        if child == nil {
170,990✔
786
                val, err := enc.getScalarVal(fj)
52,825✔
787
                if err != nil {
52,825✔
UNCOV
788
                        return err
×
UNCOV
789
                }
×
790
                _, err = enc.buf.Write(val)
52,825✔
791
                return err
52,825✔
792
        }
793

794
        // This is an internal node.
795
        if _, err := enc.buf.WriteRune('{'); err != nil {
65,340✔
UNCOV
796
                return err
×
UNCOV
797
        }
×
798
        cnt := 0
65,340✔
799
        var cur, next fastJsonNode
65,340✔
800
        for child != nil {
157,209✔
801
                cnt++
91,869✔
802
                validNext := false
91,869✔
803
                cur = child
91,869✔
804
                if cur.next != nil {
118,398✔
805
                        next = cur.next
26,529✔
806
                        validNext = true
26,529✔
807
                }
26,529✔
808

809
                if validNext && enc.getAttr(cur) == enc.getAttr(next) {
98,433✔
810
                        if cnt == 1 {
9,453✔
811
                                if err := enc.writeKey(cur); err != nil {
2,889✔
UNCOV
812
                                        return err
×
UNCOV
813
                                }
×
814
                                if _, err := enc.buf.WriteRune('['); err != nil {
2,889✔
815
                                        return err
×
UNCOV
816
                                }
×
817
                        }
818
                        if err := enc.encode(cur); err != nil {
6,564✔
819
                                return err
×
820
                        }
×
821
                } else {
85,305✔
822
                        if cnt == 1 {
167,721✔
823
                                if err := enc.writeKey(cur); err != nil {
82,416✔
824
                                        return err
×
UNCOV
825
                                }
×
826
                                if enc.getList(cur) {
115,682✔
827
                                        if _, err := enc.buf.WriteRune('['); err != nil {
33,266✔
UNCOV
828
                                                return err
×
UNCOV
829
                                        }
×
830
                                }
831
                        }
832
                        if err := enc.encode(cur); err != nil {
85,305✔
UNCOV
833
                                return err
×
UNCOV
834
                        }
×
835
                        if cnt > 1 || enc.getList(cur) {
121,460✔
836
                                if _, err := enc.buf.WriteRune(']'); err != nil {
36,155✔
UNCOV
837
                                        return err
×
838
                                }
×
839
                        }
840
                        cnt = 0 // Reset the count.
85,305✔
841
                }
842
                // We need to print comma except for the last attribute.
843
                if child.next != nil {
118,398✔
844
                        if _, err := enc.buf.WriteRune(','); err != nil {
26,529✔
UNCOV
845
                                return err
×
UNCOV
846
                        }
×
847
                }
848

849
                child = child.next
91,869✔
850
        }
851
        if _, err := enc.buf.WriteRune('}'); err != nil {
65,340✔
UNCOV
852
                return err
×
UNCOV
853
        }
×
854

855
        return nil
65,340✔
856
}
857

858
func (enc *encoder) copyFastJsonList(fj fastJsonNode) (fastJsonNode, int) {
234✔
859
        if fj == nil {
236✔
860
                return fj, 0
2✔
861
        }
2✔
862

863
        var head, tail fastJsonNode
232✔
864
        nodeCount := 0
232✔
865

232✔
866
        for fj != nil {
1,671✔
867
                nodeCount++
1,439✔
868
                nn := enc.copySingleNode(fj)
1,439✔
869
                if tail == nil {
1,671✔
870
                        head, tail = nn, nn
232✔
871
                        fj = fj.next
232✔
872
                        continue
232✔
873
                }
874
                tail.next = nn
1,207✔
875
                fj, tail = fj.next, tail.next
1,207✔
876
        }
877

878
        return head, nodeCount
232✔
879
}
880

881
func (enc *encoder) copySingleNode(fj fastJsonNode) fastJsonNode {
2,490✔
882
        if fj == nil {
2,490✔
UNCOV
883
                return nil
×
UNCOV
884
        }
×
885

886
        nn := enc.newNode(enc.getAttr(fj))
2,490✔
887
        nn.meta = fj.meta
2,490✔
888
        nn.child = fj.child
2,490✔
889
        nn.next = nil
2,490✔
890
        return nn
2,490✔
891
}
892

893
func (enc *encoder) merge(parent, child []fastJsonNode) ([]fastJsonNode, error) {
48✔
894
        if len(parent) == 0 {
48✔
UNCOV
895
                return child, nil
×
UNCOV
896
        }
×
897

898
        // Here we merge two slices of maps.
899
        mergedList := make([]fastJsonNode, 0)
48✔
900
        cnt := 0
48✔
901
        for _, pa := range parent {
105✔
902
                for _, ca := range child {
174✔
903
                        paCopy, paNodeCount := enc.copyFastJsonList(pa)
117✔
904
                        caCopy, caNodeCount := enc.copyFastJsonList(ca)
117✔
905

117✔
906
                        cnt += paNodeCount + caNodeCount
117✔
907
                        if cnt > x.Config.LimitNormalizeNode {
118✔
908
                                return nil, errors.Errorf(
1✔
909
                                        "Couldn't evaluate @normalize directive - too many results")
1✔
910
                        }
1✔
911

912
                        if paCopy == nil {
118✔
913
                                paCopy = caCopy
2✔
914
                        } else {
116✔
915
                                temp := paCopy
114✔
916
                                for temp.next != nil {
172✔
917
                                        temp = temp.next
58✔
918
                                }
58✔
919
                                temp.next = caCopy
114✔
920
                        }
921
                        mergedList = append(mergedList, paCopy)
116✔
922
                }
923
        }
924
        return mergedList, nil
47✔
925
}
926

927
// normalize returns all attributes of fj and its children (if any).
928
func (enc *encoder) normalize(fj fastJsonNode) ([]fastJsonNode, error) {
142✔
929
        cnt := 0
142✔
930
        chead := enc.children(fj)
142✔
931
        for chead != nil {
4,417✔
932
                // Here we are counting all non-scalar children of fj. If there are any such
4,275✔
933
                // children, we will flatten them, otherwise we will return all children.
4,275✔
934
                // We should only consider those children(of fj) for flattening which have
4,275✔
935
                // children and are not facetsParent.
4,275✔
936
                if enc.children(chead) != nil && !enc.getFacetsParent(chead) {
7,373✔
937
                        cnt++
3,098✔
938
                }
3,098✔
939
                chead = chead.next
4,275✔
940
        }
941

942
        if cnt == 0 {
239✔
943
                // Recursion base case
97✔
944
                // There are no children, we can just return slice with fj.child.
97✔
945
                return []fastJsonNode{enc.children(fj)}, nil
97✔
946
        }
97✔
947

948
        parentSlice := make([]fastJsonNode, 0, 5)
45✔
949

45✔
950
        // First separate children of fj which are scalar.
45✔
951
        var shead, curScalar fastJsonNode
45✔
952
        chead = enc.children(fj)
45✔
953
        for chead != nil {
4,194✔
954
                if enc.children(chead) != nil && !enc.getFacetsParent(chead) {
7,247✔
955
                        chead = chead.next
3,098✔
956
                        continue
3,098✔
957
                }
958

959
                // Here, add all nodes which have either no children or they are facetsParent.
960
                copyNode := enc.copySingleNode(chead)
1,051✔
961
                if curScalar == nil {
1,094✔
962
                        shead, curScalar = copyNode, copyNode
43✔
963
                } else {
1,051✔
964
                        curScalar.next = copyNode
1,008✔
965
                        curScalar = copyNode
1,008✔
966
                }
1,008✔
967

968
                chead = chead.next
1,051✔
969
        }
970

971
        parentSlice = append(parentSlice, shead)
45✔
972
        chead = enc.children(fj)
45✔
973
        for chead != nil {
144✔
974
                childNode := chead
99✔
975
                // Here, exclude all nodes which have either no children or they are facetsParent.
99✔
976
                if enc.children(childNode) == nil || enc.getFacetsParent(childNode) {
150✔
977
                        chead = chead.next
51✔
978
                        continue
51✔
979
                }
980

981
                childSlice := make([]fastJsonNode, 0, 5)
48✔
982
                for chead != nil && enc.getAttr(childNode) == enc.getAttr(chead) {
147✔
983
                        childSlice = append(childSlice, enc.children(chead))
99✔
984
                        chead = chead.next
99✔
985
                }
99✔
986

987
                var err error
48✔
988
                parentSlice, err = enc.merge(parentSlice, childSlice)
48✔
989
                if err != nil {
49✔
990
                        return nil, err
1✔
991
                }
1✔
992
        }
993

994
        for i, slice := range parentSlice {
148✔
995
                if x.Config.NormalizeCompatibilityMode == "" {
208✔
996
                        // sort the fastJson list. This will ensure that nodes
104✔
997
                        // with same attribute name comes together in response
104✔
998
                        enc.MergeSort(&parentSlice[i])
104✔
999
                }
104✔
1000

1001
                // From every list we need to remove node with attribute "uid".
1002
                var prev, cur fastJsonNode
104✔
1003
                cur = slice
104✔
1004
                for cur != nil {
274✔
1005
                        if enc.getAttr(cur) == enc.uidAttr {
170✔
UNCOV
1006
                                if prev == nil {
×
UNCOV
1007
                                        cur = cur.next
×
UNCOV
1008
                                        continue
×
UNCOV
1009
                                } else {
×
UNCOV
1010
                                        prev.next = cur.next
×
UNCOV
1011
                                }
×
1012
                        }
1013
                        prev = cur
170✔
1014
                        cur = cur.next
170✔
1015
                }
1016
        }
1017

1018
        return parentSlice, nil
44✔
1019
}
1020

1021
func (sg *SubGraph) addGroupby(enc *encoder, fj fastJsonNode,
1022
        res *groupResults, fname string) error {
36✔
1023

36✔
1024
        // Don't add empty groupby
36✔
1025
        if len(res.group) == 0 {
49✔
1026
                return nil
13✔
1027
        }
13✔
1028
        g := enc.newNode(enc.idForAttr(fname))
23✔
1029
        for _, grp := range res.group {
80✔
1030
                uc := enc.newNode(enc.idForAttr("@groupby"))
57✔
1031
                for _, it := range grp.keys {
130✔
1032
                        if err := enc.AddValue(uc, enc.idForAttr(it.attr), it.key); err != nil {
73✔
UNCOV
1033
                                return err
×
UNCOV
1034
                        }
×
1035
                }
1036
                for _, it := range grp.aggregates {
118✔
1037
                        if err := enc.AddValue(uc, enc.idForAttr(it.attr), it.key); err != nil {
61✔
UNCOV
1038
                                return err
×
UNCOV
1039
                        }
×
1040
                }
1041
                enc.AddListChild(g, uc)
57✔
1042
        }
1043
        enc.AddListChild(fj, g)
23✔
1044
        return nil
23✔
1045
}
1046

1047
func (sg *SubGraph) addAggregations(enc *encoder, fj fastJsonNode) error {
133✔
1048
        for _, child := range sg.Children {
253✔
1049
                aggVal, ok := child.Params.UidToVal[0]
120✔
1050
                if !ok {
134✔
1051
                        if len(child.Params.NeedsVar) == 0 {
15✔
1052
                                return errors.Errorf("Only aggregated variables allowed within empty block.")
1✔
1053
                        }
1✔
1054
                        // the aggregation didn't happen, most likely was called with unset vars.
1055
                        // See: query.go:fillVars
1056
                        // In this case we do nothing. The aggregate value in response will be returned as NULL.
1057
                }
1058
                if child.Params.Normalize && child.Params.Alias == "" {
120✔
1059
                        continue
1✔
1060
                }
1061
                fieldName := child.aggWithVarFieldName()
118✔
1062
                n1 := enc.newNode(enc.idForAttr(sg.Params.Alias))
118✔
1063
                if err := enc.AddValue(n1, enc.idForAttr(fieldName), aggVal); err != nil {
118✔
UNCOV
1064
                        return err
×
UNCOV
1065
                }
×
1066
                enc.AddListChild(fj, n1)
118✔
1067
        }
1068
        if enc.IsEmpty(fj) {
217✔
1069
                enc.AddListChild(fj, enc.newNode(enc.idForAttr(sg.Params.Alias)))
85✔
1070
        }
85✔
1071
        return nil
132✔
1072
}
1073

1074
func (sg *SubGraph) handleCountUIDNodes(enc *encoder, n fastJsonNode, count int) (bool, error) {
39,091✔
1075
        addedNewChild := false
39,091✔
1076
        fieldName := sg.fieldName()
39,091✔
1077
        sgFieldID := enc.idForAttr(fieldName)
39,091✔
1078
        for _, child := range sg.Children {
106,036✔
1079
                uidCount := child.Attr == "uid" && child.Params.DoCount && child.IsInternal()
66,945✔
1080
                normWithoutAlias := child.Params.Alias == "" && child.Params.Normalize
66,945✔
1081
                if uidCount && !normWithoutAlias {
67,033✔
1082
                        addedNewChild = true
88✔
1083

88✔
1084
                        c := types.ValueForType(types.IntID)
88✔
1085
                        c.Value = int64(count)
88✔
1086

88✔
1087
                        field := child.Params.Alias
88✔
1088
                        if field == "" {
162✔
1089
                                field = "count"
74✔
1090
                        }
74✔
1091

1092
                        fjChild := enc.newNode(sgFieldID)
88✔
1093
                        if err := enc.AddValue(fjChild, enc.idForAttr(field), c); err != nil {
88✔
UNCOV
1094
                                return false, err
×
UNCOV
1095
                        }
×
1096
                        enc.AddListChild(n, fjChild)
88✔
1097
                }
1098
        }
1099

1100
        return addedNewChild, nil
39,091✔
1101
}
1102

1103
func processNodeUids(fj fastJsonNode, enc *encoder, sg *SubGraph) error {
30,050✔
1104
        if sg.Params.IsEmpty {
30,183✔
1105
                return sg.addAggregations(enc, fj)
133✔
1106
        }
133✔
1107

1108
        enc.curSize += uint64(len(sg.Params.Alias))
29,917✔
1109

29,917✔
1110
        attrID := enc.idForAttr(sg.Params.Alias)
29,917✔
1111
        if sg.uidMatrix == nil {
29,920✔
1112
                enc.AddListChild(fj, enc.newNode(attrID))
3✔
1113
                return nil
3✔
1114
        }
3✔
1115

1116
        hasChild, err := sg.handleCountUIDNodes(enc, fj, len(sg.DestUIDs.Uids))
29,914✔
1117
        if err != nil {
29,914✔
UNCOV
1118
                return err
×
UNCOV
1119
        }
×
1120
        if sg.Params.IsGroupBy {
29,927✔
1121
                if len(sg.GroupbyRes) == 0 {
13✔
UNCOV
1122
                        return errors.Errorf("Expected GroupbyRes to have length > 0.")
×
UNCOV
1123
                }
×
1124
                return sg.addGroupby(enc, fj, sg.GroupbyRes[0], sg.Params.Alias)
13✔
1125
        }
1126

1127
        lenList := len(sg.uidMatrix[0].Uids)
29,901✔
1128
        for i := 0; i < lenList; i++ {
83,554✔
1129
                uid := sg.uidMatrix[0].Uids[i]
53,653✔
1130
                if algo.IndexOf(sg.DestUIDs, uid) < 0 {
53,721✔
1131
                        // This UID was filtered. So Ignore it.
68✔
1132
                        continue
68✔
1133
                }
1134

1135
                n1 := enc.newNode(attrID)
53,585✔
1136
                enc.setAttr(n1, enc.idForAttr(sg.Params.Alias))
53,585✔
1137
                if err := sg.preTraverse(enc, uid, n1); err != nil {
53,586✔
1138
                        if err.Error() == "_INV_" {
1✔
UNCOV
1139
                                continue
×
1140
                        }
1141
                        return err
1✔
1142
                }
1143

1144
                if enc.IsEmpty(n1) {
75,502✔
1145
                        continue
21,918✔
1146
                }
1147

1148
                hasChild = true
31,666✔
1149
                if !sg.Params.Normalize {
63,315✔
1150
                        enc.AddListChild(fj, n1)
31,649✔
1151
                        continue
31,649✔
1152
                }
1153

1154
                // With the new changes we store children in reverse order(check addChildren method). This
1155
                // leads to change of order of field responses for existing Normalize test cases. To
1156
                // minimize the changes of existing tests case we are fixing order of node children before
1157
                // calling normalize() on it. Also once we have fixed order for children, we don't need to
1158
                // fix its order again. Hence mark the newly created node visited immediately.
1159
                enc.fixOrder(n1)
17✔
1160
                // Lets normalize the response now.
17✔
1161
                normalized, err := enc.normalize(n1)
17✔
1162
                if err != nil {
17✔
UNCOV
1163
                        return err
×
UNCOV
1164
                }
×
1165
                for _, c := range normalized {
71✔
1166
                        node := enc.newNode(attrID)
54✔
1167
                        enc.setVisited(node, true)
54✔
1168
                        enc.addChildren(node, c)
54✔
1169
                        enc.AddListChild(fj, node)
54✔
1170
                }
54✔
1171
        }
1172

1173
        if !hasChild {
32,975✔
1174
                // So that we return an empty key if the root didn't have any children.
3,075✔
1175
                enc.AddListChild(fj, enc.newNode(attrID))
3,075✔
1176
        }
3,075✔
1177
        return nil
29,900✔
1178
}
1179

1180
// Extensions represents the extra information appended to query results.
1181
type Extensions struct {
1182
        Latency *api.Latency    `json:"server_latency,omitempty"`
1183
        Txn     *api.TxnContext `json:"txn,omitempty"`
1184
        Metrics *api.Metrics    `json:"metrics,omitempty"`
1185
}
1186

1187
func (sg *SubGraph) toFastJSON(ctx context.Context, l *Latency, field gqlSchema.Field) ([]byte,
1188
        error) {
39,292✔
1189
        encodingStart := time.Now()
39,292✔
1190
        defer func() {
78,584✔
1191
                l.Json = time.Since(encodingStart)
39,292✔
1192
        }()
39,292✔
1193

1194
        enc := newEncoder()
39,292✔
1195
        defer func() {
78,584✔
1196
                // Put encoder's arena back to arena pool.
39,292✔
1197
                arenaPool.Put(enc.arena)
39,292✔
1198
                enc.alloc.Release()
39,292✔
1199
        }()
39,292✔
1200

1201
        var err error
39,292✔
1202
        n := enc.newNode(enc.idForAttr("_root_"))
39,292✔
1203
        for _, sg := range sg.Children {
69,342✔
1204
                err = processNodeUids(n, enc, sg)
30,050✔
1205
                if err != nil {
30,052✔
1206
                        return nil, err
2✔
1207
                }
2✔
1208
        }
1209
        enc.fixOrder(n)
39,290✔
1210

39,290✔
1211
        // According to GraphQL spec response should only contain data, errors and extensions as top
39,290✔
1212
        // level keys. Hence we send server_latency under extensions key.
39,290✔
1213
        // https://facebook.github.io/graphql/#sec-Response-Format
39,290✔
1214

39,290✔
1215
        // if there is a GraphQL field that means we need to encode the response in GraphQL form,
39,290✔
1216
        // otherwise encode it in DQL form.
39,290✔
1217
        if field != nil {
41,283✔
1218
                // if there were any GraphQL errors, we need to propagate them back to GraphQL layer along
1,993✔
1219
                // with the data. So, don't return here if we get an error.
1,993✔
1220
                err = sg.toGraphqlJSON(newGraphQLEncoder(ctx, enc), n, field)
1,993✔
1221
        } else if err = sg.toDqlJSON(enc, n); err != nil {
39,290✔
UNCOV
1222
                return nil, err
×
UNCOV
1223
        }
×
1224

1225
        // Return error if encoded buffer size exceeds than a threshold size.
1226
        if uint64(enc.buf.Len()) > maxEncodedSize {
39,290✔
UNCOV
1227
                return nil, fmt.Errorf("while writing to buffer. Encoded response size: %d"+
×
UNCOV
1228
                        " is bigger than threshold: %d", enc.buf.Len(), maxEncodedSize)
×
UNCOV
1229
        }
×
1230

1231
        return enc.buf.Bytes(), err
39,290✔
1232
}
1233

1234
func (sg *SubGraph) toDqlJSON(enc *encoder, n fastJsonNode) error {
37,297✔
1235
        if enc.children(n) == nil {
48,301✔
1236
                x.Check2(enc.buf.WriteString(`{}`))
11,004✔
1237
                return nil
11,004✔
1238
        }
11,004✔
1239
        return enc.encode(n)
26,293✔
1240
}
1241

1242
func (sg *SubGraph) toGraphqlJSON(genc *graphQLEncoder, n fastJsonNode, f gqlSchema.Field) error {
1,993✔
1243
        // GraphQL queries will always have at least one query whose results are visible to users,
1,993✔
1244
        // implying that the root fastJson node will always have at least one child. So, no need
1,993✔
1245
        // to check for the case where there are no children for the root fastJson node.
1,993✔
1246

1,993✔
1247
        // if this field has any @custom(http: {...}) children,
1,993✔
1248
        // then need to resolve them first before encoding the final GraphQL result.
1,993✔
1249
        genc.processCustomFields(f, n)
1,993✔
1250
        // now encode the GraphQL results.
1,993✔
1251
        if !genc.encode(encodeInput{
1,993✔
1252
                parentField: nil,
1,993✔
1253
                parentPath:  f.PreAllocatePathSlice(),
1,993✔
1254
                fj:          n,
1,993✔
1255
                fjIsRoot:    true,
1,993✔
1256
                childSelSet: []gqlSchema.Field{f},
1,993✔
1257
        }) {
1,993✔
UNCOV
1258
                // if genc.encode() didn't finish successfully here, that means we need to send
×
UNCOV
1259
                // data as null in the GraphQL response like this:
×
UNCOV
1260
                //                 {
×
UNCOV
1261
                //                         "errors": [...],
×
UNCOV
1262
                //                         "data": null
×
UNCOV
1263
                //                 }
×
UNCOV
1264
                // and not just null for a single query in data.
×
UNCOV
1265
                // So, reset the buffer contents here, so that GraphQL layer may know that if it gets
×
UNCOV
1266
                // error of type x.GqlErrorList along with nil JSON response, then it needs to set whole
×
UNCOV
1267
                // data as null.
×
UNCOV
1268
                genc.buf.Reset()
×
UNCOV
1269
        }
×
1270

1271
        if len(genc.errs) > 0 {
2,004✔
1272
                return genc.errs
11✔
1273
        }
11✔
1274
        return nil
1,982✔
1275
}
1276

1277
func (sg *SubGraph) fieldName() string {
118,845✔
1278
        fieldName := sg.Attr
118,845✔
1279
        if sg.Params.Alias != "" {
164,760✔
1280
                fieldName = sg.Params.Alias
45,915✔
1281
        }
45,915✔
1282
        return fieldName
118,845✔
1283
}
1284

1285
func (sg *SubGraph) addCount(enc *encoder, count uint64, dst fastJsonNode) error {
102✔
1286
        if sg.Params.Normalize && sg.Params.Alias == "" {
103✔
1287
                return nil
1✔
1288
        }
1✔
1289
        c := types.ValueForType(types.IntID)
101✔
1290
        c.Value = int64(count)
101✔
1291
        fieldName := sg.Params.Alias
101✔
1292
        if fieldName == "" {
153✔
1293
                fieldName = fmt.Sprintf("count(%s)", sg.Attr)
52✔
1294
        }
52✔
1295
        return enc.AddValue(dst, enc.idForAttr(fieldName), c)
101✔
1296
}
1297

1298
func (sg *SubGraph) aggWithVarFieldName() string {
827✔
1299
        if sg.Params.Alias != "" {
1,344✔
1300
                return sg.Params.Alias
517✔
1301
        }
517✔
1302
        fieldName := fmt.Sprintf("val(%v)", sg.Params.Var)
310✔
1303
        if len(sg.Params.NeedsVar) > 0 {
583✔
1304
                fieldName = fmt.Sprintf("val(%v)", sg.Params.NeedsVar[0].Name)
273✔
1305
                if sg.SrcFunc != nil {
320✔
1306
                        fieldName = fmt.Sprintf("%s(%v)", sg.SrcFunc.Name, fieldName)
47✔
1307
                }
47✔
1308
        }
1309
        return fieldName
310✔
1310
}
1311

1312
func (sg *SubGraph) addInternalNode(enc *encoder, uid uint64, dst fastJsonNode) error {
22,417✔
1313
        sv, ok := sg.Params.UidToVal[uid]
22,417✔
1314
        if !ok || sv.Value == nil {
44,125✔
1315
                return nil
21,708✔
1316
        }
21,708✔
1317
        fieldName := sg.aggWithVarFieldName()
709✔
1318
        return enc.AddValue(dst, enc.idForAttr(fieldName), sv)
709✔
1319
}
1320

1321
func (sg *SubGraph) addCheckPwd(enc *encoder, vals []*pb.TaskValue, dst fastJsonNode) error {
645✔
1322
        c := types.ValueForType(types.BoolID)
645✔
1323
        if len(vals) == 0 {
646✔
1324
                c.Value = false
1✔
1325
        } else {
645✔
1326
                c.Value = task.ToBool(vals[0])
644✔
1327
        }
644✔
1328

1329
        fieldName := sg.Params.Alias
645✔
1330
        if fieldName == "" {
665✔
1331
                fieldName = fmt.Sprintf("checkpwd(%s)", sg.Attr)
20✔
1332
        }
20✔
1333
        return enc.AddValue(dst, enc.idForAttr(fieldName), c)
645✔
1334
}
1335

1336
func alreadySeen(parentIds []uint64, uid uint64) bool {
48✔
1337
        for _, id := range parentIds {
102✔
1338
                if id == uid {
60✔
1339
                        return true
6✔
1340
                }
6✔
1341
        }
1342
        return false
42✔
1343
}
1344

1345
func facetName(fieldName string, f *api.Facet) string {
461✔
1346
        if f.Alias != "" {
466✔
1347
                return f.Alias
5✔
1348
        }
5✔
1349
        return fieldName + x.FacetDelimiter + f.Key
456✔
1350
}
1351

1352
// This method gets the values and children for a subprotos.
1353
func (sg *SubGraph) preTraverse(enc *encoder, uid uint64, dst fastJsonNode) error {
65,128✔
1354
        if sg.Params.IgnoreReflex {
65,176✔
1355
                if alreadySeen(sg.Params.ParentIds, uid) {
54✔
1356
                        // A node can't have itself as the child at any level.
6✔
1357
                        return nil
6✔
1358
                }
6✔
1359
                // Push myself to stack before sending this to children.
1360
                sg.Params.ParentIds = append(sg.Params.ParentIds, uid)
42✔
1361
        }
1362

1363
        var invalidUids map[uint64]bool
65,122✔
1364
        // We go through all predicate children of the subprotos.
65,122✔
1365
        for _, pc := range sg.Children {
167,343✔
1366
                if pc.Params.IgnoreResult {
102,221✔
UNCOV
1367
                        continue
×
1368
                }
1369
                if pc.IsInternal() {
124,642✔
1370
                        if pc.Params.Expand != "" {
22,421✔
UNCOV
1371
                                continue
×
1372
                        }
1373
                        if pc.Params.Normalize && pc.Params.Alias == "" {
22,425✔
1374
                                continue
4✔
1375
                        }
1376
                        if err := pc.addInternalNode(enc, uid, dst); err != nil {
22,417✔
UNCOV
1377
                                return err
×
UNCOV
1378
                        }
×
1379
                        continue
22,417✔
1380
                }
1381

1382
                if len(pc.uidMatrix) == 0 {
79,902✔
1383
                        // Can happen in recurse query.
102✔
1384
                        continue
102✔
1385
                }
1386
                if len(pc.facetsMatrix) > 0 && len(pc.facetsMatrix) != len(pc.uidMatrix) {
79,698✔
1387
                        return errors.Errorf("Length of facetsMatrix and uidMatrix mismatch: %d vs %d",
×
1388
                                len(pc.facetsMatrix), len(pc.uidMatrix))
×
UNCOV
1389
                }
×
1390

1391
                idx := algo.IndexOf(pc.SrcUIDs, uid)
79,698✔
1392
                if idx < 0 {
79,698✔
UNCOV
1393
                        continue
×
1394
                }
1395
                if pc.Params.IsGroupBy {
79,721✔
1396
                        if len(pc.GroupbyRes) <= idx {
23✔
1397
                                return errors.Errorf("Unexpected length while adding Groupby. Idx: [%v], len: [%v]",
×
UNCOV
1398
                                        idx, len(pc.GroupbyRes))
×
UNCOV
1399
                        }
×
1400
                        if err := pc.addGroupby(enc, dst, pc.GroupbyRes[idx], pc.fieldName()); err != nil {
23✔
1401
                                return err
×
1402
                        }
×
1403
                        continue
23✔
1404
                }
1405

1406
                fieldName := pc.fieldName()
79,675✔
1407
                switch {
79,675✔
1408
                case len(pc.counts) > 0:
102✔
1409
                        if err := pc.addCount(enc, uint64(pc.counts[idx]), dst); err != nil {
102✔
UNCOV
1410
                                return err
×
UNCOV
1411
                        }
×
1412

1413
                case pc.SrcFunc != nil && pc.SrcFunc.Name == "checkpwd":
645✔
1414
                        if err := pc.addCheckPwd(enc, pc.valueMatrix[idx].Values, dst); err != nil {
645✔
UNCOV
1415
                                return err
×
UNCOV
1416
                        }
×
1417

1418
                case idx < len(pc.uidMatrix) && len(pc.uidMatrix[idx].Uids) > 0:
9,177✔
1419
                        var fcsList []*pb.Facets
9,177✔
1420
                        if pc.Params.Facet != nil {
9,442✔
1421
                                fcsList = pc.facetsMatrix[idx].FacetsList
265✔
1422
                        }
265✔
1423

1424
                        if sg.Params.IgnoreReflex {
9,192✔
1425
                                pc.Params.ParentIds = sg.Params.ParentIds
15✔
1426
                        }
15✔
1427

1428
                        // calculate it once to avoid multiple call to idToAttr()
1429
                        fieldID := enc.idForAttr(fieldName)
9,177✔
1430
                        // Add len of fieldName to enc.curSize.
9,177✔
1431
                        enc.curSize += uint64(len(fieldName))
9,177✔
1432

9,177✔
1433
                        // We create as many predicate entity children as the length of uids for
9,177✔
1434
                        // this predicate.
9,177✔
1435
                        ul := pc.uidMatrix[idx]
9,177✔
1436
                        for childIdx, childUID := range ul.Uids {
20,786✔
1437
                                if fieldName == "" || (invalidUids != nil && invalidUids[childUID]) {
11,675✔
1438
                                        continue
66✔
1439
                                }
1440
                                uc := enc.newNode(fieldID)
11,543✔
1441
                                if rerr := pc.preTraverse(enc, childUID, uc); rerr != nil {
11,543✔
1442
                                        if rerr.Error() == "_INV_" {
×
1443
                                                if invalidUids == nil {
×
UNCOV
1444
                                                        invalidUids = make(map[uint64]bool)
×
UNCOV
1445
                                                }
×
1446

UNCOV
1447
                                                invalidUids[childUID] = true
×
UNCOV
1448
                                                continue // next UID.
×
1449
                                        }
1450
                                        return rerr
×
1451
                                }
1452

1453
                                if !enc.IsEmpty(uc) {
22,662✔
1454
                                        if sg.Params.GetUid {
11,123✔
1455
                                                if err := enc.SetUID(uc, childUID, enc.uidAttr); err != nil {
4✔
UNCOV
1456
                                                        return err
×
UNCOV
1457
                                                }
×
1458
                                        }
1459

1460
                                        // Add facets nodes.
1461
                                        if pc.Params.Facet != nil && len(fcsList) > childIdx {
11,462✔
1462
                                                fs := fcsList[childIdx].Facets
343✔
1463
                                                if err := enc.attachFacets(uc, fieldName, false, fs, childIdx); err != nil {
343✔
UNCOV
1464
                                                        return err
×
UNCOV
1465
                                                }
×
1466
                                        }
1467

1468
                                        if pc.Params.Normalize {
11,243✔
1469
                                                // We will normalize at each level instead of
124✔
1470
                                                // calling normalize after pretraverse.
124✔
1471
                                                // Now normalize() only flattens one level,
124✔
1472
                                                // the expectation is that its children have
124✔
1473
                                                // already been normalized.
124✔
1474

124✔
1475
                                                // TODO(ashish): Check reason for calling fixOrder() here in
124✔
1476
                                                // processNodeUids(), just before calling normalize().
124✔
1477
                                                enc.fixOrder(uc)
124✔
1478
                                                normAttrs, err := enc.normalize(uc)
124✔
1479
                                                if err != nil {
124✔
UNCOV
1480
                                                        return err
×
UNCOV
1481
                                                }
×
1482

1483
                                                for _, c := range normAttrs {
271✔
1484
                                                        // Adding as list child irrespective of the type of pc
147✔
1485
                                                        // (list or non-list), otherwise result might be inconsistent or might
147✔
1486
                                                        // depend on children and grandchildren of pc. Consider the case:
147✔
1487
                                                        //         boss: uid .
147✔
1488
                                                        //         friend: [uid] .
147✔
1489
                                                        //         name: string .
147✔
1490
                                                        // For query like:
147✔
1491
                                                        // {
147✔
1492
                                                        //         me(func: uid(0x1)) {
147✔
1493
                                                        //                 boss @normalize {
147✔
1494
                                                        //                         name
147✔
1495
                                                        //                 }
147✔
1496
                                                        //         }
147✔
1497
                                                        // }
147✔
1498
                                                        // boss will be non list type in response, but for query like:
147✔
1499
                                                        // {
147✔
1500
                                                        //         me(func: uid(0x1)) {
147✔
1501
                                                        //                 boss @normalize {
147✔
1502
                                                        //                         friend {
147✔
1503
                                                        //                                 name
147✔
1504
                                                        //                         }
147✔
1505
                                                        //                 }
147✔
1506
                                                        //         }
147✔
1507
                                                        // }
147✔
1508
                                                        // boss should be of list type because there can be multiple friends of
147✔
1509
                                                        // boss.
147✔
1510
                                                        node := enc.newNode(fieldID)
147✔
1511
                                                        enc.setVisited(node, true)
147✔
1512
                                                        enc.addChildren(node, c)
147✔
1513
                                                        enc.AddListChild(dst, node)
147✔
1514
                                                }
147✔
1515
                                                continue
124✔
1516
                                        }
1517
                                        if pc.List {
21,253✔
1518
                                                enc.AddListChild(dst, uc)
10,258✔
1519
                                        } else {
10,995✔
1520
                                                enc.AddMapChild(dst, uc)
737✔
1521
                                        }
737✔
1522
                                }
1523
                        }
1524

1525
                        // add value for count(uid) nodes if any.
1526
                        if _, err := pc.handleCountUIDNodes(enc, dst, len(ul.Uids)); err != nil {
9,177✔
UNCOV
1527
                                return err
×
1528
                        }
×
1529
                default:
69,751✔
1530
                        if pc.Params.Alias == "" && len(pc.Params.Langs) > 0 && pc.Params.Langs[0] != "*" {
70,263✔
1531
                                fieldName += "@"
512✔
1532
                                fieldName += strings.Join(pc.Params.Langs, ":")
512✔
1533
                        }
512✔
1534

1535
                        // calculate it once to avoid multiple call to idToAttr()
1536
                        fieldID := enc.idForAttr(fieldName)
69,751✔
1537
                        // Add len of fieldName to enc.curSize.
69,751✔
1538
                        enc.curSize += uint64(len(fieldName))
69,751✔
1539

69,751✔
1540
                        if pc.Attr == "uid" {
80,594✔
1541
                                if err := enc.SetUID(dst, uid, fieldID); err != nil {
10,843✔
UNCOV
1542
                                        return err
×
UNCOV
1543
                                }
×
1544
                                continue
10,843✔
1545
                        }
1546

1547
                        if len(pc.facetsMatrix) > idx && len(pc.facetsMatrix[idx].FacetsList) > 0 {
59,057✔
1548
                                // In case of Value we have only one Facets.
149✔
1549
                                for i, fcts := range pc.facetsMatrix[idx].FacetsList {
310✔
1550
                                        if err := enc.attachFacets(dst, fieldName, pc.List, fcts.Facets, i); err != nil {
161✔
UNCOV
1551
                                                return err
×
UNCOV
1552
                                        }
×
1553
                                }
1554
                        }
1555

1556
                        if len(pc.valueMatrix) <= idx {
70,434✔
1557
                                continue
11,526✔
1558
                        }
1559

1560
                        for i, tv := range pc.valueMatrix[idx].Values {
93,810✔
1561
                                // if conversion not possible, we ignore it in the result.
46,428✔
1562
                                sv, convErr := convertWithBestEffort(tv, pc.Attr)
46,428✔
1563
                                if convErr != nil {
46,429✔
1564
                                        return convErr
1✔
1565
                                }
1✔
1566

1567
                                if pc.Params.ExpandAll && len(pc.LangTags[idx].Lang) != 0 {
46,517✔
1568
                                        if i >= len(pc.LangTags[idx].Lang) {
90✔
UNCOV
1569
                                                return errors.Errorf(
×
UNCOV
1570
                                                        "pb.error: all lang tags should be either present or absent")
×
UNCOV
1571
                                        }
×
1572
                                        fieldNameWithTag := fieldName
90✔
1573
                                        lang := pc.LangTags[idx].Lang[i]
90✔
1574
                                        if lang != "" && lang != "*" {
100✔
1575
                                                fieldNameWithTag += "@" + lang
10✔
1576
                                        }
10✔
1577
                                        encodeAsList := pc.List && lang == ""
90✔
1578
                                        if err := enc.AddListValue(dst, enc.idForAttr(fieldNameWithTag),
90✔
1579
                                                sv, encodeAsList); err != nil {
90✔
UNCOV
1580
                                                return err
×
UNCOV
1581
                                        }
×
1582
                                        continue
90✔
1583
                                }
1584

1585
                                encodeAsList := pc.List && len(pc.Params.Langs) == 0
46,337✔
1586
                                if !pc.Params.Normalize {
92,493✔
1587
                                        err := enc.AddListValue(dst, fieldID, sv, encodeAsList)
46,156✔
1588
                                        if err != nil {
46,156✔
UNCOV
1589
                                                return err
×
UNCOV
1590
                                        }
×
1591
                                        continue
46,156✔
1592
                                }
1593
                                // If the query had the normalize directive, then we only add nodes
1594
                                // with an Alias.
1595
                                if pc.Params.Alias != "" {
331✔
1596
                                        err := enc.AddListValue(dst, fieldID, sv, encodeAsList)
150✔
1597
                                        if err != nil {
150✔
UNCOV
1598
                                                return err
×
UNCOV
1599
                                        }
×
1600
                                }
1601
                        }
1602
                }
1603
        }
1604

1605
        if sg.Params.IgnoreReflex && len(sg.Params.ParentIds) > 0 {
65,163✔
1606
                // Lets pop the stack.
42✔
1607
                sg.Params.ParentIds = (sg.Params.ParentIds)[:len(sg.Params.ParentIds)-1]
42✔
1608
        }
42✔
1609

1610
        // Only for shortest path query we want to return uid always if there is
1611
        // nothing else at that level.
1612
        if (sg.Params.GetUid && !enc.IsEmpty(dst)) || sg.Params.Shortest {
65,424✔
1613
                if err := enc.SetUID(dst, uid, enc.uidAttr); err != nil {
303✔
UNCOV
1614
                        return err
×
UNCOV
1615
                }
×
1616
        }
1617

1618
        if sg.pathMeta != nil {
65,187✔
1619
                totalWeight := types.Val{
66✔
1620
                        Tid:   types.FloatID,
66✔
1621
                        Value: sg.pathMeta.weight,
66✔
1622
                }
66✔
1623
                if err := enc.AddValue(dst, enc.idForAttr("_weight_"), totalWeight); err != nil {
66✔
UNCOV
1624
                        return err
×
UNCOV
1625
                }
×
1626
        }
1627

1628
        return nil
65,121✔
1629
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc