SQL - TRAVERSE
Retrieves connected records crossing relationships. This works with both the Document and Graph API’s, meaning that you can traverse relationships between say invoices and customers on a graph, without the need to model the domain using the Graph API.
In many cases, you may find it more efficient to use SELECT, which can result in shorter and faster queries. For more information, see TRAVERSE versus SELECT below.
|
Syntax
TRAVERSE <projection> [, <projection>]* (a field, or a function such as out(), in() or both())
FROM <target>
[MAXDEPTH <number>]
[WHILE <condition>]
[LIMIT <max-records>]
[STRATEGY DEPTH_FIRST|BREADTH_FIRST]
-
<fields>Defines the fields you want to traverse. -
<target>Defines the target you want to traverse. This can be a type, one or more buckets, a single Record ID, set of Record ID’s, or a sub-query. -
MAXDEPTHDefines the maximum depth of the traversal.0indicates that you only want to traverse the root node. Negative values are invalid. The result is every record reachable within that many hops of a root, whichever strategy is used. -
WHILEDefines the condition for continuing the traversal while it is true. It is evaluated on every record before it is traversed, the roots included: a root that does not satisfy it is neither returned nor traversed.MAXDEPTHandWHILEcan be used together. -
LIMITDefines the maximum number of results the command can return. -
STRATEGYDefines strategy for traversing the graph. It also decides what$depthand$pathmean on a graph where a record can be reached by more than one path, see Traversal Strategies.
TRAVERSE has no WHERE clause. To filter the traversed records, wrap the TRAVERSE in a SELECT, as in the examples below.
|
There is a difference between MAXDEPTH N and WHILE $depth <= N: the MAXDEPTH will evaluate exactly N levels, while the WHILE will evaluate N+1 levels and will discard the N+1th, so the MAXDEPTH in general has better performance. Both return the same records.
|
Examples
In a social network-like domain, a user profile is connected to friend through links. The following examples consider common operations on a user with the record ID #10:1234.
-
Traverse the outgoing edges of the root record (
*is not accepted afterTRAVERSE; useout(),in()orboth()):
ArcadeDB> TRAVERSE out() FROM #10:1234
-
Specify fields and depth up to the third level, using the
BREADTH_FIRSTstrategy:
ArcadeDB> TRAVERSE out("Friend") FROM #10:1234 MAXDEPTH 3
STRATEGY BREADTH_FIRST
-
Execute the same command, this time filtering for a minimum depth to exclude the first target vertex:
ArcadeDB> SELECT FROM (TRAVERSE out("Friend") FROM #10:1234 MAXDEPTH 3)
WHERE $depth >= 1
Bound the depth with MAXDEPTH or WHILE in the inner TRAVERSE rather than with $depth in the outer SELECT. It is much more efficient, because the traversal stops there instead of walking the whole graph. It is also correct with both strategies, while an outer WHERE $depth <= N returns every record within N hops only with STRATEGY BREADTH_FIRST: with the default DEPTH_FIRST, a record first reached through a longer path carries that longer $depth and is filtered out, see Traversal Strategies. Filtering out only the root, as above, is safe with both: the roots are the only records at depth 0.
|
-
Combine traversal with
SELECTcommand to filter the result-set. Repeat the above example, filtering for users in Rome:
ArcadeDB> SELECT FROM (TRAVERSE out("Friend") FROM #10:1234 MAXDEPTH 3)
WHERE city = 'Rome'
-
Extract movies of actors that have worked, at least once, in any movie produced by J.J. Abrams:
ArcadeDB> SELECT FROM (TRAVERSE out("Actors"), out("Movies") FROM (SELECT FROM
Movie WHERE producer = "J.J. Abrams") MAXDEPTH 3) WHERE
@type = 'Movie'
-
Display the current path in the traversal:
ArcadeDB> SELECT $path FROM ( TRAVERSE out() FROM V MAXDEPTH 10 )
Supported Variables
Fields
Defines the fields that you want to traverse. If set to *, then it traverses all fields. This can prove costly to performance and resource usage, so it is recommended that you optimize the command to only traverse the pertinent fields.
In addition to his, you can specify the fields at a type-level. Inheritance is supported. By specifying Person.city and the type Customer extends person, you also traverse fields in Customer.
Field names are case-sensitive, types not.
Target
Targets for traversal can be:
-
<type>Defines the type that you want to traverse. -
BUCKET:<bucket>Defines the bucket you want to traverse. -
<record-id>Individual root Record ID that you want to traverse. -
[<record-id>,<record-id>,…]Set of Record ID’s that you want to traverse. This is useful when navigating graphs starting from the same root nodes.
Context Variables
In addition to the above, you can use the following context variables in traversals:
-
$parentGives the parent context, if any. You may find this useful when traversing from a sub-query. -
$currentGives the current record in the iteration. To get the upper-level record in nested queries, you can use$parent.$current. -
$depthGives the number of hops from the root to the current record, along$path. The root is at depth0. -
$pathGives the path the traversal followed to reach the current record, as the list of its Record IDs from the root to the record itself (for instance[#8:0, #8:1, #8:3]). It always has$depth + 1elements. You can also display it throughSELECT:
ArcadeDB> SELECT $path, $depth FROM (TRAVERSE out() FROM #10:1234)
-
$stackGives the same Record IDs as$path, in reverse order: the current record first, the root last.
With STRATEGY BREADTH_FIRST, $depth is the shortest distance from the root. With the default DEPTH_FIRST it can be longer, see Traversal Strategies.
Use Cases
TRAVERSE versus SELECT
When you already know traversal information, such as relationship names and depth-level, consider using SELECT instead of TRAVERSE as it is faster in some cases.
For example, this query traverses the follow relationship from the Twitter account #10:1234, getting the accounts at the second level of friendship, that is the accounts it does not follow directly but that the accounts it follows do:
ArcadeDB> SELECT FROM (TRAVERSE out('follow') FROM #10:1234 MAXDEPTH 2
STRATEGY BREADTH_FIRST) WHERE $depth = 2
STRATEGY BREADTH_FIRST is what makes $depth = 2 mean "two hops away and not closer", see Traversal Strategies. Note that the traversal starts from one account: each record is returned once per TRAVERSE, so starting from every account (FROM TwitterAccounts) makes them all roots, an account is typically returned at depth 0, and $depth = 2 finds next to nothing.
If you only need the accounts reachable in exactly two hops, you can express it with a SELECT, in a way that is also shorter and faster:
ArcadeDB> SELECT expand(out('follow').out('follow')) FROM #10:1234
The two are not the same query: the SELECT also returns an account the root follows directly when it is followed by another account the root follows, and returns an account once for every path that reaches it.
TRAVERSE with the Graph Model and API
While you can use the TRAVERSE command with any domain model, it provides the greatest utility with the Graph Model.
This model is based on the concepts of the Vertex (or Node) and the Edge (or Arc, Connection, Link, etc.) If you want to traverse in a direction, you have to use the type name when declaring the traversing fields. The supported directions are:
-
Vertex to outgoing edges Using
outE()oroutE('EdgeTypeName'). That is, going out from a vertex and into the outgoing edges. -
Vertex to incoming edges Using
inE()orinE('EdgeTypeName'). That is, going from a vertex and into the incoming edges. -
Vertex to all edges Using
bothE()orbothE('EdgeTypeName'). That is, going from a vertex and into all the connected edges. -
Edge to Vertex (end point) Using
inV(). That is, going out from an edge and into a vertex. -
Edge to Vertex (starting point) Using
outV(). That is, going back from an edge and into a vertex. -
Edge to Vertex (both sizes) Using
bothV(). That is, going from an edge and into connected vertices. -
Vertex to Vertex (outgoing edges) Using
out()orout('EdgeTypeName'). This is the same asoutE().inV() -
Vertex to Vertex (incoming edges) Using
in()orin('EdgeTypeName'). This is the same asinE().outV() -
Vertex to Vertex (all directions) Using
both()orboth('EdgeTypeName').
For instance, traversing outgoing edges on the record #10:3434:
ArcadeDB> TRAVERSE out() FROM #10:3434
In a domain for emails, to find all messages sent on January 1, 2012 from the user Luca, assuming that they are stored in the vertex type User and that the messages are contained in the vertex type Message. Sent messages are stored as out connections on the edge type SentMessage:
ArcadeDB> SELECT FROM (TRAVERSE outE(), inV() FROM (SELECT FROM User WHERE
name = 'Luca') WHILE $depth <= 2 AND (@type = 'User' OR
@type = 'Message' OR (@type = 'SentMessage' AND
sentOn = '01/01/2012'))) WHERE @type = 'Message'
The WHILE condition accepts User too because it is also evaluated on the root: without it, the traversal would stop before leaving the user.
Traversal Strategies
When ArcadeDB traverses a graph it can use one of two available approaches, either explore every branch to its leaf before backtracking (depth-first), or visiting each child before progressing down the next level (breadth-first), see:
By default, the depth-first strategy is used.
A TRAVERSE returns each record at most once, even when several roots or several paths reach it. Without a LIMIT, both strategies return the same records, and MAXDEPTH N (or WHILE $depth <= N) returns every record within N hops with either of them. What differs is the order of the results and, on a graph where a record can be reached by paths of different lengths, the $depth and $path a record is returned with:
| Strategy | Order of the results | $depth and $path of a record |
|---|---|---|
|
A branch is explored to its end before the next one |
The path the walk took. It can be longer than the shortest one |
|
All the records at depth N before any record at depth N+1 |
A shortest path. |
For example, if A reaches X through A → B → X and also through A → C → D → E → X, a depth-first traversal that explores the C branch first returns X with $depth 4 and the longer path, while a breadth-first one always returns it with $depth 2. On a tree, where every record has only one path from the root, the two strategies return the same $depth and $path.
|
When the meaning of |