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  }