github.com/cayleygraph/cayley@v0.7.7/query/gizmo/traversals.go (about) 1 // Copyright 2017 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gizmo 16 17 // Adds special traversal functions to JS Gizmo objects. Most of these just build the chain of objects, and won't often need the session. 18 19 import ( 20 "errors" 21 "fmt" 22 23 "github.com/dop251/goja" 24 25 "github.com/cayleygraph/cayley/graph" 26 "github.com/cayleygraph/cayley/graph/iterator" 27 "github.com/cayleygraph/cayley/graph/path" 28 "github.com/cayleygraph/cayley/graph/shape" 29 "github.com/cayleygraph/cayley/query" 30 "github.com/cayleygraph/quad" 31 ) 32 33 // pathObject is a Path object in Gizmo. 34 // 35 // Both `.Morphism()` and `.Vertex()` create path objects, which provide the following traversal methods. 36 // Note that `.Vertex()` returns a query object, which is a subclass of path object. 37 // 38 // For these examples, suppose we have the following graph: 39 // 40 // +-------+ +------+ 41 // | alice |----- ->| fred |<-- 42 // +-------+ \---->+-------+-/ +------+ \-+-------+ 43 // ----->| #bob# | | |*emily*| 44 // +---------+--/ --->+-------+ | +-------+ 45 // | charlie | / v 46 // +---------+ / +--------+ 47 // \--- +--------+ |*#greg#*| 48 // \-->| #dani# |------------>+--------+ 49 // +--------+ 50 // 51 // Where every link is a `<follows>` relationship, and the nodes with an extra `#` in the name have an extra `<status>` link. As in, 52 // 53 // <dani> -- <status> --> "cool_person" 54 // 55 // Perhaps these are the influencers in our community. So too are extra `*`s in the name -- these are our smart people, 56 // according to the `<smart_graph>` label, eg, the quad: 57 // 58 // <greg> <status> "smart_person" <smart_graph> . 59 type pathObject struct { 60 s *Session 61 finals bool 62 path *path.Path 63 } 64 65 func (p *pathObject) new(np *path.Path) *pathObject { 66 return &pathObject{ 67 s: p.s, 68 finals: p.finals, 69 path: np, 70 } 71 } 72 73 func (p *pathObject) newVal(np *path.Path) goja.Value { 74 return p.s.vm.ToValue(p.new(np)) 75 } 76 func (p *pathObject) clonePath() *path.Path { 77 np := p.path.Clone() 78 // most likely path will be continued, so we'll put non-capped stack slice 79 // into new path object instead of preserving it in an old one 80 p.path, np = np, p.path 81 return np 82 } 83 func (p *pathObject) buildIteratorTree() graph.Iterator { 84 if p.path == nil { 85 return iterator.NewNull() 86 } 87 return p.path.BuildIteratorOn(p.s.qs) 88 } 89 90 // Filter all paths to ones which, at this point, are on the given node. 91 // Signature: (node, [node..]) 92 // 93 // Arguments: 94 // 95 // * `node`: A string for a node. Can be repeated or a list of strings. 96 // 97 // Example: 98 // // javascript 99 // // Starting from all nodes in the graph, find the paths that follow bob. 100 // // Results in three paths for bob (from alice, charlie and dani).all() 101 // g.V().out("<follows>").is("<bob>").all() 102 func (p *pathObject) Is(call goja.FunctionCall) goja.Value { 103 args, err := toQuadValues(exportArgs(call.Arguments)) 104 if err != nil { 105 return throwErr(p.s.vm, err) 106 } 107 np := p.clonePath().Is(args...) 108 return p.newVal(np) 109 } 110 func (p *pathObject) inout(call goja.FunctionCall, in bool) goja.Value { 111 preds, tags, ok := toViaData(exportArgs(call.Arguments)) 112 if !ok { 113 return throwErr(p.s.vm, errNoVia) 114 } 115 np := p.clonePath() 116 if in { 117 np = np.InWithTags(tags, preds...) 118 } else { 119 np = np.OutWithTags(tags, preds...) 120 } 121 return p.newVal(np) 122 } 123 124 // In is inverse of Out. 125 // Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects. 126 // Signature: ([predicatePath], [tags]) 127 // 128 // Arguments: 129 // 130 // * `predicatePath` (Optional): One of: 131 // * null or undefined: All predicates pointing into this node 132 // * a string: The predicate name to follow into this node 133 // * a list of strings: The predicates to follow into this node 134 // * a query path object: The target of which is a set of predicates to follow. 135 // * `tags` (Optional): One of: 136 // * null or undefined: No tags 137 // * a string: A single tag to add the predicate used to the output set. 138 // * a list of strings: Multiple tags to use as keys to save the predicate used to the output set. 139 // 140 // Example: 141 // 142 // // javascript 143 // // Find the cool people, bob, dani and greg 144 // g.V("cool_person").in("<status>").all() 145 // // Find who follows bob, in this case, alice, charlie, and dani 146 // g.V("<bob>").in("<follows>").all() 147 // // Find who follows the people emily follows, namely, bob and emily 148 // g.V("<emily>").out("<follows>").in("<follows>").all() 149 func (p *pathObject) In(call goja.FunctionCall) goja.Value { 150 return p.inout(call, true) 151 } 152 153 // Out is the work-a-day way to get between nodes, in the forward direction. 154 // Starting with the nodes in `path` on the subject, follow the quads with predicates defined by `predicatePath` to their objects. 155 // Signature: ([predicatePath], [tags]) 156 // 157 // Arguments: 158 // 159 // * `predicatePath` (Optional): One of: 160 // * null or undefined: All predicates pointing out from this node 161 // * a string: The predicate name to follow out from this node 162 // * a list of strings: The predicates to follow out from this node 163 // * a query path object: The target of which is a set of predicates to follow. 164 // * `tags` (Optional): One of: 165 // * null or undefined: No tags 166 // * a string: A single tag to add the predicate used to the output set. 167 // * a list of strings: Multiple tags to use as keys to save the predicate used to the output set. 168 // 169 // Example: 170 // 171 // // javascript 172 // // The working set of this is bob and dani 173 // g.V("<charlie>").out("<follows>").all() 174 // // The working set of this is fred, as alice follows bob and bob follows fred. 175 // g.V("<alice>").out("<follows>").out("<follows>").all() 176 // // Finds all things dani points at. Result is bob, greg and cool_person 177 // g.V("<dani>").out().all() 178 // // Finds all things dani points at on the status linkage. 179 // // Result is bob, greg and cool_person 180 // g.V("<dani>").out(["<follows>", "<status>"]).all() 181 // // Finds all things dani points at on the status linkage, given from a separate query path. 182 // // Result is {"id": "cool_person", "pred": "<status>"} 183 // g.V("<dani>").out(g.V("<status>"), "pred").all() 184 func (p *pathObject) Out(call goja.FunctionCall) goja.Value { 185 return p.inout(call, false) 186 } 187 188 // Both follow the predicate in either direction. Same as Out or In. 189 // Signature: ([predicatePath], [tags]) 190 // 191 // Example: 192 // // javascript 193 // // Find all followers/followees of fred. Returns bob, emily and greg 194 // g.V("<fred>").both("<follows>").all() 195 func (p *pathObject) Both(call goja.FunctionCall) goja.Value { 196 preds, tags, ok := toViaData(exportArgs(call.Arguments)) 197 if !ok { 198 return throwErr(p.s.vm, errNoVia) 199 } 200 np := p.clonePath().BothWithTags(tags, preds...) 201 return p.newVal(np) 202 } 203 func (p *pathObject) follow(ep *pathObject, rev bool) *pathObject { 204 if ep == nil { 205 return p 206 } 207 np := p.clonePath() 208 if rev { 209 np = np.FollowReverse(ep.path) 210 } else { 211 np = np.Follow(ep.path) 212 } 213 return p.new(np) 214 } 215 216 // Follow is the way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path. 217 // 218 // Starts as if at the g.M() and follows through the morphism path. 219 // 220 // Example: 221 // // javascript: 222 // var friendOfFriend = g.Morphism().Out("<follows>").Out("<follows>") 223 // // Returns the followed people of who charlie follows -- a simplistic "friend of my friend" 224 // // and whether or not they have a "cool" status. Potential for recommending followers abounds. 225 // // Returns bob and greg 226 // g.V("<charlie>").follow(friendOfFriend).has("<status>", "cool_person").all() 227 func (p *pathObject) Follow(path *pathObject) *pathObject { 228 return p.follow(path, false) 229 } 230 231 // FollowR is the same as Follow but follows the chain in the reverse direction. Flips "In" and "Out" where appropriate, 232 // the net result being a virtual predicate followed in the reverse direction. 233 // 234 // Starts at the end of the morphism and follows it backwards (with appropriate flipped directions) to the g.M() location. 235 // 236 // Example: 237 // // javascript: 238 // var friendOfFriend = g.Morphism().Out("<follows>").Out("<follows>") 239 // // Returns the third-tier of influencers -- people who follow people who follow the cool people. 240 // // Returns charlie (from bob), charlie (from greg), bob and emily 241 // g.V().has("<status>", "cool_person").followR(friendOfFriend).all() 242 func (p *pathObject) FollowR(path *pathObject) *pathObject { 243 return p.follow(path, true) 244 } 245 246 // FollowRecursive is the same as Follow but follows the chain recursively. 247 // 248 // Starts as if at the g.M() and follows through the morphism path multiple times, returning all nodes encountered. 249 // 250 // Example: 251 // // javascript: 252 // var friend = g.Morphism().out("<follows>") 253 // // Returns all people in Charlie's network. 254 // // Returns bob and dani (from charlie), fred (from bob) and greg (from dani). 255 // g.V("<charlie>").followRecursive(friend).all() 256 func (p *pathObject) FollowRecursive(call goja.FunctionCall) goja.Value { 257 preds, maxDepth, tags, ok := toViaDepthData(exportArgs(call.Arguments)) 258 if !ok || len(preds) == 0 { 259 return throwErr(p.s.vm, errNoVia) 260 } else if len(preds) != 1 { 261 return throwErr(p.s.vm, fmt.Errorf("expected one predicate or path for recursive follow")) 262 } 263 np := p.clonePath() 264 np = np.FollowRecursive(preds[0], maxDepth, tags) 265 return p.newVal(np) 266 } 267 268 // And is an alias for Intersect. 269 func (p *pathObject) And(path *pathObject) *pathObject { 270 return p.Intersect(path) 271 } 272 273 // Intersect filters all paths by the result of another query path. 274 // 275 // This is essentially a join where, at the stage of each path, a node is shared. 276 // Example: 277 // // javascript 278 // var cFollows = g.V("<charlie>").Out("<follows>") 279 // var dFollows = g.V("<dani>").Out("<follows>") 280 // // People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob. 281 // cFollows.Intersect(dFollows).All() 282 // // Equivalently, g.V("<charlie>").Out("<follows>").And(g.V("<dani>").Out("<follows>")).All() 283 func (p *pathObject) Intersect(path *pathObject) *pathObject { 284 if path == nil { 285 return p 286 } 287 np := p.clonePath().And(path.path) 288 return p.new(np) 289 } 290 291 // Union returns the combined paths of the two queries. 292 // 293 // Notice that it's per-path, not per-node. Once again, if multiple paths reach the same destination, 294 // they might have had different ways of getting there (and different tags). 295 // See also: `path.Tag()` 296 // 297 // Example: 298 // // javascript 299 // var cFollows = g.V("<charlie>").Out("<follows>") 300 // var dFollows = g.V("<dani>").Out("<follows>") 301 // // People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob (from charlie), dani, bob (from dani), and greg. 302 // cFollows.Union(dFollows).All() 303 func (p *pathObject) Union(path *pathObject) *pathObject { 304 if path == nil { 305 return p 306 } 307 np := p.clonePath().Or(path.path) 308 return p.new(np) 309 } 310 311 // Or is an alias for Union. 312 func (p *pathObject) Or(path *pathObject) *pathObject { 313 return p.Union(path) 314 } 315 316 // Back returns current path to a set of nodes on a given tag, preserving all constraints. 317 // 318 // If still valid, a path will now consider their vertex to be the same one as the previously tagged one, 319 // with the added constraint that it was valid all the way here. 320 // Useful for traversing back in queries and taking another route for things that have matched so far. 321 // 322 // Arguments: 323 // 324 // * `tag`: A previous tag in the query to jump back to. 325 // 326 // Example: 327 // // javascript 328 // // Start from all nodes, save them into start, follow any status links, 329 // // jump back to the starting node, and find who follows them. Return the result. 330 // // Results are: 331 // // {"id": "<alice>", "start": "<bob>"}, 332 // // {"id": "<charlie>", "start": "<bob>"}, 333 // // {"id": "<charlie>", "start": "<dani>"}, 334 // // {"id": "<dani>", "start": "<bob>"}, 335 // // {"id": "<dani>", "start": "<greg>"}, 336 // // {"id": "<dani>", "start": "<greg>"}, 337 // // {"id": "<fred>", "start": "<greg>"}, 338 // // {"id": "<fred>", "start": "<greg>"} 339 // g.V().tag("start").out("<status>").back("start").in("<follows>").all() 340 func (p *pathObject) Back(tag string) *pathObject { 341 np := p.clonePath().Back(tag) 342 return p.new(np) 343 } 344 345 // Tag saves a list of nodes to a given tag. 346 // 347 // In order to save your work or learn more about how a path got to the end, we have tags. 348 // The simplest thing to do is to add a tag anywhere you'd like to put each node in the result set. 349 // 350 // Arguments: 351 // 352 // * `tag`: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached "Tag" 353 // Example: 354 // // javascript 355 // // Start from all nodes, save them into start, follow any status links, and return the result. 356 // // Results are: 357 // // {"id": "cool_person", "start": "<bob>"}, 358 // // {"id": "cool_person", "start": "<dani>"}, 359 // // {"id": "cool_person", "start": "<greg>"}, 360 // // {"id": "smart_person", "start": "<emily>"}, 361 // // {"id": "smart_person", "start": "<greg>"} 362 // g.V().tag("start").out("<status>").All() 363 func (p *pathObject) Tag(tags ...string) *pathObject { 364 np := p.clonePath().Tag(tags...) 365 return p.new(np) 366 } 367 368 // As is an alias for Tag. 369 func (p *pathObject) As(tags ...string) *pathObject { 370 return p.Tag(tags...) 371 } 372 373 // Has filters all paths which are, at this point, on the subject for the given predicate and object, 374 // but do not follow the path, merely filter the possible paths. 375 // 376 // Usually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair. 377 // 378 // Signature: (predicate, object) 379 // 380 // Arguments: 381 // 382 // * `predicate`: A string for a predicate node. 383 // * `object`: A string for a object node or a set of filters to find it. 384 // 385 // Example: 386 // // javascript 387 // // Start from all nodes that follow bob -- results in alice, charlie and dani 388 // g.V().has("<follows>", "<bob>").all() 389 // // People charlie follows who then follow fred. Results in bob. 390 // g.V("<charlie>").Out("<follows>").has("<follows>", "<fred>").all() 391 // // People with friends who have names sorting lower then "f". 392 // g.V().has("<follows>", gt("<f>")).all() 393 func (p *pathObject) Has(call goja.FunctionCall) goja.Value { 394 return p.has(call, false) 395 } 396 397 // HasR is the same as Has, but sets constraint in reverse direction. 398 func (p *pathObject) HasR(call goja.FunctionCall) goja.Value { 399 return p.has(call, true) 400 } 401 func (p *pathObject) has(call goja.FunctionCall, rev bool) goja.Value { 402 args := exportArgs(call.Arguments) 403 if len(args) == 0 { 404 return throwErr(p.s.vm, errArgCount{Got: len(args)}) 405 } 406 via := args[0] 407 args = args[1:] 408 if vp, ok := via.(*pathObject); ok { 409 via = vp.path 410 } else { 411 var err error 412 via, err = toQuadValue(via) 413 if err != nil { 414 return throwErr(p.s.vm, err) 415 } 416 } 417 if len(args) > 0 { 418 var filt []shape.ValueFilter 419 loop: 420 for _, a := range args { 421 switch a := a.(type) { 422 case valFilter: 423 filt = append(filt, a.f) 424 case []valFilter: 425 for _, s := range a { 426 filt = append(filt, s.f) 427 } 428 default: 429 filt = nil 430 // failed to collect all argument as filters - switch back to nodes-only mode 431 break loop 432 } 433 } 434 if len(filt) > 0 { 435 np := p.clonePath() 436 np = np.HasFilter(via, rev, filt...) 437 return p.newVal(np) 438 } 439 } 440 qv, err := toQuadValues(args) 441 if err != nil { 442 return throwErr(p.s.vm, err) 443 } 444 np := p.clonePath() 445 if rev { 446 np = np.HasReverse(via, qv...) 447 } else { 448 np = np.Has(via, qv...) 449 } 450 return p.newVal(np) 451 } 452 func (p *pathObject) save(call goja.FunctionCall, rev, opt bool) goja.Value { 453 args := exportArgs(call.Arguments) 454 if len(args) > 2 || len(args) == 0 { 455 return throwErr(p.s.vm, errArgCount{Got: len(args)}) 456 } 457 var vtag interface{} = "" 458 if len(args) == 2 { 459 vtag = args[1] 460 } 461 tag, ok := vtag.(string) 462 if !ok { 463 return throwErr(p.s.vm, fmt.Errorf("expected string, got: %T", vtag)) 464 } 465 via := args[0] 466 if vp, ok := via.(*pathObject); ok { 467 via = vp.path 468 if tag == "" { 469 return throwErr(p.s.vm, errors.New("must specify a tag name when saving a path")) 470 } 471 } else { 472 qv, err := toQuadValue(via) 473 via = qv 474 if err != nil { 475 return throwErr(p.s.vm, err) 476 } 477 if tag == "" { 478 if p.s.col == query.JSONLD { 479 switch qv := qv.(type) { 480 case quad.IRI: 481 tag = string(qv) 482 case quad.String: 483 tag = string(qv) 484 default: 485 tag = quad.StringOf(qv) 486 } 487 } else { 488 tag = quad.StringOf(qv) 489 } 490 } 491 } 492 np := p.clonePath() 493 if opt { 494 if rev { 495 np = np.SaveOptionalReverse(via, tag) 496 } else { 497 np = np.SaveOptional(via, tag) 498 } 499 } else { 500 if rev { 501 np = np.SaveReverse(via, tag) 502 } else { 503 np = np.Save(via, tag) 504 } 505 } 506 return p.newVal(np) 507 } 508 509 // Save saves the object of all quads with predicate into tag, without traversal. 510 // Signature: (predicate, tag) 511 // 512 // Arguments: 513 // 514 // * `predicate`: A string for a predicate node. 515 // * `tag`: A string for a tag key to store the object node. 516 // 517 // Example: 518 // // javascript 519 // // Start from dani and bob and save who they follow into "target" 520 // // Returns: 521 // // {"id" : "<bob>", "target": "<fred>" }, 522 // // {"id" : "<dani>", "target": "<bob>" }, 523 // // {"id" : "<dani>", "target": "<greg>" } 524 // g.V("<dani>", "<bob>").Save("<follows>", "target").All() 525 func (p *pathObject) Save(call goja.FunctionCall) goja.Value { 526 return p.save(call, false, false) 527 } 528 529 // SaveR is the same as Save, but tags values via reverse predicate. 530 func (p *pathObject) SaveR(call goja.FunctionCall) goja.Value { 531 return p.save(call, true, false) 532 } 533 534 // SaveOpt is the same as Save, but returns empty tags if predicate does not exists. 535 func (p *pathObject) SaveOpt(call goja.FunctionCall) goja.Value { 536 return p.save(call, false, true) 537 } 538 539 // SaveOptR is the same as SaveOpt, but tags values via reverse predicate. 540 func (p *pathObject) SaveOptR(call goja.FunctionCall) goja.Value { 541 return p.save(call, true, true) 542 } 543 544 // Except removes all paths which match query from current path. 545 // 546 // In a set-theoretic sense, this is (A - B). While `g.V().Except(path)` to achieve `U - B = !B` is supported, it's often very slow. 547 // Example: 548 // // javascript 549 // var cFollows = g.V("<charlie>").Out("<follows>") 550 // var dFollows = g.V("<dani>").Out("<follows>") 551 // // People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob. 552 // cFollows.Except(dFollows).All() // The set (dani) -- what charlie follows that dani does not also follow. 553 // // Equivalently, g.V("<charlie>").Out("<follows>").Except(g.V("<dani>").Out("<follows>")).All() 554 func (p *pathObject) Except(path *pathObject) *pathObject { 555 if path == nil { 556 return p 557 } 558 np := p.clonePath().Except(path.path) 559 return p.new(np) 560 } 561 562 // Unique removes duplicate values from the path. 563 func (p *pathObject) Unique() *pathObject { 564 np := p.clonePath().Unique() 565 return p.new(np) 566 } 567 568 // Difference is an alias for Except. 569 func (p *pathObject) Difference(path *pathObject) *pathObject { 570 return p.Except(path) 571 } 572 573 // Labels gets the list of inbound and outbound quad labels 574 func (p *pathObject) Labels() *pathObject { 575 np := p.clonePath().Labels() 576 return p.new(np) 577 } 578 579 // InPredicates gets the list of predicates that are pointing in to a node. 580 // 581 // Example: 582 // // javascript 583 // // bob only has "<follows>" predicates pointing inward 584 // // returns "<follows>" 585 // g.V("<bob>").InPredicates().All() 586 func (p *pathObject) InPredicates() *pathObject { 587 np := p.clonePath().InPredicates() 588 return p.new(np) 589 } 590 591 // OutPredicates gets the list of predicates that are pointing out from a node. 592 // 593 // Example: 594 // // javascript 595 // // bob has "<follows>" and "<status>" edges pointing outwards 596 // // returns "<follows>", "<status>" 597 // g.V("<bob>").OutPredicates().All() 598 func (p *pathObject) OutPredicates() *pathObject { 599 np := p.clonePath().OutPredicates() 600 return p.new(np) 601 } 602 603 // SaveInPredicates tags the list of predicates that are pointing in to a node. 604 // 605 // Example: 606 // // javascript 607 // // bob only has "<follows>" predicates pointing inward 608 // // returns {"id":"<bob>", "pred":"<follows>"} 609 // g.V("<bob>").SaveInPredicates("pred").All() 610 func (p *pathObject) SaveInPredicates(tag string) *pathObject { 611 np := p.clonePath().SavePredicates(true, tag) 612 return p.new(np) 613 } 614 615 // SaveOutPredicates tags the list of predicates that are pointing out from a node. 616 // 617 // Example: 618 // // javascript 619 // // bob has "<follows>" and "<status>" edges pointing outwards 620 // // returns {"id":"<bob>", "pred":"<follows>"} 621 // g.V("<bob>").SaveInPredicates("pred").All() 622 func (p *pathObject) SaveOutPredicates(tag string) *pathObject { 623 np := p.clonePath().SavePredicates(false, tag) 624 return p.new(np) 625 } 626 627 // LabelContext sets (or removes) the subgraph context to consider in the following traversals. 628 // Affects all In(), Out(), and Both() calls that follow it. The default LabelContext is null (all subgraphs). 629 // Signature: ([labelPath], [tags]) 630 // 631 // Arguments: 632 // 633 // * `predicatePath` (Optional): One of: 634 // * null or undefined: In future traversals, consider all edges, regardless of subgraph. 635 // * a string: The name of the subgraph to restrict traversals to. 636 // * a list of strings: A set of subgraphs to restrict traversals to. 637 // * a query path object: The target of which is a set of subgraphs. 638 // * `tags` (Optional): One of: 639 // * null or undefined: No tags 640 // * a string: A single tag to add the last traversed label to the output set. 641 // * a list of strings: Multiple tags to use as keys to save the label used to the output set. 642 // 643 // Example: 644 // // javascript 645 // // Find the status of people Dani follows 646 // g.V("<dani>").out("<follows>").out("<status>").all() 647 // // Find only the statuses provided by the smart_graph 648 // g.V("<dani>").out("<follows>").labelContext("<smart_graph>").out("<status>").all() 649 // // Find all people followed by people with statuses in the smart_graph. 650 // g.V().labelContext("<smart_graph>").in("<status>").labelContext(null).in("<follows>").all() 651 func (p *pathObject) LabelContext(call goja.FunctionCall) goja.Value { 652 labels, tags, ok := toViaData(exportArgs(call.Arguments)) 653 if !ok { 654 return throwErr(p.s.vm, errNoVia) 655 } 656 np := p.clonePath().LabelContextWithTags(tags, labels...) 657 return p.newVal(np) 658 } 659 660 // Filter applies constraints to a set of nodes. Can be used to filter values by range or match strings. 661 func (p *pathObject) Filter(args ...valFilter) (*pathObject, error) { 662 if len(args) == 0 { 663 return nil, errArgCount{Got: len(args)} 664 } 665 filt := make([]shape.ValueFilter, 0, len(args)) 666 for _, f := range args { 667 if f.f == nil { 668 return nil, errors.New("invalid argument type in filter()") 669 } 670 filt = append(filt, f.f) 671 } 672 np := p.clonePath().Filters(filt...) 673 return p.new(np), nil 674 } 675 676 // Limit limits a number of nodes for current path. 677 // 678 // Arguments: 679 // 680 // * `limit`: A number of nodes to limit results to. 681 // 682 // Example: 683 // // javascript 684 // // Start from all nodes that follow bob, and limit them to 2 nodes -- results in alice and charlie 685 // g.V().has("<follows>", "<bob>").limit(2).all() 686 func (p *pathObject) Limit(limit int) *pathObject { 687 np := p.clonePath().Limit(int64(limit)) 688 return p.new(np) 689 } 690 691 // Skip skips a number of nodes for current path. 692 // 693 // Arguments: 694 // 695 // * `offset`: A number of nodes to skip. 696 // 697 // Example: 698 // // javascript 699 // // Start from all nodes that follow bob, and skip 2 nodes -- results in dani 700 // g.V().has("<follows>", "<bob>").skip(2).all() 701 func (p *pathObject) Skip(offset int) *pathObject { 702 np := p.clonePath().Skip(int64(offset)) 703 return p.new(np) 704 } 705 706 func (p *pathObject) Order() *pathObject { 707 np := p.clonePath().Order() 708 return p.new(np) 709 } 710 711 // Backwards compatibility 712 func (p *pathObject) CapitalizedIs(call goja.FunctionCall) goja.Value { 713 return p.Is(call) 714 } 715 func (p *pathObject) CapitalizedIn(call goja.FunctionCall) goja.Value { 716 return p.In(call) 717 } 718 func (p *pathObject) CapitalizedOut(call goja.FunctionCall) goja.Value { 719 return p.Out(call) 720 } 721 func (p *pathObject) CapitalizedBoth(call goja.FunctionCall) goja.Value { 722 return p.Both(call) 723 } 724 func (p *pathObject) CapitalizedFollow(path *pathObject) *pathObject { 725 return p.Follow(path) 726 } 727 func (p *pathObject) CapitalizedFollowR(path *pathObject) *pathObject { 728 return p.FollowR(path) 729 } 730 func (p *pathObject) CapitalizedFollowRecursive(call goja.FunctionCall) goja.Value { 731 return p.FollowRecursive(call) 732 } 733 func (p *pathObject) CapitalizedAnd(path *pathObject) *pathObject { 734 return p.And(path) 735 } 736 func (p *pathObject) CapitalizedIntersect(path *pathObject) *pathObject { 737 return p.Intersect(path) 738 } 739 func (p *pathObject) CapitalizedUnion(path *pathObject) *pathObject { 740 return p.Union(path) 741 } 742 func (p *pathObject) CapitalizedOr(path *pathObject) *pathObject { 743 return p.Or(path) 744 } 745 func (p *pathObject) CapitalizedBack(tag string) *pathObject { 746 return p.Back(tag) 747 } 748 func (p *pathObject) CapitalizedTag(tags ...string) *pathObject { 749 return p.Tag(tags...) 750 } 751 func (p *pathObject) CapitalizedAs(tags ...string) *pathObject { 752 return p.As(tags...) 753 } 754 func (p *pathObject) CapitalizedHas(call goja.FunctionCall) goja.Value { 755 return p.Has(call) 756 } 757 func (p *pathObject) CapitalizedHasR(call goja.FunctionCall) goja.Value { 758 return p.HasR(call) 759 } 760 func (p *pathObject) CapitalizedSave(call goja.FunctionCall) goja.Value { 761 return p.Save(call) 762 } 763 func (p *pathObject) CapitalizedSaveR(call goja.FunctionCall) goja.Value { 764 return p.SaveR(call) 765 } 766 func (p *pathObject) CapitalizedSaveOpt(call goja.FunctionCall) goja.Value { 767 return p.SaveOpt(call) 768 } 769 func (p *pathObject) CapitalizedSaveOptR(call goja.FunctionCall) goja.Value { 770 return p.SaveOptR(call) 771 } 772 func (p *pathObject) CapitalizedExcept(path *pathObject) *pathObject { 773 return p.Except(path) 774 } 775 func (p *pathObject) CapitalizedUnique() *pathObject { 776 return p.Unique() 777 } 778 func (p *pathObject) CapitalizedDifference(path *pathObject) *pathObject { 779 return p.Difference(path) 780 } 781 func (p *pathObject) CapitalizedLabels() *pathObject { 782 return p.Labels() 783 } 784 func (p *pathObject) CapitalizedInPredicates() *pathObject { 785 return p.InPredicates() 786 } 787 func (p *pathObject) CapitalizedOutPredicates() *pathObject { 788 return p.OutPredicates() 789 } 790 func (p *pathObject) CapitalizedSaveInPredicates(tag string) *pathObject { 791 return p.SaveInPredicates(tag) 792 } 793 func (p *pathObject) CapitalizedSaveOutPredicates(tag string) *pathObject { 794 return p.SaveOutPredicates(tag) 795 } 796 func (p *pathObject) CapitalizedLabelContext(call goja.FunctionCall) goja.Value { 797 return p.LabelContext(call) 798 } 799 func (p *pathObject) CapitalizedFilter(args ...valFilter) (*pathObject, error) { 800 return p.Filter(args...) 801 } 802 func (p *pathObject) CapitalizedLimit(limit int) *pathObject { 803 return p.Limit(limit) 804 } 805 func (p *pathObject) CapitalizedSkip(offset int) *pathObject { 806 return p.Skip(offset) 807 }