1
0
Fork 0

Started documentation of Traversals. Expander example not yet working

This commit is contained in:
Michael Hackstein 2013-06-12 16:06:47 +02:00
parent 87b31a54c1
commit ed32dd0a9b
2 changed files with 543 additions and 46 deletions

View File

@ -7,22 +7,17 @@ HTTP Interface for Traversals {#HttpTraversals}
Traverals {#HttpTraversalsIntro}
================================
TODO: FIXME
ArangoDB's transactions are executed on the server. Transactions can be
initiated by clients by sending the transaction description for execution to
ArangoDB's graph traversals are executed on the server. Traversals can be
initiated by clients by sending the traversal description for execution to
the server.
Transactions in ArangoDB do not offer seperate `BEGIN`, `COMMIT` and `ROLLBACK`
operations as they are available in many other database products.
Instead, ArangoDB transactions are described by a Javascript function, and the
code inside the Javascript function will then be executed transactionally.
At the end of the function, the transaction is automatically committed, and all
changes done by the transaction will be persisted. If an exception is thrown
during transaction execution, all operations performed in the transaction are
rolled back.
For a more detailed description of how transactions work in ArangoDB please
refer to @ref Transactions.
Traversals in ArangoDB are used to walk over a graph
stored in one edge collection. It can easily be described
which edges of the graph should be followed and which actions
should be performed on each visited vertex.
Furthermore the ordering of visiting the nodes can be
specified, for instance depth-first or breadth-first search
are offered.
Executing Traversals via HTTP {#HttpTraversalsHttp}
=======================================================

View File

@ -87,49 +87,551 @@ function validateArg (value, map) {
///
/// @RESTBODYPARAM{body,string,required}
///
/// TODO:
/// startVertex: id of the startVertex, e.g. "users/foo", mandatory
/// edgeCollection: name of the collection that contains the edges, mandatory
/// filter: body (JavaScript code) of custom filter function, optional
/// @RESTDESCRIPTION
/// Starts a traversal starting from a given vertex and following.
/// edges contained in a given edgeCollection. The request must
/// contain the following attributes.
///
/// - `startVertex`: id of the startVertex, e.g. `"users/foo"`.
///
/// - `edgeCollection`: name of the collection that contains the edges.
///
/// - `filter` (optional, default is to include all nodes):
/// body (JavaScript code) of custom filter function
/// function signature: (config, vertex, path) -> mixed
/// can return either undefined (=include), 'exclude', 'prune' or an array of these
/// if no filter is specified, all nodes will be included
/// minDepth: optional minDepth filter value (will be ANDed with any existing filters)
/// maxDepth: optional maxDepth filter value (will be ANDed with any existing filters)
/// visitor: body (JavaScript) code of custom visitor function, optional
/// 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.
/// - `minDepth` (optional, ANDed with any existing filters): visits only nodes in at least the given depth
/// - `maxDepth` (optional, ANDed with any existing filters): visits only nodes in at most the given depth
/// - `visitor` (optional): body (JavaScript) code of custom visitor function
/// function signature: (config, result, vertex, path) -> void
/// visitor function can do anything, but its return value is ignored. To
/// populate a result, use the "result" variable by reference
/// example: "result.visited++; result.myVertices.push(vertex);"
/// direction: direction for traversal, optional
/// if set, must be either "outbound", "inbound", or "any"
/// if not set, the "expander" attribute must be specified
/// init: body (JavaScript) code of custom result initialisation function, optional
/// populate a result, use the `result` variable by reference
/// - `direction` (optional): direction for traversal
/// - *if set*, must be either `"outbound"`, `"inbound"`, or `"any"`
/// - *if not set*, the `expander` attribute must be specified
/// - `init` (optional): body (JavaScript) code of custom result initialisation function
/// function signature: (config, result) -> void
/// initialise any values in result with what is required
/// example: "result.visited = 0; result.myVertices = [ ];"
/// expander: body (JavaScript) code of custom expander function, optional
/// must be set if "direction" attribute is not set
/// - `expander` (optional): 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"
/// example: "expander": "var connections = [ ]; config.edgeCollection.outEdges(vertex).forEach(function (e) { connections.push({ vertex: e._to, edge: e }); }); return connections;"
/// strategy: traversal strategy, optional
/// can be "depthfirst" or "breadthfirst"
/// order: traversal order, optional
/// can be "preorder" or "postorder"
/// itemOrder: item iteration order, optional
/// can be "forward" or "backward"
/// uniqueness: specifies uniqueness for vertices and edges visited, optional
/// if set, must be an object like this: "uniqueness": { "vertices": "none"|"global"|path", "edges": "none"|"global"|"path" }
/// expander must return an array of the connections for `vertex`
/// each connection is an object with the attributes `edge` and `vertex`
/// - `strategy` (optional): traversal strategy
/// can be `"depthfirst"` or `"breadthfirst"`
/// - `order` (optional): traversal order
/// can be `"preorder"` or `"postorder"`
/// - `itemOrder` (optional): item iteration order
/// can be `"forward"` or `"backward"`
/// - `uniqueness` (optional): specifies uniqueness for vertices and edges visited
/// if set, must be an object like this:
/// `"uniqueness": {"vertices": "none"|"global"|path", "edges": "none"|"global"|"path"}`
///
/// the "result" object will be returned by the traversal
///
/// 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
///
/// example:
/// POST {"edgeCollection": "e", "expander": "var connections = [ ]; config.edgeCollection.outEdges(vertex).forEach(function (e) { connections.push({ vertex: e._to, edge: e }); }); return connections;", "startVertex" : "v/jan", "filter": "return undefined;", "minDepth": 0 }
///
/// @RESTDESCRIPTION
/// TODO
/// @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`.
///
/// @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 cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "outbound"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Follow only inbound edges:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalInbound}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "inbound"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Follow any direction of edges:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalAny}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "any"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Excluding `Charlie` and `Bob`:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalFilterExclude}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "outbound", ';
/// body += '"filter" : "if (vertex.name === \\"Bob\\" || ';
/// body += 'vertex.name === \\"Charlie\\") {';
/// body += 'return \\"exclude\\";'
/// body += '}'
/// body += 'return;"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Do not follow edges from `Bob`:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalFilterPrune}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "outbound", ';
/// body += '"filter" : "if (vertex.name === \\"Bob\\") {';
/// body += 'return \\"prune\\";'
/// body += '}'
/// body += 'return;"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Visit only nodes in a depth of at least 2:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalMinDepth}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "outbound", ';
/// body += '"minDepth" : 2}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Visit only nodes in a depth of at most 1:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalMaxDepth}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "outbound", ';
/// body += '"maxDepth" : 1}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Count all visited nodes and return a list of nodes only:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalVisitorCountAndList}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "outbound", ';
/// body += '"init" : "result.visited = 0; result.myVertices = [ ];", ';
/// body += '"visitor" : "result.visited++; result.myVertices.push(vertex);"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Expand only inbound edges of `Alice` and outbound edges of `Eve`:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalVisitorExpander}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"expander" : ';
/// body += '"var connections = [ ];';
/// body += 'if (vertex.name === \\"Alice\\") {';
/// body += 'config.edgeCollection.inEdges(vertex).forEach(function (e) {';
/// body += 'connections.push({ vertex: db.document(e._from), edge: e });';
/// body += '});';
/// body += '}';
/// body += 'if (vertex.name === \\"Eve\\") {';
/// body += 'config.edgeCollection.outEdges(vertex).forEach(function (e) {';
/// body += 'connections.push({ vertex: db.document(e._to), edge: e });';
/// body += '});';
/// body += '}';
/// body += 'return connections;"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Follow the `depthfirst` strategy:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalDepthFirst}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "any", ';
/// body += '"strategy" : "depthfirst"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Using `postorder` ordering:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalPostorder}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "any", ';
/// body += '"order" : "postorder"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
///
/// Using `backward` item-ordering:
///
/// @EXAMPLE_ARANGOSH_RUN{RestTraversalBackwardItemOrder}
/// var cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "any", ';
/// body += '"itemOrder" : "backward"}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @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 cv = "persons";
/// var ce = "knows";
/// db._drop(cv);
/// db._drop(ce);
/// var users = db._create(cv);
/// var knows = db._createEdgeCollection(ce);
/// var a = users.save({name: "Alice"})._id;
/// var b = users.save({name: "Bob"})._id;
/// var c = users.save({name: "Charlie"})._id;
/// var d = users.save({name: "Dave"})._id;
/// var e = users.save({name: "Eve"})._id;
/// knows.save(a, b, {});
/// knows.save(b, c, {});
/// knows.save(b, d, {});
/// knows.save(e, a, {});
/// knows.save(e, b, {});
/// var url = "/_api/traversal";
/// var body = '{ "startVertex": "' + a + '", ';
/// body += '"edgeCollection" : "' + knows.name() + '", ';
/// body += '"direction" : "any", ';
/// body += '"uniqueness" : {"vertices": "none", "edges": "global"}}';
///
/// var response = logCurlRequest('POST', url, body);
/// assert(response.code === 200);
///
/// logJsonResponse(response);
/// db._drop(cv);
/// db._drop(ce);
/// @END_EXAMPLE_ARANGOSH_RUN
////////////////////////////////////////////////////////////////////////////////
function post_api_traversal(req, res) {