//////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_HTTP_API_TRAVERSAL /// @brief execute a server-side traversal /// /// @RESTHEADER{POST /_api/traversal,executes a traversal} /// /// @RESTDESCRIPTION /// Starts a traversal starting from a given vertex and following. /// edges contained in a given edgeCollection. The request must /// contain the following attributes. /// /// @RESTBODYPARAM{startVertex,string,required,string} /// id of the startVertex, e.g. *"users/foo"*. /// /// @RESTBODYPARAM{edgeCollection,string,optional, string} /// name of the collection that contains the edges. /// /// @RESTBODYPARAM{graphName,string,optional,string} /// name of the graph that contains the edges. /// Either *edgeCollection* or *graphName* has to be given. /// In case both values are set the *graphName* is prefered. /// /// @RESTBODYPARAM{filter,string,optional,string} /// default is to include all nodes: /// body (JavaScript code) of custom filter function /// function signature: *(config, vertex, path) -> mixed* /// can return four different string values: /// - *"exclude"* -> this vertex will not be visited. /// - *"prune"* -> the edges of this vertex will not be followed. /// - *""* or *undefined* -> visit the vertex and follow it's edges. /// - *Array* -> containing any combination of the above. /// If there is at least one *"exclude"* or *"prune"* respectivly /// is contained, it's effect will occur. /// /// @RESTBODYPARAM{minDepth,string,optional,string} /// ANDed with any existing filters): /// visits only nodes in at least the given depth /// /// @RESTBODYPARAM{maxDepth,string,optional,string} /// ANDed with any existing filters visits only nodes in at most the given depth /// /// @RESTBODYPARAM{visitor,string,optional,string} /// body (JavaScript) code of custom visitor function /// function signature: *(config, result, vertex, path, connected) -> void* /// The visitor function can do anything, but its return value is ignored. To /// populate a result, use the *result* variable by reference. Note that the /// *connected* argument is only populated when the *order* attribute is set /// to *"preorder-expander"*. /// /// @RESTBODYPARAM{direction,string,optional,string} /// direction for traversal /// - *if set*, must be either *"outbound"*, *"inbound"*, or *"any"* /// - *if not set*, the *expander* attribute must be specified /// /// @RESTBODYPARAM{init,string,optional,string} /// body (JavaScript) code of custom result initialization function /// function signature: *(config, result) -> void* /// initialize any values in result with what is required /// /// @RESTBODYPARAM{expander,string,optional,string} /// body (JavaScript) code of custom expander function /// *must* be set if *direction* attribute is **not** set /// function signature: *(config, vertex, path) -> array* /// expander must return an array of the connections for *vertex* /// each connection is an object with the attributes *edge* and *vertex* /// /// @RESTBODYPARAM{sort,string,optional,string} /// body (JavaScript) code of a custom comparison function /// for the edges. The signature of this function is /// *(l, r) -> integer* (where l and r are edges) and must /// return -1 if l is smaller than, +1 if l is greater than, /// and 0 if l and r are equal. The reason for this is the /// following: The order of edges returned for a certain /// vertex is undefined. This is because there is no natural /// order of edges for a vertex with multiple connected edges. /// To explicitly define the order in which edges on the /// vertex are followed, you can specify an edge comparator /// function with this attribute. Note that the value here has /// to be a string to conform to the JSON standard, which in /// turn is parsed as function body on the server side. Furthermore /// note that this attribute is only used for the standard /// expanders. If you use your custom expander you have to /// do the sorting yourself within the expander code. /// /// @RESTBODYPARAM{strategy,string,optional,string} /// traversal strategy can be *"depthfirst"* or *"breadthfirst"* /// /// @RESTBODYPARAM{order,string,optional,string} /// traversal order can be *"preorder"*, *"postorder"* or *"preorder-expander"* /// /// @RESTBODYPARAM{itemOrder,string,optional,string} /// item iteration order can be *"forward"* or *"backward"* /// /// @RESTBODYPARAM{uniqueness,string,optional,string} /// specifies uniqueness for vertices and edges visited /// if set, must be an object like this: /// /// *"uniqueness": {"vertices": "none"|"global"|"path", "edges": "none"|"global"|"path"}* /// /// @RESTBODYPARAM{maxIterations,string,optional,string} /// Maximum number of iterations in each traversal. This number can be /// set to prevent endless loops in traversal of cyclic graphs. When a traversal performs /// as many iterations as the *maxIterations* value, the traversal will abort with an /// error. If *maxIterations* is not set, a server-defined value may be used. /// /// @RESTDESCRIPTION /// /// If the Traversal is successfully executed *HTTP 200* will be returned. /// Additionally the *result* object will be returned by the traversal. /// /// For successful traversals, the returned JSON object has the /// following properties: /// /// - *error*: boolean flag to indicate if an error occurred (*false* /// in this case) /// /// - *code*: the HTTP status code /// /// - *result*: the return value of the traversal /// /// If the traversal specification is either missing or malformed, the server /// will respond with *HTTP 400*. /// /// The body of the response will then contain a JSON object with additional error /// details. The object has the following attributes: /// /// - *error*: boolean flag to indicate that an error occurred (*true* in this case) /// /// - *code*: the HTTP status code /// /// - *errorNum*: the server error number /// /// - *errorMessage*: a descriptive error message /// /// @RESTRETURNCODES /// /// @RESTRETURNCODE{200} /// If the traversal is fully executed /// *HTTP 200* will be returned. /// /// @RESTRETURNCODE{400} /// If the traversal specification is either missing or malformed, the server /// will respond with *HTTP 400*. /// /// @RESTRETURNCODE{404} /// The server will responded with *HTTP 404* if the specified edge collection /// does not exist, or the specified start vertex cannot be found. /// /// @RESTRETURNCODE{500} /// The server will responded with *HTTP 500* when an error occurs inside the /// traversal or if a traversal performs more than *maxIterations* iterations. /// /// @EXAMPLES /// /// In the following examples the underlying graph will contain five persons /// *Alice*, *Bob*, *Charlie*, *Dave* and *Eve*. /// We will have the following directed relations: /// - *Alice* knows *Bob* /// - *Bob* knows *Charlie* /// - *Bob* knows *Dave* /// - *Eve* knows *Alice* /// - *Eve* knows *Bob* /// /// The starting vertex will always be Alice. /// /// Follow only outbound edges /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalOutbound} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// "startVertex": a , /// "graphName" : g.__name, /// "direction" : "outbound"}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Follow only inbound edges /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalInbound} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { "startVertex": a, /// "graphName" : g.__name, /// "direction" : "inbound"}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Follow any direction of edges /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalAny} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// direction: "any", /// uniqueness: { /// vertices: "none", /// edges: "global" /// } /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Excluding *Charlie* and *Bob* /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalFilterExclude} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var filter = 'if (vertex.name === "Bob" || '+ /// ' vertex.name === "Charlie") {'+ /// ' return "exclude";'+ /// '}'+ /// 'return;'; /// /// var body = { /// "startVertex" : a, /// "graphName" : g.__name, /// "direction" : "outbound", /// "filter" : filter}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Do not follow edges from *Bob* /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalFilterPrune} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var filter = 'if (vertex.name === "Bob") {'+ /// 'return "prune";' + /// '}' + /// 'return;'; /// /// var body = { "startVertex": a, /// "graphName" : g.__name, /// "direction" : "outbound", /// "filter" : filter}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Visit only nodes in a depth of at least 2 /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalMinDepth} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { "startVertex": a, /// "graphName" : g.__name, /// "direction" : "outbound", /// "minDepth" : 2}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Visit only nodes in a depth of at most 1 /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalMaxDepth} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { "startVertex" : a, /// "graphName" : g.__name, /// "direction" : "outbound", /// "maxDepth" : 1}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Using a visitor function to return vertex ids only /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalVisitorFunc} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { "startVertex": a, /// "graphName" : g.__name, /// "direction" : "outbound", /// "visitor" : "result.visited.vertices.push(vertex._id);"}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Count all visited nodes and return a list of nodes only /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalVisitorCountAndList} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { "startVertex": a, /// "graphName" : g.__name, /// "direction" : "outbound", /// "init" : "result.visited = 0; result.myVertices = [ ];", /// "visitor" : "result.visited++; result.myVertices.push(vertex);"}; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Expand only inbound edges of *Alice* and outbound edges of *Eve* /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalVisitorExpander} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// expander: "var connections = [ ];" + /// "if (vertex.name === \"Alice\") {" + /// "config.datasource.getInEdges(vertex).forEach(function (e) {" + /// "connections.push({ " + /// "vertex: require(\"internal\").db._document(e._from), " + /// "edge: e" + /// "});" + /// "});" + /// "}" + /// "if (vertex.name === \"Eve\") {" + /// "config.datasource.getOutEdges(vertex).forEach(function (e) {" + /// "connections.push({" + /// "vertex: require(\"internal\").db._document(e._to), " + /// "edge: e" + /// "});" + /// "});" + /// "}" + /// "return connections;" /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Follow the *depthfirst* strategy /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalDepthFirst} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// direction: "any", /// strategy: "depthfirst" /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Using *postorder* ordering /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalPostorder} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// direction: "any", /// order: "postorder" /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Using *backward* item-ordering: /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalBackwardItemOrder} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// direction: "any", /// itemOrder: "backward" /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// Edges should only be included once globally, /// but nodes are included every time they are visited /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalEdgeUniqueness} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// direction: "any", /// uniqueness: { /// vertices: "none", /// edges: "global" /// } /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 200); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// If the underlying graph is cyclic, *maxIterations* should be set /// /// The underlying graph has two vertices *Alice* and *Bob*. /// With the directed edges: /// - *Alice* knows *Bob* /// _ *Bob* knows *Alice* /// /// /// @EXAMPLE_ARANGOSH_RUN{RestTraversalMaxIterations} /// var examples = require("@arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("knows_graph"); /// var a = g.persons.document("alice")._id; /// var b = g.persons.document("bob")._id; /// g.knows.truncate(); /// g.knows.save(a, b, {}); /// g.knows.save(b, a, {}); /// var url = "/_api/traversal"; /// var body = { /// startVertex: a, /// graphName: g.__name, /// direction: "any", /// uniqueness: { /// vertices: "none", /// edges: "none" /// }, /// maxIterations: 5 /// }; /// /// var response = logCurlRequest('POST', url, body); /// assert(response.code === 500); /// /// logJsonResponse(response); /// examples.dropGraph("knows_graph"); /// @END_EXAMPLE_ARANGOSH_RUN /// /// @endDocuBlock ////////////////////////////////////////////////////////////////////////////////