github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/query/outputnode.go (about) 1 /* 2 * Copyright 2017-2018 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 "encoding/json" 22 "fmt" 23 "sort" 24 "strings" 25 "time" 26 27 "github.com/golang/glog" 28 "github.com/pkg/errors" 29 geom "github.com/twpayne/go-geom" 30 "github.com/twpayne/go-geom/encoding/geojson" 31 32 "github.com/dgraph-io/dgo/protos/api" 33 "github.com/dgraph-io/dgraph/algo" 34 "github.com/dgraph-io/dgraph/protos/pb" 35 "github.com/dgraph-io/dgraph/task" 36 "github.com/dgraph-io/dgraph/types" 37 "github.com/dgraph-io/dgraph/types/facets" 38 "github.com/dgraph-io/dgraph/x" 39 ) 40 41 // ToJson converts the list of subgraph into a JSON response by calling toFastJSON. 42 func ToJson(l *Latency, sgl []*SubGraph) ([]byte, error) { 43 sgr := &SubGraph{} 44 for _, sg := range sgl { 45 if sg.Params.Alias == "var" || sg.Params.Alias == "shortest" { 46 continue 47 } 48 if sg.Params.GetUid { 49 sgr.Params.GetUid = true 50 } 51 sgr.Children = append(sgr.Children, sg) 52 } 53 return sgr.toFastJSON(l) 54 } 55 56 // outputNode is the generic output / writer for preTraverse. 57 type outputNode interface { 58 AddValue(attr string, v types.Val) 59 AddListValue(attr string, v types.Val, list bool) 60 AddMapChild(attr string, node outputNode, isRoot bool) 61 AddListChild(attr string, child outputNode) 62 New(attr string) outputNode 63 SetUID(uid uint64, attr string) 64 IsEmpty() bool 65 66 addCountAtRoot(*SubGraph) 67 addGroupby(*SubGraph, *groupResults, string) 68 addAggregations(*SubGraph) error 69 } 70 71 func makeScalarNode(attr string, isChild bool, val []byte, list bool) *fastJsonNode { 72 return &fastJsonNode{ 73 attr: attr, 74 isChild: isChild, 75 scalarVal: val, 76 list: list, 77 } 78 } 79 80 type fastJsonNode struct { 81 attr string 82 order int // relative ordering (for sorted results) 83 isChild bool 84 scalarVal []byte 85 attrs []*fastJsonNode 86 list bool 87 } 88 89 func (fj *fastJsonNode) AddValue(attr string, v types.Val) { 90 fj.AddListValue(attr, v, false) 91 } 92 93 func (fj *fastJsonNode) AddListValue(attr string, v types.Val, list bool) { 94 if bs, err := valToBytes(v); err == nil { 95 fj.attrs = append(fj.attrs, makeScalarNode(attr, false, bs, list)) 96 } 97 } 98 99 func (fj *fastJsonNode) AddMapChild(attr string, val outputNode, isRoot bool) { 100 var childNode *fastJsonNode 101 for _, c := range fj.attrs { 102 if c.attr == attr { 103 childNode = c 104 break 105 } 106 } 107 108 if childNode != nil { 109 val.(*fastJsonNode).isChild = true 110 val.(*fastJsonNode).attr = attr 111 childNode.attrs = append(childNode.attrs, val.(*fastJsonNode).attrs...) 112 } else { 113 val.(*fastJsonNode).isChild = false 114 val.(*fastJsonNode).attr = attr 115 fj.attrs = append(fj.attrs, val.(*fastJsonNode)) 116 } 117 } 118 119 func (fj *fastJsonNode) AddListChild(attr string, child outputNode) { 120 child.(*fastJsonNode).attr = attr 121 child.(*fastJsonNode).isChild = true 122 fj.attrs = append(fj.attrs, child.(*fastJsonNode)) 123 } 124 125 func (fj *fastJsonNode) New(attr string) outputNode { 126 return &fastJsonNode{attr: attr, isChild: false} 127 } 128 129 func (fj *fastJsonNode) SetUID(uid uint64, attr string) { 130 // if we're in debug mode, uid may be added second time, skip this 131 if attr == "uid" { 132 for _, a := range fj.attrs { 133 if a.attr == attr { 134 return 135 } 136 } 137 } 138 fj.attrs = append(fj.attrs, makeScalarNode(attr, false, []byte(fmt.Sprintf("\"%#x\"", uid)), 139 false)) 140 } 141 142 func (fj *fastJsonNode) IsEmpty() bool { 143 return len(fj.attrs) == 0 144 } 145 146 func valToBytes(v types.Val) ([]byte, error) { 147 switch v.Tid { 148 case types.StringID, types.DefaultID: 149 return json.Marshal(v.Value) 150 case types.BinaryID: 151 return []byte(fmt.Sprintf("%q", v.Value)), nil 152 case types.IntID: 153 return []byte(fmt.Sprintf("%d", v.Value)), nil 154 case types.FloatID: 155 return []byte(fmt.Sprintf("%f", v.Value)), nil 156 case types.BoolID: 157 if v.Value.(bool) { 158 return []byte("true"), nil 159 } 160 return []byte("false"), nil 161 case types.DateTimeID: 162 // Return empty string instead of zero-time value string - issue#3166 163 t := v.Value.(time.Time) 164 if t.IsZero() { 165 return []byte(`""`), nil 166 } 167 return t.MarshalJSON() 168 case types.GeoID: 169 return geojson.Marshal(v.Value.(geom.T)) 170 case types.UidID: 171 return []byte(fmt.Sprintf("\"%#x\"", v.Value)), nil 172 case types.PasswordID: 173 return []byte(fmt.Sprintf("%q", v.Value.(string))), nil 174 default: 175 return nil, errors.New("Unsupported types.Val.Tid") 176 } 177 } 178 179 type nodeSlice []*fastJsonNode 180 181 func (n nodeSlice) Len() int { 182 return len(n) 183 } 184 185 func (n nodeSlice) Less(i, j int) bool { 186 cmp := strings.Compare(n[i].attr, n[j].attr) 187 if cmp == 0 { 188 return n[i].order < n[j].order 189 } 190 return cmp < 0 191 } 192 193 func (n nodeSlice) Swap(i, j int) { 194 n[i], n[j] = n[j], n[i] 195 } 196 197 func (fj *fastJsonNode) writeKey(out *bytes.Buffer) { 198 out.WriteRune('"') 199 out.WriteString(fj.attr) 200 out.WriteRune('"') 201 out.WriteRune(':') 202 } 203 204 func (fj *fastJsonNode) encode(out *bytes.Buffer) { 205 // set relative ordering 206 for i, a := range fj.attrs { 207 a.order = i 208 } 209 210 i := 0 211 if i < len(fj.attrs) { 212 out.WriteRune('{') 213 cur := fj.attrs[i] 214 i++ 215 cnt := 1 216 last := false 217 inArray := false 218 for { 219 var next *fastJsonNode 220 if i < len(fj.attrs) { 221 next = fj.attrs[i] 222 i++ 223 } else { 224 last = true 225 } 226 227 if !last { 228 if cur.attr == next.attr { 229 if cnt == 1 { 230 cur.writeKey(out) 231 out.WriteRune('[') 232 inArray = true 233 } 234 cur.encode(out) 235 cnt++ 236 } else { 237 if cnt == 1 { 238 cur.writeKey(out) 239 if cur.isChild || cur.list { 240 out.WriteRune('[') 241 inArray = true 242 } 243 } 244 cur.encode(out) 245 if cnt != 1 || (cur.isChild || cur.list) { 246 out.WriteRune(']') 247 inArray = false 248 } 249 cnt = 1 250 } 251 out.WriteRune(',') 252 253 cur = next 254 } else { 255 if cnt == 1 { 256 cur.writeKey(out) 257 } 258 if (cur.isChild || cur.list) && !inArray { 259 out.WriteRune('[') 260 } 261 cur.encode(out) 262 if cnt != 1 || (cur.isChild || cur.list) { 263 out.WriteRune(']') 264 } 265 break 266 } 267 } 268 out.WriteRune('}') 269 } else { 270 out.Write(fj.scalarVal) 271 } 272 } 273 274 func merge(parent [][]*fastJsonNode, child [][]*fastJsonNode) ([][]*fastJsonNode, error) { 275 if len(parent) == 0 { 276 return child, nil 277 } 278 279 // Here we merge two slices of maps. 280 mergedList := make([][]*fastJsonNode, 0, len(parent)*len(child)) 281 cnt := 0 282 for _, pa := range parent { 283 for _, ca := range child { 284 cnt += len(pa) + len(ca) 285 if cnt > x.Config.NormalizeNodeLimit { 286 return nil, errors.Errorf( 287 "Couldn't evaluate @normalize directive - too many results") 288 } 289 list := make([]*fastJsonNode, 0, len(pa)+len(ca)) 290 list = append(list, pa...) 291 list = append(list, ca...) 292 mergedList = append(mergedList, list) 293 } 294 } 295 return mergedList, nil 296 } 297 298 func (fj *fastJsonNode) normalize() ([][]*fastJsonNode, error) { 299 cnt := 0 300 for _, a := range fj.attrs { 301 if a.isChild { 302 cnt++ 303 } 304 } 305 306 if cnt == 0 { 307 // Recursion base case 308 // There are no children, we can just return slice with fj.attrs map. 309 return [][]*fastJsonNode{fj.attrs}, nil 310 } 311 312 parentSlice := make([][]*fastJsonNode, 0, 5) 313 // If the parents has attrs, lets add them to the slice so that it can be 314 // merged with children later. 315 attrs := make([]*fastJsonNode, 0, len(fj.attrs)-cnt) 316 for _, a := range fj.attrs { 317 if !a.isChild { 318 attrs = append(attrs, a) 319 } 320 } 321 parentSlice = append(parentSlice, attrs) 322 323 for ci := 0; ci < len(fj.attrs); { 324 childNode := fj.attrs[ci] 325 if !childNode.isChild { 326 ci++ 327 continue 328 } 329 childSlice := make([][]*fastJsonNode, 0, 5) 330 for ci < len(fj.attrs) && childNode.attr == fj.attrs[ci].attr { 331 normalized, err := fj.attrs[ci].normalize() 332 if err != nil { 333 return nil, err 334 } 335 childSlice = append(childSlice, normalized...) 336 ci++ 337 } 338 // Merging with parent. 339 var err error 340 parentSlice, err = merge(parentSlice, childSlice) 341 if err != nil { 342 return nil, err 343 } 344 } 345 for i, slice := range parentSlice { 346 sort.Sort(nodeSlice(slice)) 347 348 first := -1 349 last := 0 350 for i := range slice { 351 if slice[i].attr == "uid" { 352 if first == -1 { 353 first = i 354 } 355 last = i 356 } 357 } 358 if first != -1 && first != last { 359 if first == 0 { 360 parentSlice[i] = slice[last:] 361 } else { 362 parentSlice[i] = append(slice[:first], slice[last:]...) 363 } 364 } 365 } 366 367 return parentSlice, nil 368 } 369 370 func (fj *fastJsonNode) addGroupby(sg *SubGraph, res *groupResults, fname string) { 371 // Don't add empty groupby 372 if len(res.group) == 0 { 373 return 374 } 375 g := fj.New(fname) 376 for _, grp := range res.group { 377 uc := g.New("@groupby") 378 for _, it := range grp.keys { 379 uc.AddValue(it.attr, it.key) 380 } 381 for _, it := range grp.aggregates { 382 uc.AddValue(it.attr, it.key) 383 } 384 g.AddListChild("@groupby", uc) 385 } 386 fj.AddListChild(fname, g) 387 } 388 389 func (fj *fastJsonNode) addCountAtRoot(sg *SubGraph) { 390 c := types.ValueForType(types.IntID) 391 c.Value = int64(len(sg.DestUIDs.Uids)) 392 n1 := fj.New(sg.Params.Alias) 393 field := sg.Params.uidCountAlias 394 if field == "" { 395 field = "count" 396 } 397 n1.AddValue(field, c) 398 fj.AddListChild(sg.Params.Alias, n1) 399 } 400 401 func (fj *fastJsonNode) addAggregations(sg *SubGraph) error { 402 for _, child := range sg.Children { 403 aggVal, ok := child.Params.uidToVal[0] 404 if !ok { 405 if len(child.Params.NeedsVar) == 0 { 406 return errors.Errorf("Only aggregated variables allowed within empty block.") 407 } 408 // the aggregation didn't happen, most likely was called with unset vars. 409 // See: query.go:fillVars 410 aggVal = types.Val{Tid: types.FloatID, Value: float64(0)} 411 } 412 if child.Params.Normalize && child.Params.Alias == "" { 413 continue 414 } 415 fieldName := aggWithVarFieldName(child) 416 n1 := fj.New(fieldName) 417 n1.AddValue(fieldName, aggVal) 418 fj.AddListChild(sg.Params.Alias, n1) 419 } 420 if fj.IsEmpty() { 421 fj.AddListChild(sg.Params.Alias, &fastJsonNode{}) 422 } 423 return nil 424 } 425 426 func processNodeUids(fj *fastJsonNode, sg *SubGraph) error { 427 var seedNode *fastJsonNode 428 if sg.Params.IsEmpty { 429 return fj.addAggregations(sg) 430 } 431 432 if sg.uidMatrix == nil { 433 fj.AddListChild(sg.Params.Alias, &fastJsonNode{}) 434 return nil 435 } 436 437 hasChild := false 438 if sg.Params.uidCount && !(sg.Params.uidCountAlias == "" && sg.Params.Normalize) { 439 hasChild = true 440 fj.addCountAtRoot(sg) 441 } 442 443 if sg.Params.isGroupBy { 444 if len(sg.GroupbyRes) == 0 { 445 return errors.Errorf("Expected GroupbyRes to have length > 0.") 446 } 447 fj.addGroupby(sg, sg.GroupbyRes[0], sg.Params.Alias) 448 return nil 449 } 450 451 lenList := len(sg.uidMatrix[0].Uids) 452 for i := 0; i < lenList; i++ { 453 uid := sg.uidMatrix[0].Uids[i] 454 if algo.IndexOf(sg.DestUIDs, uid) < 0 { 455 // This UID was filtered. So Ignore it. 456 continue 457 } 458 459 n1 := seedNode.New(sg.Params.Alias) 460 if err := sg.preTraverse(uid, n1); err != nil { 461 if err.Error() == "_INV_" { 462 continue 463 } 464 return err 465 } 466 467 if n1.IsEmpty() { 468 continue 469 } 470 471 hasChild = true 472 if !sg.Params.Normalize { 473 fj.AddListChild(sg.Params.Alias, n1) 474 continue 475 } 476 477 // Lets normalize the response now. 478 normalized, err := n1.(*fastJsonNode).normalize() 479 if err != nil { 480 return err 481 } 482 for _, c := range normalized { 483 fj.AddListChild(sg.Params.Alias, &fastJsonNode{attrs: c}) 484 } 485 } 486 487 if !hasChild { 488 // So that we return an empty key if the root didn't have any children. 489 fj.AddListChild(sg.Params.Alias, &fastJsonNode{}) 490 } 491 return nil 492 } 493 494 // Extensions represents the extra information appended to query results. 495 type Extensions struct { 496 Latency *api.Latency `json:"server_latency,omitempty"` 497 Txn *api.TxnContext `json:"txn,omitempty"` 498 } 499 500 func (sg *SubGraph) toFastJSON(l *Latency) ([]byte, error) { 501 encodingStart := time.Now() 502 defer func() { 503 l.Json = time.Since(encodingStart) 504 }() 505 506 var seedNode *fastJsonNode 507 var err error 508 n := seedNode.New("_root_") 509 for _, sg := range sg.Children { 510 err = processNodeUids(n.(*fastJsonNode), sg) 511 if err != nil { 512 return nil, err 513 } 514 } 515 516 // According to GraphQL spec response should only contain data, errors and extensions as top 517 // level keys. Hence we send server_latency under extensions key. 518 // https://facebook.github.io/graphql/#sec-Response-Format 519 520 var bufw bytes.Buffer 521 if len(n.(*fastJsonNode).attrs) == 0 { 522 bufw.WriteString(`{}`) 523 } else { 524 n.(*fastJsonNode).encode(&bufw) 525 } 526 return bufw.Bytes(), nil 527 } 528 529 func (sg *SubGraph) fieldName() string { 530 fieldName := sg.Attr 531 if sg.Params.Alias != "" { 532 fieldName = sg.Params.Alias 533 } 534 return fieldName 535 } 536 537 func addCount(pc *SubGraph, count uint64, dst outputNode) { 538 if pc.Params.Normalize && pc.Params.Alias == "" { 539 return 540 } 541 c := types.ValueForType(types.IntID) 542 c.Value = int64(count) 543 fieldName := pc.Params.Alias 544 if fieldName == "" { 545 fieldName = fmt.Sprintf("count(%s)", pc.Attr) 546 } 547 dst.AddValue(fieldName, c) 548 } 549 550 func aggWithVarFieldName(pc *SubGraph) string { 551 if pc.Params.Alias != "" { 552 return pc.Params.Alias 553 } 554 fieldName := fmt.Sprintf("val(%v)", pc.Params.Var) 555 if len(pc.Params.NeedsVar) > 0 { 556 fieldName = fmt.Sprintf("val(%v)", pc.Params.NeedsVar[0].Name) 557 if pc.SrcFunc != nil { 558 fieldName = fmt.Sprintf("%s(%v)", pc.SrcFunc.Name, fieldName) 559 } 560 } 561 return fieldName 562 } 563 564 func addInternalNode(pc *SubGraph, uid uint64, dst outputNode) error { 565 sv, ok := pc.Params.uidToVal[uid] 566 if !ok || sv.Value == nil { 567 return nil 568 } 569 fieldName := aggWithVarFieldName(pc) 570 dst.AddValue(fieldName, sv) 571 return nil 572 } 573 574 func addCheckPwd(pc *SubGraph, vals []*pb.TaskValue, dst outputNode) { 575 c := types.ValueForType(types.BoolID) 576 if len(vals) == 0 { 577 c.Value = false 578 } else { 579 c.Value = task.ToBool(vals[0]) 580 } 581 582 fieldName := pc.Params.Alias 583 if fieldName == "" { 584 fieldName = fmt.Sprintf("checkpwd(%s)", pc.Attr) 585 } 586 dst.AddValue(fieldName, c) 587 } 588 589 func alreadySeen(parentIds []uint64, uid uint64) bool { 590 for _, id := range parentIds { 591 if id == uid { 592 return true 593 } 594 } 595 return false 596 } 597 598 func facetName(fieldName string, f *api.Facet) string { 599 if f.Alias != "" { 600 return f.Alias 601 } 602 return fieldName + x.FacetDelimeter + f.Key 603 } 604 605 // This method gets the values and children for a subprotos. 606 func (sg *SubGraph) preTraverse(uid uint64, dst outputNode) error { 607 if sg.Params.IgnoreReflex { 608 if alreadySeen(sg.Params.parentIds, uid) { 609 // A node can't have itself as the child at any level. 610 return nil 611 } 612 // Push myself to stack before sending this to children. 613 sg.Params.parentIds = append(sg.Params.parentIds, uid) 614 } 615 616 var invalidUids map[uint64]bool 617 // We go through all predicate children of the subprotos. 618 for _, pc := range sg.Children { 619 if pc.Params.ignoreResult { 620 continue 621 } 622 if pc.IsInternal() { 623 if pc.Params.Expand != "" { 624 continue 625 } 626 if pc.Params.Normalize && pc.Params.Alias == "" { 627 continue 628 } 629 if err := addInternalNode(pc, uid, dst); err != nil { 630 return err 631 } 632 continue 633 } 634 635 if len(pc.uidMatrix) == 0 { 636 // Can happen in recurse query. 637 continue 638 } 639 if len(pc.facetsMatrix) > 0 && len(pc.facetsMatrix) != len(pc.uidMatrix) { 640 return errors.Errorf("Length of facetsMatrix and uidMatrix mismatch: %d vs %d", 641 len(pc.facetsMatrix), len(pc.uidMatrix)) 642 } 643 644 idx := algo.IndexOf(pc.SrcUIDs, uid) 645 if idx < 0 { 646 continue 647 } 648 if pc.Params.isGroupBy { 649 if len(pc.GroupbyRes) <= idx { 650 return errors.Errorf("Unexpected length while adding Groupby. Idx: [%v], len: [%v]", 651 idx, len(pc.GroupbyRes)) 652 } 653 dst.addGroupby(pc, pc.GroupbyRes[idx], pc.fieldName()) 654 continue 655 } 656 657 fieldName := pc.fieldName() 658 if len(pc.counts) > 0 { 659 addCount(pc, uint64(pc.counts[idx]), dst) 660 661 } else if pc.SrcFunc != nil && pc.SrcFunc.Name == "checkpwd" { 662 addCheckPwd(pc, pc.valueMatrix[idx].Values, dst) 663 664 } else if idx < len(pc.uidMatrix) && len(pc.uidMatrix[idx].Uids) > 0 { 665 var fcsList []*pb.Facets 666 if pc.Params.Facet != nil { 667 fcsList = pc.facetsMatrix[idx].FacetsList 668 } 669 670 if sg.Params.IgnoreReflex { 671 pc.Params.parentIds = sg.Params.parentIds 672 } 673 // We create as many predicate entity children as the length of uids for 674 // this predicate. 675 ul := pc.uidMatrix[idx] 676 for childIdx, childUID := range ul.Uids { 677 if fieldName == "" || (invalidUids != nil && invalidUids[childUID]) { 678 continue 679 } 680 uc := dst.New(fieldName) 681 if rerr := pc.preTraverse(childUID, uc); rerr != nil { 682 if rerr.Error() == "_INV_" { 683 if invalidUids == nil { 684 invalidUids = make(map[uint64]bool) 685 } 686 687 invalidUids[childUID] = true 688 continue // next UID. 689 } 690 // Some other error. 691 glog.Errorf("Error while traversal: %v", rerr) 692 return rerr 693 } 694 695 if pc.Params.Facet != nil && len(fcsList) > childIdx { 696 fs := fcsList[childIdx] 697 for _, f := range fs.Facets { 698 fVal, err := facets.ValFor(f) 699 if err != nil { 700 return err 701 } 702 703 uc.AddValue(facetName(fieldName, f), fVal) 704 } 705 } 706 707 if !uc.IsEmpty() { 708 if sg.Params.GetUid { 709 uc.SetUID(childUID, "uid") 710 } 711 if pc.List { 712 dst.AddListChild(fieldName, uc) 713 } else { 714 dst.AddMapChild(fieldName, uc, false) 715 } 716 } 717 } 718 if pc.Params.uidCount && !(pc.Params.uidCountAlias == "" && pc.Params.Normalize) { 719 uc := dst.New(fieldName) 720 c := types.ValueForType(types.IntID) 721 c.Value = int64(len(ul.Uids)) 722 alias := pc.Params.uidCountAlias 723 if alias == "" { 724 alias = "count" 725 } 726 uc.AddValue(alias, c) 727 dst.AddListChild(fieldName, uc) 728 } 729 } else { 730 if pc.Params.Alias == "" && len(pc.Params.Langs) > 0 { 731 fieldName += "@" 732 fieldName += strings.Join(pc.Params.Langs, ":") 733 } 734 735 if pc.Attr == "uid" { 736 dst.SetUID(uid, pc.fieldName()) 737 continue 738 } 739 740 if len(pc.facetsMatrix) > idx && len(pc.facetsMatrix[idx].FacetsList) > 0 { 741 // in case of Value we have only one Facets 742 for _, f := range pc.facetsMatrix[idx].FacetsList[0].Facets { 743 fVal, err := facets.ValFor(f) 744 if err != nil { 745 return err 746 } 747 748 dst.AddValue(facetName(fieldName, f), fVal) 749 } 750 } 751 752 if len(pc.valueMatrix) <= idx { 753 continue 754 } 755 756 for i, tv := range pc.valueMatrix[idx].Values { 757 // if conversion not possible, we ignore it in the result. 758 sv, convErr := convertWithBestEffort(tv, pc.Attr) 759 if convErr != nil { 760 return convErr 761 } 762 763 if pc.Params.expandAll && len(pc.LangTags[idx].Lang) != 0 { 764 if i >= len(pc.LangTags[idx].Lang) { 765 return errors.Errorf( 766 "pb.error: all lang tags should be either present or absent") 767 } 768 fieldNameWithTag := fieldName 769 lang := pc.LangTags[idx].Lang[i] 770 if lang != "" { 771 fieldNameWithTag += "@" + lang 772 } 773 encodeAsList := pc.List && len(lang) == 0 774 dst.AddListValue(fieldNameWithTag, sv, encodeAsList) 775 continue 776 } 777 778 encodeAsList := pc.List && len(pc.Params.Langs) == 0 779 if !pc.Params.Normalize { 780 dst.AddListValue(fieldName, sv, encodeAsList) 781 continue 782 } 783 // If the query had the normalize directive, then we only add nodes 784 // with an Alias. 785 if pc.Params.Alias != "" { 786 dst.AddListValue(fieldName, sv, encodeAsList) 787 } 788 } 789 } 790 } 791 792 if sg.Params.IgnoreReflex && len(sg.Params.parentIds) > 0 { 793 // Lets pop the stack. 794 sg.Params.parentIds = (sg.Params.parentIds)[:len(sg.Params.parentIds)-1] 795 } 796 797 // Only for shortest path query we wan't to return uid always if there is 798 // nothing else at that level. 799 if (sg.Params.GetUid && !dst.IsEmpty()) || sg.Params.shortest { 800 dst.SetUID(uid, "uid") 801 } 802 803 if sg.pathMeta != nil { 804 totalWeight := types.Val{ 805 Tid: types.FloatID, 806 Value: sg.pathMeta.weight, 807 } 808 dst.AddValue("_weight_", totalWeight) 809 } 810 811 return nil 812 }