diff --git a/CHANGELOG b/CHANGELOG index 25a1e0191f..f83650bafe 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ v2.2.0 (XXXX-XX-XX) ------------------- +* fixed check-version for empty directory + * moved try/catch block to the top of routing chain * added mountedApp function for foxx-manager diff --git a/Documentation/Books/Users/Aql/GraphOperations.mdpp b/Documentation/Books/Users/Aql/GraphOperations.mdpp index a08dd9598c..41b1b70920 100644 --- a/Documentation/Books/Users/Aql/GraphOperations.mdpp +++ b/Documentation/Books/Users/Aql/GraphOperations.mdpp @@ -7,25 +7,2878 @@ This chapter describe graph related aql functions. +
+`GRAPH_PATHS (graphName, direction, followCycles, minLength, maxLength)` +*The GRAPH\_PATHS function returns all paths of a graph.* +
+This function determines all available paths in a graph identified by *graphName*. +Except for *graphName* every other parameter is optional. +
+* String *graphName* : The name of the graph. +* String *direction* : The direction of the edges. +Possible values are *any*, *inbound* and *outbound* (default). +* Boolean *followCycles* : If set to *true* the query follows cycles in the graph, +default is false. +* Number *minLength* : Defines the minimal length a path must +have to be returned (default is 0). +* Number *maxLength* : Defines the maximal length a path must +have to be returned (default is 10). +
+@EXAMPLES +
+Return all paths of the graph "social": +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> db._query("RETURN GRAPH_PATHS('social')").toArray(); +[ + [ + { + "vertices" : [ + { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + } + ], + "edges" : [ ], + "source" : { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + "destination" : { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + } + }, + { + "vertices" : [ + { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + } + ], + "edges" : [ + { + "_id" : "relation/aliceAndBob", + "_rev" : "197898824", + "_key" : "aliceAndBob", + "_from" : "female/alice", + "_to" : "male/bob", + "type" : "married" + } + ], + "source" : { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + "destination" : { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + } + }, + { + "vertices" : [ + { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + }, + { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + ], + "edges" : [ + { + "_id" : "relation/aliceAndBob", + "_rev" : "197898824", + "_key" : "aliceAndBob", + "_from" : "female/alice", + "_to" : "male/bob", + "type" : "married" + }, + { + "_id" : "relation/bobAndDiana", + "_rev" : "198554184", + "_key" : "bobAndDiana", + "_from" : "male/bob", + "_to" : "female/diana", + "type" : "friend" + } + ], + "source" : { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + "destination" : { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + }, + { + "vertices" : [ + { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + } + ], + "edges" : [ + { + "_id" : "relation/aliceAndCharly", + "_rev" : "198160968", + "_key" : "aliceAndCharly", + "_from" : "female/alice", + "_to" : "male/charly", + "type" : "friend" + } + ], + "source" : { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + "destination" : { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + } + }, + { + "vertices" : [ + { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + }, + { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + ], + "edges" : [ + { + "_id" : "relation/aliceAndCharly", + "_rev" : "198160968", + "_key" : "aliceAndCharly", + "_from" : "female/alice", + "_to" : "male/charly", + "type" : "friend" + }, + { + "_id" : "relation/charlyAndDiana", + "_rev" : "198357576", + "_key" : "charlyAndDiana", + "_from" : "male/charly", + "_to" : "female/diana", + "type" : "married" + } + ], + "source" : { + "_id" : "female/alice", + "_rev" : "196719176", + "_key" : "alice", + "name" : "Alice" + }, + "destination" : { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + }, + { + "vertices" : [ + { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + ], + "edges" : [ ], + "source" : { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + }, + "destination" : { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + }, + { + "vertices" : [ + { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + } + ], + "edges" : [ ], + "source" : { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + }, + "destination" : { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + } + }, + { + "vertices" : [ + { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + }, + { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + ], + "edges" : [ + { + "_id" : "relation/bobAndDiana", + "_rev" : "198554184", + "_key" : "bobAndDiana", + "_from" : "male/bob", + "_to" : "female/diana", + "type" : "friend" + } + ], + "source" : { + "_id" : "male/bob", + "_rev" : "197112392", + "_key" : "bob", + "name" : "Bob" + }, + "destination" : { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + }, + { + "vertices" : [ + { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + } + ], + "edges" : [ ], + "source" : { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + }, + "destination" : { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + } + }, + { + "vertices" : [ + { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + }, + { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + ], + "edges" : [ + { + "_id" : "relation/charlyAndDiana", + "_rev" : "198357576", + "_key" : "charlyAndDiana", + "_from" : "male/charly", + "_to" : "female/diana", + "type" : "married" + } + ], + "source" : { + "_id" : "male/charly", + "_rev" : "197309000", + "_key" : "charly", + "name" : "Charly" + }, + "destination" : { + "_id" : "female/diana", + "_rev" : "197505608", + "_key" : "diana", + "name" : "Diana" + } + } + ] +] +``` +
+Return all inbound paths of the graph "social" with a maximal +length of 1 and a minimal length of 2: +
+ +``` +arangosh> ~require("internal").db; +-1 +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> db._query("RETURN GRAPH_PATHS('social', 'inbound', false, 1, 2)").toArray(); +[ + [ + { + "vertices" : [ + { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + { + "_id" : "male/charly", + "_rev" : "128299592", + "_key" : "charly", + "name" : "Charly" + } + ], + "edges" : [ + { + "_id" : "relation/charlyAndDiana", + "_rev" : "129348168", + "_key" : "charlyAndDiana", + "_from" : "male/charly", + "_to" : "female/diana", + "type" : "married" + } + ], + "source" : { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + "destination" : { + "_id" : "male/charly", + "_rev" : "128299592", + "_key" : "charly", + "name" : "Charly" + } + }, + { + "vertices" : [ + { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + { + "_id" : "male/charly", + "_rev" : "128299592", + "_key" : "charly", + "name" : "Charly" + }, + { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + ], + "edges" : [ + { + "_id" : "relation/charlyAndDiana", + "_rev" : "129348168", + "_key" : "charlyAndDiana", + "_from" : "male/charly", + "_to" : "female/diana", + "type" : "married" + }, + { + "_id" : "relation/aliceAndCharly", + "_rev" : "129151560", + "_key" : "aliceAndCharly", + "_from" : "female/alice", + "_to" : "male/charly", + "type" : "friend" + } + ], + "source" : { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + "destination" : { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + }, + { + "vertices" : [ + { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + { + "_id" : "male/bob", + "_rev" : "128102984", + "_key" : "bob", + "name" : "Bob" + } + ], + "edges" : [ + { + "_id" : "relation/bobAndDiana", + "_rev" : "129544776", + "_key" : "bobAndDiana", + "_from" : "male/bob", + "_to" : "female/diana", + "type" : "friend" + } + ], + "source" : { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + "destination" : { + "_id" : "male/bob", + "_rev" : "128102984", + "_key" : "bob", + "name" : "Bob" + } + }, + { + "vertices" : [ + { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + { + "_id" : "male/bob", + "_rev" : "128102984", + "_key" : "bob", + "name" : "Bob" + }, + { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + ], + "edges" : [ + { + "_id" : "relation/bobAndDiana", + "_rev" : "129544776", + "_key" : "bobAndDiana", + "_from" : "male/bob", + "_to" : "female/diana", + "type" : "friend" + }, + { + "_id" : "relation/aliceAndBob", + "_rev" : "128889416", + "_key" : "aliceAndBob", + "_from" : "female/alice", + "_to" : "male/bob", + "type" : "married" + } + ], + "source" : { + "_id" : "female/diana", + "_rev" : "128496200", + "_key" : "diana", + "name" : "Diana" + }, + "destination" : { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + }, + { + "vertices" : [ + { + "_id" : "male/bob", + "_rev" : "128102984", + "_key" : "bob", + "name" : "Bob" + }, + { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + ], + "edges" : [ + { + "_id" : "relation/aliceAndBob", + "_rev" : "128889416", + "_key" : "aliceAndBob", + "_from" : "female/alice", + "_to" : "male/bob", + "type" : "married" + } + ], + "source" : { + "_id" : "male/bob", + "_rev" : "128102984", + "_key" : "bob", + "name" : "Bob" + }, + "destination" : { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + }, + { + "vertices" : [ + { + "_id" : "male/charly", + "_rev" : "128299592", + "_key" : "charly", + "name" : "Charly" + }, + { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + ], + "edges" : [ + { + "_id" : "relation/aliceAndCharly", + "_rev" : "129151560", + "_key" : "aliceAndCharly", + "_from" : "female/alice", + "_to" : "male/charly", + "type" : "friend" + } + ], + "source" : { + "_id" : "male/charly", + "_rev" : "128299592", + "_key" : "charly", + "name" : "Charly" + }, + "destination" : { + "_id" : "female/alice", + "_rev" : "127709768", + "_key" : "alice", + "name" : "Alice" + } + } + ] +] +``` + !SUBSECTION GRAPH_SHORTEST_PATH -!SUBSECTION_GRAPH_DISTANCE_TO +
+`GRAPH_SHORTEST_PATH (graphName, startVertexExample, endVertexExample, options)` +*The GRAPH\_SHORTEST\_PATH function returns all shortest paths of a graph.* +
+This function determines all shortest paths in a graph identified by *graphName*. +The function accepts an id, an example, a list of examples +or even an empty example as parameter for +start and end vertex. If one wants to calls this function to receive nearly all +shortest paths for a graph the +option *algorithm* should be set to *Floyd-Warshall* to increase performance. +If no algorithm is provided in the options the function chooses the appropriate +one (either *Floyd-Warshall* or *Dijsktra*) according to its parameters. +The length of a path is by default the amount of edges from one start vertex to +an end vertex. The option weight allows the user to define an edge attribute +representing the length. +
+* String *graphName* : The name of the graph. +* String|Object|Array *startVertexExample* : An example for the desired +start Vertices (see below). +* String|Object|Array *endVertexExample* : An example for the desired +end Vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String|Array *edgeCollectionRestriction* : One or multiple edge +collections that should be considered. +* String|Array *startVertexCollectionRestriction* : One or multiple vertex +collections that should be considered. +* String|Array *endVertexCollectionRestriction* : One or multiple vertex +collections that should be considered. +* String|Object|Array *edgeExamples* : A filter example for the +edges in the shortest paths (see below). +* String *algorithm* : The algorithm to calculate +the shortest paths. If both start and end vertex examples are empty *Floyd-Warshall* is +used, otherwise the default is *Dijkstra* +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path could +not be calculated. +
+Examples for startVertexExample/endVertexExample: +* {} : Returns all possible start/end vertices for this graph. +* *idString* : Returns the vertex with the id *idString*. +* {*key* : *value*} : Returns the vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the vertices that match one of +the examples. +
+@EXAMPLES +
+A route planner example, shortest distance from all villages to other cities: +
- +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_SHORTEST_PATH(" +........> +"'routeplanner', {}, {}, {weight : 'distance', endVertexCollectionRestriction : 'city', " + +........> "startVertexCollectionRestriction : 'village'}) RETURN [e.startVertex, e.vertex._id, " + +........> "e.distance, LENGTH(e.paths)]" +........> ).toArray(); +[ + [ + "village/Rosenheim", + "city/Cologne", + 730, + 1 + ], + [ + "village/Rosenheim", + "city/Berlin", + 680, + 1 + ], + [ + "village/Rosenheim", + "city/Munich", + 80, + 1 + ], + [ + "village/Olpe", + "city/Berlin", + 700, + 1 + ], + [ + "village/Olpe", + "city/Munich", + 600, + 1 + ], + [ + "village/Olpe", + "city/Cologne", + 100, + 1 + ] +] +``` +
+A route planner example, shortest distance from Munich and Cologne to Olpe: +
-!SUBSECTION_GRAPH_TRAVERSAL +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_SHORTEST_PATH(" +........> +"'routeplanner', [{_id: 'city/Cologne'},{_id: 'city/Munich'}], 'village/Olpe', " + +........> "{weight : 'distance'}) RETURN [e.startVertex, e.vertex._id, e.distance, LENGTH(e.paths)]" +........> ).toArray(); +[ + [ + "city/Cologne", + "village/Olpe", + 100, + 1 + ], + [ + "city/Munich", + "village/Olpe", + 600, + 1 + ] +] +``` +
+ +!SUBSECTION GRAPH_TRAVERSAL -!SUBSECTION_GRAPH_TRAVERSAL_TREE +
+`GRAPH_TRAVERSAL (graphName, startVertexExample, direction, options)` +*The GRAPH\_TRAVERSAL function traverses through the graph.* +
+This function performs traversals on the given graph. +For a more detailed documentation on the optional parameters see +[Traversals](../Traversals/README.md). +
+* String *graphName* : The name of the graph. +* String|Object|Array *startVerte* : The ID of the start vertex of the traversal. +* String|Object|Array *direction* : The direction of the edges. Possible values +are *outbound*, *inbound* and *any* (default). +* Object *options* : Optional options, see below: +
+@EXAMPLES +
+A route planner example, start a traversal from Munich : +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_TRAVERSAL('routeplanner', 'city/Munich'," + +........> " 'outbound') RETURN e" +........> ).toArray(); +[ + { + "vertex" : { + "_id" : "city/Munich", + "_rev" : "113553992", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + }, + { + "vertex" : { + "_id" : "city/Cologne", + "_rev" : "113357384", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + } + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "114012744", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + }, + { + "vertex" : { + "_id" : "village/Rosenheim", + "_rev" : "114209352", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + }, + { + "vertex" : { + "_id" : "village/Rosenheim", + "_rev" : "114209352", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "114012744", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + } +] +``` +
+A route planner example, start a traversal from Munich with a max depth of 1 +so only the direct neighbors of munich are returned: +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_TRAVERSAL('routeplanner', 'city/Munich'," + +........> " 'outbound', {maxDepth : 1}) RETURN e" +........> ).toArray(); +[ + { + "vertex" : { + "_id" : "city/Munich", + "_rev" : "118600264", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + }, + { + "vertex" : { + "_id" : "city/Cologne", + "_rev" : "118403656", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + } + }, + { + "vertex" : { + "_id" : "village/Rosenheim", + "_rev" : "119255624", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "119059016", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + } +] +``` +
+ +!SUBSECTION GRAPH_TRAVERSAL_TREE -!SUBSECTION_GRAPH_NEIGHBORS +
+`GRAPH_TRAVERSAL_TREE (graphName, startVertexExample, direction, connectName, options)` +*The GRAPH\_TRAVERSAL\_TREE function traverses through the graph.* +This function creates a tree format from the result for a better visualization of +the path. +This function performs traversals on the given graph. +For a more detailed documentation on the optional parameters see +[Traversals](../Traversals/README.md). +
+* String *graphName* : The name of the graph. +* String|Object|Array *startVerte* : The ID of the start vertex +of the traversal. +* String|Object|Array *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String|Object|Array *connectName* : The result attribute which +contains the connection. +* Object *options* : Optional options, see below: +
+@EXAMPLES +
+A route planner example, start a traversal from Munich : +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_TRAVERSAL_TREE('routeplanner', 'city/Munich'," + +........> " 'outbound', 'connnection') RETURN e" +........> ).toArray(); +[ + [ + { + "_id" : "city/Munich", + "_rev" : "157004360", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000, + "connnection" : [ + { + "_id" : "city/Cologne", + "_rev" : "156807752", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000, + "connnection" : [ + { + "_id" : "village/Olpe", + "_rev" : "157463112", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "157659720", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + }, + { + "_id" : "village/Rosenheim", + "_rev" : "157659720", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Olpe", + "_rev" : "157463112", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + } + ] +] +``` +
+A route planner example, start a traversal from Munich with a max depth of 1 so +only the direct neighbors of munich are returned: +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_TRAVERSAL_TREE('routeplanner', 'city/Munich',"+ +........> " 'outbound', 'connnection', {maxDepth : 1}) RETURN e" +........> ).toArray(); +[ + [ + { + "_id" : "city/Munich", + "_rev" : "162050632", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000, + "connnection" : [ + { + "_id" : "city/Cologne", + "_rev" : "161854024", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "162705992", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Olpe", + "_rev" : "162509384", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + } + ] +] +``` +
+ +!SUBSECTION GRAPH_DISTANCE_TO + + + +
+`GENERAL_GRAPH_DISTANCE_TO (graphName, startVertexExample, endVertexExample, options)` +/// *The GRAPH\_DISTANCE\_TO function returns all paths and there distance within a graph.* +/// +/// This function is a wrapper of [GRAPH\_SHORTEST\_PATH](#SUBSECTION GRAPH_SHORTEST_PATH). +/// It does not return the actual path but only the distance between two vertices. +
+ +!SUBSECTION GRAPH_NEIGHBORS +
+`GRAPH_NEIGHBORS (graphName, vertexExample, options)` +*The GRAPH\_NEIGHBORS function returns all neighbors of vertices.* +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertex. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertexExample* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction +of the edges. Possible values are *outbound*, *inbound* and *any* (default). +* String|Object|Array *edgeExamples* : A filter example +for the edges to the neighbors (see below). +* String|Object|Array *neighborExamples* : An example for +the desired neighbors (see below). +* String|Array *edgeCollectionRestriction* : One or multiple +edge collections that should be considered. +* String|Array *vertexCollectionRestriction* : One or multiple +vertex collections that should be considered. +* Number *minDepth* : Defines the minimal +depth a path to a neighbor must have to be returned (default is 1). +* Number *maxDepth* : Defines the maximal +depth a path to a neighbor must have to be returned (default is 1). +
+Examples for edgeExamples/neighborExamples: +* {} : Returns all possible edges/neighbors for this graph. +* *idString* : Returns the edge/vertex with the id *idString*. +* {*key* : *value*} : Returns the edges/vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the edges/vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, all neighbors of locations with a distance of either +700 or 600.: +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_NEIGHBORS(" +........> +"'routeplanner', {}, {edgeExamples : [{distance: 600}, {distance: 700}]}) RETURN e" +........> ).toArray(); +[ + { + "vertex" : { + "_id" : "city/Munich", + "_rev" : "78230088", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + "path" : { + "edges" : [ + { + "_id" : "highway/79475272", + "_rev" : "79475272", + "_key" : "79475272", + "_from" : "city/Berlin", + "_to" : "city/Munich", + "distance" : 600 + } + ], + "vertices" : [ + { + "_id" : "city/Berlin", + "_rev" : "77836872", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + }, + { + "_id" : "city/Munich", + "_rev" : "78230088", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + ] + }, + "startVertex" : "city/Berlin" + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "78688840", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + "path" : { + "edges" : [ + { + "_id" : "road/80065096", + "_rev" : "80065096", + "_key" : "80065096", + "_from" : "city/Berlin", + "_to" : "village/Olpe", + "distance" : 700 + } + ], + "vertices" : [ + { + "_id" : "city/Berlin", + "_rev" : "77836872", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "78688840", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + }, + "startVertex" : "city/Berlin" + }, + { + "vertex" : { + "_id" : "city/Berlin", + "_rev" : "77836872", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + }, + "path" : { + "edges" : [ + { + "_id" : "highway/79475272", + "_rev" : "79475272", + "_key" : "79475272", + "_from" : "city/Berlin", + "_to" : "city/Munich", + "distance" : 600 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "78230088", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "city/Berlin", + "_rev" : "77836872", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + } + ] + }, + "startVertex" : "city/Munich" + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "78688840", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + "path" : { + "edges" : [ + { + "_id" : "road/80654920", + "_rev" : "80654920", + "_key" : "80654920", + "_from" : "city/Munich", + "_to" : "village/Olpe", + "distance" : 600 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "78230088", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "78688840", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + }, + "startVertex" : "city/Munich" + }, + { + "vertex" : { + "_id" : "city/Berlin", + "_rev" : "77836872", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + }, + "path" : { + "edges" : [ + { + "_id" : "road/80065096", + "_rev" : "80065096", + "_key" : "80065096", + "_from" : "city/Berlin", + "_to" : "village/Olpe", + "distance" : 700 + } + ], + "vertices" : [ + { + "_id" : "village/Olpe", + "_rev" : "78688840", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "city/Berlin", + "_rev" : "77836872", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + } + ] + }, + "startVertex" : "village/Olpe" + }, + { + "vertex" : { + "_id" : "city/Munich", + "_rev" : "78230088", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + "path" : { + "edges" : [ + { + "_id" : "road/80654920", + "_rev" : "80654920", + "_key" : "80654920", + "_from" : "city/Munich", + "_to" : "village/Olpe", + "distance" : 600 + } + ], + "vertices" : [ + { + "_id" : "village/Olpe", + "_rev" : "78688840", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "city/Munich", + "_rev" : "78230088", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + ] + }, + "startVertex" : "village/Olpe" + } +] +``` +
+A route planner example, all outbound neighbors of munich with a maximal depth of 2 : +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_NEIGHBORS(" +........> +"'routeplanner', 'city/Munich', {direction : 'outbound', maxDepth : 2}) RETURN e" +........> ).toArray(); +[ + { + "vertex" : { + "_id" : "city/Cologne", + "_rev" : "131576392", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + "path" : { + "edges" : [ + { + "_id" : "highway/133214792", + "_rev" : "133214792", + "_key" : "133214792", + "_from" : "city/Munich", + "_to" : "city/Cologne", + "distance" : 650 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "131773000", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "city/Cologne", + "_rev" : "131576392", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + } + ] + }, + "startVertex" : "city/Munich" + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "132231752", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + "path" : { + "edges" : [ + { + "_id" : "highway/133214792", + "_rev" : "133214792", + "_key" : "133214792", + "_from" : "city/Munich", + "_to" : "city/Cologne", + "distance" : 650 + }, + { + "_id" : "road/134394440", + "_rev" : "134394440", + "_key" : "134394440", + "_from" : "city/Cologne", + "_to" : "village/Olpe", + "distance" : 100 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "131773000", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "city/Cologne", + "_rev" : "131576392", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "132231752", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + }, + "startVertex" : "city/Munich" + }, + { + "vertex" : { + "_id" : "village/Rosenheim", + "_rev" : "132428360", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + }, + "path" : { + "edges" : [ + { + "_id" : "highway/133214792", + "_rev" : "133214792", + "_key" : "133214792", + "_from" : "city/Munich", + "_to" : "city/Cologne", + "distance" : 650 + }, + { + "_id" : "road/134591048", + "_rev" : "134591048", + "_key" : "134591048", + "_from" : "city/Cologne", + "_to" : "village/Rosenheim", + "distance" : 750 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "131773000", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "city/Cologne", + "_rev" : "131576392", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "132428360", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + }, + "startVertex" : "city/Munich" + }, + { + "vertex" : { + "_id" : "village/Rosenheim", + "_rev" : "132428360", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + }, + "path" : { + "edges" : [ + { + "_id" : "road/134001224", + "_rev" : "134001224", + "_key" : "134001224", + "_from" : "city/Munich", + "_to" : "village/Rosenheim", + "distance" : 80 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "131773000", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "132428360", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + }, + "startVertex" : "city/Munich" + }, + { + "vertex" : { + "_id" : "village/Olpe", + "_rev" : "132231752", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + "path" : { + "edges" : [ + { + "_id" : "road/134197832", + "_rev" : "134197832", + "_key" : "134197832", + "_from" : "city/Munich", + "_to" : "village/Olpe", + "distance" : 600 + } + ], + "vertices" : [ + { + "_id" : "city/Munich", + "_rev" : "131773000", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "132231752", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + }, + "startVertex" : "city/Munich" + } +] +``` +
+ + + +!SUBSECTION GRAPH_EDGES + + + +
+`GRAPH_EDGES (graphName, vertexExample, options)` +*The GRAPH\_EDGES function returns all edges of vertices.* +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertex. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertexExample* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction +of the edges. Possible values are *outbound*, *inbound* and *any* (default). +* String|Array *edgeCollectionRestriction* : One or multiple +edge collections that should be considered. +* String|Array *startVertexCollectionRestriction* : One or multiple +vertex collections that should be considered. +* String|Array *endVertexCollectionRestriction* : One or multiple +vertex collections that should be considered. +* String|Object|Array *edgeExamples* : A filter example +for the edges (see below). +* String|Object|Array *neighborExamples* : An example for +the desired neighbors (see below). +* Number *minDepth* : Defines the minimal +depth a path to a neighbor must have to be returned (default is 1). +* Number *maxDepth* : Defines the maximal +depth a path to a neighbor must have to be returned (default is 1). +
+Examples for edgeExamples/neighborExamples: +* {} : Returns all possible edges/neighbors for this graph. +* *idString* : Returns the edge/vertex with the id *idString*. +* {*key* : *value*} : Returns the edges/vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the edges/vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, all edges to locations with a distance of either 700 or 600.: +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_EDGES(" +........> +"'routeplanner', {}, {edgeExamples : [{distance: 600}, {distance: 700}]}) RETURN e" +........> ).toArray(); +[ + { + "_id" : "highway/29012552", + "_rev" : "29012552", + "_key" : "29012552", + "_from" : "city/Berlin", + "_to" : "city/Munich", + "distance" : 600 + }, + { + "_id" : "road/29602376", + "_rev" : "29602376", + "_key" : "29602376", + "_from" : "city/Berlin", + "_to" : "village/Olpe", + "distance" : 700 + }, + { + "_id" : "highway/29012552", + "_rev" : "29012552", + "_key" : "29012552", + "_from" : "city/Berlin", + "_to" : "city/Munich", + "distance" : 600 + }, + { + "_id" : "road/30192200", + "_rev" : "30192200", + "_key" : "30192200", + "_from" : "city/Munich", + "_to" : "village/Olpe", + "distance" : 600 + }, + { + "_id" : "road/29602376", + "_rev" : "29602376", + "_key" : "29602376", + "_from" : "city/Berlin", + "_to" : "village/Olpe", + "distance" : 700 + }, + { + "_id" : "road/30192200", + "_rev" : "30192200", + "_key" : "30192200", + "_from" : "city/Munich", + "_to" : "village/Olpe", + "distance" : 600 + } +] +``` +
+A route planner example, all outbound edges of munich with a maximal depth of 2 : +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_EDGES(" +........> +"'routeplanner', 'city/Munich', {direction : 'outbound', maxDepth : 2}) RETURN e" +........> ).toArray(); +[ + { + "_id" : "highway/34255432", + "_rev" : "34255432", + "_key" : "34255432", + "_from" : "city/Munich", + "_to" : "city/Cologne", + "distance" : 650 + }, + { + "_id" : "highway/34255432", + "_rev" : "34255432", + "_key" : "34255432", + "_from" : "city/Munich", + "_to" : "city/Cologne", + "distance" : 650 + }, + { + "_id" : "road/35435080", + "_rev" : "35435080", + "_key" : "35435080", + "_from" : "city/Cologne", + "_to" : "village/Olpe", + "distance" : 100 + }, + { + "_id" : "highway/34255432", + "_rev" : "34255432", + "_key" : "34255432", + "_from" : "city/Munich", + "_to" : "city/Cologne", + "distance" : 650 + }, + { + "_id" : "road/35631688", + "_rev" : "35631688", + "_key" : "35631688", + "_from" : "city/Cologne", + "_to" : "village/Rosenheim", + "distance" : 750 + }, + { + "_id" : "road/35041864", + "_rev" : "35041864", + "_key" : "35041864", + "_from" : "city/Munich", + "_to" : "village/Rosenheim", + "distance" : 80 + }, + { + "_id" : "road/35238472", + "_rev" : "35238472", + "_key" : "35238472", + "_from" : "city/Munich", + "_to" : "village/Olpe", + "distance" : 600 + } +] +``` +
+ + + +!SUBSECTION GRAPH_VERTICES + + + +
+`GRAPH_VERTICES (graphName, vertexExample, options)` +*The GRAPH\_VERTICES function returns all vertices.* +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertex. +According to the optional filters it will only return vertices that have +outbound, onbound or any (default) edges. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertexExample* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the +edges. Possible values are *outbound*, *inbound* and *any* (default). +* String|Array *vertexCollectionRestriction* : One or multiple +vertex collections that should be considered. +
+Examples for vertexExample: +* {} : Returns all possible vertices for this graph. +* *idString* : Returns the vertex with the id *idString*. +* {*key* : *value*} : Returns the vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, all vertices of the graph +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_COMMON_NEIGHBORS(" +........> +"'routeplanner', {}) RETURN e" +........> ).toArray(); +[ArangoError 1541: invalid number of arguments for function '_AQL:GRAPH_COMMON_NEIGHBORS()'] +``` +
+A route planner example, all vertices from collection *city*. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_COMMON_NEIGHBORS(" +........> +"'routeplanner', {}, {direction : 'any', vertexCollectionRestriction" + +........> " : 'city'}) RETURN e" +........> ).toArray(); +[ ] +``` +
+ + +!SUBSECTION GRAPH_COMMON_NEIGHBORS + + + +
+`GRAPH_VERTICES (graphName, vertexExample, options)` +*The GRAPH\_VERTICES function returns all vertices.* +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertex. +According to the optional filters it will only return vertices that have +outbound, onbound or any (default) edges. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertexExample* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the +edges. Possible values are *outbound*, *inbound* and *any* (default). +* String|Array *vertexCollectionRestriction* : One or multiple +vertex collections that should be considered. +
+Examples for vertexExample: +* {} : Returns all possible vertices for this graph. +* *idString* : Returns the vertex with the id *idString*. +* {*key* : *value*} : Returns the vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, all vertices of the graph +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_COMMON_NEIGHBORS(" +........> +"'routeplanner', {isCapital : true}, {isCapital : true}) RETURN e" +........> ).toArray(); +[ + { + "city/Berlin" : { + "city/Munich" : [ + { + "_id" : "city/Cologne", + "_rev" : "214651185", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "215306545", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "215503153", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + } + }, + { + "city/Munich" : { + "city/Berlin" : [ + { + "_id" : "city/Cologne", + "_rev" : "214651185", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "215306545", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "215503153", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + } + } +] +``` +
+A route planner example, all vertices from collection *city*. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_COMMON_NEIGHBORS(" +........> +"'routeplanner', 'city/Munich', {}, {direction : 'outbound', maxDepth : 2}, "+ +........> "{direction : 'outbound', maxDepth : 2}) RETURN e" +........> ).toArray(); +[ + { + "city/Munich" : { + "city/Berlin" : [ + { + "_id" : "city/Cologne", + "_rev" : "204558641", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "205214001", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "205410609", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ], + "city/Cologne" : [ + { + "_id" : "village/Olpe", + "_rev" : "205214001", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "205410609", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + } + } +] +``` +
+ + + +!SUBSECTION GRAPH_COMMON_PROPERTIES + + + +
+`GRAPH_COMMON_PROPERTIES (graphName, vertex1Example, vertex2Examples, options)` +*The GRAPH\_COMMON\_PROPERTIES function returns all vertices +defined by the examples that share common properties +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertex1Example and vertex2Example. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertex1Example* : An example for the desired +vertices (see below). +* String|Object|Array *vertex2Example* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +// * String|Array *vertex1CollectionRestriction* : One or multiple +vertex collections that should be considered. +* String|Array *vertex2CollectionRestriction* : One or multiple +vertex collections that should be considered. +* String|Array *ignoreProperties* : One or multiple +attributes of a document that should be ignored. +
+Examples for vertexExample: +* {} : Returns all possible vertices for this graph. +* *idString* : Returns the vertex with the id *idString*. +* {*key* : *value*} : Returns the vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, all locations with the same properties: +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_COMMON_PROPERTIES(" +........> +"'routeplanner', {}, {}) RETURN e" +........> ).toArray(); +[ + { + "city/Berlin" : [ + { + "_id" : "city/Munich", + "_rev" : "77156657", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + ] + }, + { + "city/Munich" : [ + { + "_id" : "city/Berlin", + "_rev" : "76763441", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + }, + { + "_id" : "city/Cologne", + "_rev" : "76960049", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + } + ] + }, + { + "city/Cologne" : [ + { + "_id" : "village/Rosenheim", + "_rev" : "77812017", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "village/Olpe", + "_rev" : "77615409", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + }, + { + "_id" : "city/Munich", + "_rev" : "77156657", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + ] + }, + { + "village/Rosenheim" : [ + { + "_id" : "city/Cologne", + "_rev" : "76960049", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Olpe", + "_rev" : "77615409", + "_key" : "Olpe", + "isCapital" : false, + "population" : 80000 + } + ] + }, + { + "village/Olpe" : [ + { + "_id" : "city/Cologne", + "_rev" : "76960049", + "_key" : "Cologne", + "isCapital" : false, + "population" : 1000000 + }, + { + "_id" : "village/Rosenheim", + "_rev" : "77812017", + "_key" : "Rosenheim", + "isCapital" : false, + "population" : 80000 + } + ] + } +] +``` +
+A route planner example, all cities which share same properties except for population. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("FOR e IN GRAPH_COMMON_PROPERTIES(" +........> +"'routeplanner', {}, {}, {vertex1CollectionRestriction : 'city', " + +........> "vertex2CollectionRestriction : 'city'" + +........> " ,ignoreProperties: 'population'}) RETURN e" +........> ).toArray(); +[ + { + "city/Berlin" : [ + { + "_id" : "city/Munich", + "_rev" : "67064113", + "_key" : "Munich", + "isCapital" : true, + "population" : 1000000 + } + ] + }, + { + "city/Munich" : [ + { + "_id" : "city/Berlin", + "_rev" : "66670897", + "_key" : "Berlin", + "isCapital" : true, + "population" : 3000000 + } + ] + } +] +``` +
+ + + +!SUBSECTION GRAPH_ABSOLUTE_ECCENTRICITY + + + +
+`GRAPH_ABSOLUTE_ECCENTRICITY (graphName, vertexExample, options)` +*The GRAPH\_ABSOLUTE\_ECCENTRICITY function returns the +[eccentricity](http://en.wikipedia.org/wiki/Distance_%28graph_theory%29) +of the vertices defined by the examples. +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertexExample. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertexExample* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String|Array *edgeCollectionRestriction* : One or multiple edge +collections that should be considered. +* String|Array *startVertexCollectionRestriction* : One or multiple vertex +collections that should be considered. +* String|Array *endVertexCollectionRestriction* : One or multiple vertex +collections that should be considered. +* String|Object|Array *edgeExamples* : A filter example for the +edges in the shortest paths (see below). +* String *algorithm* : The algorithm to calculate +the shortest paths. +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+Examples for vertexExample: +* {} : Returns all possible vertices for this graph. +* *idString* : Returns the vertex with the id *idString*. +* {*key* : *value*} : Returns the vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, the absolute eccentricity of all locations. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_ECCENTRICITY(" +........> +"'routeplanner', {})" +........> ).toArray(); +[ + { + "city/Munich" : 1, + "city/Cologne" : 1, + "city/Berlin" : 1, + "village/Olpe" : 2, + "village/Rosenheim" : 2 + } +] +``` +
+A route planner example, the absolute eccentricity of all locations. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_ECCENTRICITY(" +........> +"'routeplanner', {}, {weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 800, + "city/Munich" : 650, + "city/Cologne" : 800, + "village/Rosenheim" : 730, + "village/Olpe" : 700 + } +] +``` +
+A route planner example, the absolute eccentricity of all cities regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_ECCENTRICITY(" +........> + "'routeplanner', {}, {startVertexCollectionRestriction : 'city', " + +........> "direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Munich" : 650, + "city/Berlin" : 850, + "city/Cologne" : 750 + } +] +``` +
+ + +!SUBSECTION GRAPH_ECCENTRICITY + + + +
+`GRAPH_ECCENTRICITY (graphName, options)` +*The GRAPH\_ECCENTRICITY function returns the normalized +[eccentricity](http://en.wikipedia.org/wiki/Distance_%28graph_theory%29) +of the graphs vertices +
+* String *graphName* : The name of the graph. +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String *algorithm* : The algorithm to calculate +the shortest paths. +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+@EXAMPLES +
+A route planner example, the eccentricity of all locations. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ECCENTRICITY(" +........> +"'routeplanner')" +........> ).toArray(); +[ + { + "city/Berlin" : 1, + "city/Munich" : 1, + "city/Cologne" : 1, + "village/Rosenheim" : 0.5, + "village/Olpe" : 0.5 + } +] +``` +
+A route planner example, the eccentricity of all locations. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ECCENTRICITY(" +........> +"'routeplanner', {weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Munich" : 1, + "city/Cologne" : 0.8125, + "city/Berlin" : 0.8125, + "village/Olpe" : 0.9285714285714286, + "village/Rosenheim" : 0.8904109589041096 + } +] +``` +
+ +!SUBSECTION GRAPH_ABSOLUTE_CLOSENESS + + + +
+`GRAPH_ABSOLUTE_CLOSENESS (graphName, vertexExample, options)` +*The GRAPH\_ABSOLUTE\_CLOSENESS function returns the +[closeness](http://en.wikipedia.org/wiki/Centrality#Closeness_centrality) +of the vertices defined by the examples. +
+The function accepts an id, an example, a list of examples or even an empty +example as parameter for vertexExample. +
+* String *graphName* : The name of the graph. +* String|Object|Array *vertexExample* : An example for the desired +vertices (see below). +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String|Array *edgeCollectionRestriction* : One or multiple edge +collections that should be considered. +* String|Array *startVertexCollectionRestriction* : One or multiple vertex +collections that should be considered. +* String|Array *endVertexCollectionRestriction* : One or multiple vertex +collections that should be considered. +* String|Object|Array *edgeExamples* : A filter example for the +edges in the shortest paths (see below). +* String *algorithm* : The algorithm to calculate +the shortest paths. +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+Examples for vertexExample: +* {} : Returns all possible vertices for this graph. +* *idString* : Returns the vertex with the id *idString*. +* {*key* : *value*} : Returns the vertices that match this example. +* [{*key1* : *value1*}, {*key2* : *value2*}] : Returns the vertices that +match one of the examples. +
+@EXAMPLES +
+A route planner example, the absolute closeness of all locations. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_CLOSENESS(" +........> +"'routeplanner', {})" +........> ).toArray(); +[ + { + "city/Berlin" : 4, + "city/Cologne" : 4, + "city/Munich" : 4, + "village/Rosenheim" : 5, + "village/Olpe" : 5 + } +] +``` +
+A route planner example, the absolute closeness of all locations. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_CLOSENESS(" +........> +"'routeplanner', {}, {weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 2780, + "city/Cologne" : 2280, + "city/Munich" : 1930, + "village/Olpe" : 2080, + "village/Rosenheim" : 2170 + } +] +``` +
+A route planner example, the absolute closeness of all cities regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_CLOSENESS(" +........> + "'routeplanner', {}, {startVertexCollectionRestriction : 'city', " + +........> "direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 2830, + "city/Munich" : 1330, + "city/Cologne" : 850 + } +] +``` +
+ +!SUBSECTION GRAPH_CLOSENESS + + + +
+`GRAPH_CLOSENESS (graphName, options)` +*The GRAPH\_CLOSENESS function returns the normalized +[closeness](http://en.wikipedia.org/wiki/Centrality#Closeness_centrality) +of graphs vertices. +
+* String *graphName* : The name of the graph. +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String *algorithm* : The algorithm to calculate +the shortest paths. +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+@EXAMPLES +
+A route planner example, the closeness of all locations. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_CLOSENESS(" +........> +"'routeplanner')" +........> ).toArray(); +[ + { + "city/Berlin" : 1, + "city/Munich" : 1, + "city/Cologne" : 1, + "village/Rosenheim" : 0.8, + "village/Olpe" : 0.8 + } +] +``` +
+A route planner example, the closeness of all locations. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_CLOSENESS(" +........> +"'routeplanner', {weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Munich" : 1, + "city/Cologne" : 0.8464912280701755, + "city/Berlin" : 0.6942446043165468, + "village/Rosenheim" : 0.8894009216589862, + "village/Olpe" : 0.9278846153846155 + } +] +``` +
+A route planner example, the absolute closeness of all cities regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_CLOSENESS(" +........> + "'routeplanner',{direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 0.3656319670725433, + "city/Munich" : 1, + "city/Cologne" : 0.7216326530612246, + "village/Rosenheim" : 0, + "village/Olpe" : 0 + } +] +``` +
+ +!SUBSECTION GRAPH_ABSOLUTE_BETWEENNESS + + + +
+`GRAPH_ABSOLUTE_BETWEENNESS (graphName, vertexExample, options)` +*The GRAPH\_ABSOLUTE\_BETWEENNESS function returns the +[betweenness](http://en.wikipedia.org/wiki/Betweenness_centrality) +of all vertices in the graph. +
+
+* String *graphName* : The name of the graph. +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+@EXAMPLES +
+A route planner example, the absolute betweenness of all locations. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_BETWEENNESS(" +........> +"'routeplanner', {})" +........> ).toArray(); +[ + { + "city/Berlin" : 0.6666666666666666, + "city/Cologne" : 0.6666666666666666, + "city/Munich" : 0.6666666666666666, + "village/Olpe" : 0, + "village/Rosenheim" : 0 + } +] +``` +
+A route planner example, the absolute closeness of all locations. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_BETWEENNESS(" +........> +"'routeplanner', {weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 0, + "city/Munich" : 6, + "city/Cologne" : 0, + "village/Olpe" : 2, + "village/Rosenheim" : 0 + } +] +``` +
+A route planner example, the absolute closeness of all cities regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_ABSOLUTE_BETWEENNESS(" +........> + "'routeplanner', {direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 0, + "city/Munich" : 1, + "city/Cologne" : 0, + "village/Rosenheim" : 0, + "village/Olpe" : 0 + } +] +``` +
+ +!SUBSECTION GRAPH_BETWEENNESS + + + +
+`GRAPH_BETWEENNESS (graphName, options)` +*The GRAPH\_BETWEENNESS function returns the +[betweenness](http://en.wikipedia.org/wiki/Betweenness_centrality) +of graphs vertices. +
+* String *graphName* : The name of the graph. +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+@EXAMPLES +
+A route planner example, the betweenness of all locations. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_BETWEENNESS(" +........> +"'routeplanner')" +........> ).toArray(); +[ + { + "city/Munich" : 1, + "city/Cologne" : 1, + "city/Berlin" : 1, + "village/Olpe" : 0, + "village/Rosenheim" : 0 + } +] +``` +
+A route planner example, the closeness of all locations. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_BETWEENNESS(" +........> +"'routeplanner', {weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 0, + "city/Cologne" : 0, + "village/Olpe" : 0.3333333333333333, + "city/Munich" : 1, + "village/Rosenheim" : 0 + } +] +``` +
+A route planner example, the closeness of all cities regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_BETWEENNESS(" +........> + "'routeplanner', {direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + { + "city/Berlin" : 0, + "city/Munich" : 1, + "city/Cologne" : 0, + "village/Rosenheim" : 0, + "village/Olpe" : 0 + } +] +``` +
+ +!SUBSECTION GRAPH_RADIUS + + + +
+`GRAPH_RADIUS (graphName, options)` +*The GRAPH\_RADIUS function returns the +[radius](http://en.wikipedia.org/wiki/Eccentricity_%28graph_theory%29) +of a graph. +
+* String *graphName* : The name of the graph. +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String *algorithm* : The algorithm to calculate +the shortest paths. +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+@EXAMPLES +
+A route planner example, the radius of the graph. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_RADIUS(" +........> +"'routeplanner')" +........> ).toArray(); +[ + 1 +] +``` +
+A route planner example, the radius of the graph. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_RADIUS(" +........> +"'routeplanner', {weight : 'distance'})" +........> ).toArray(); +[ + 650 +] +``` +
+A route planner example, the cradius of the graph regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_RADIUS(" +........> + "'routeplanner', {direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + 650 +] +``` +
+ +!SUBSECTION GRAPH_DIAMETER + + + +
+`GRAPH_DIAMETER (graphName, options)` +*The GRAPH\_DIAMETER function returns the +[diameter](http://en.wikipedia.org/wiki/Eccentricity_%28graph_theory%29) +of a graph. +
+* String *graphName* : The name of the graph. +* Object *options* : Optional options, see below: +
+Possible options and there defaults: +* String *direction* : The direction of the edges. +Possible values are *outbound*, *inbound* and *any* (default). +* String *algorithm* : The algorithm to calculate +the shortest paths. +* String *weight* : The name of the attribute of +the edges containing the length. +* Number *defaultWeight* : Only used with the option *weight*. +If an edge does not have the attribute named as defined in option *weight* this default +is used as length. +If no default is supplied the default would be positive Infinity so the path and +hence the eccentricity can not be calculated. +
+@EXAMPLES +
+A route planner example, the diameter of the graph. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_DIAMETER(" +........> +"'routeplanner')" +........> ).toArray(); +[ + 2 +] +``` +
+A route planner example, tthe diameter of the graph. +This considers the actual distances. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_DIAMETER(" +........> +"'routeplanner', {weight : 'distance'})" +........> ).toArray(); +[ + 800 +] +``` +
+A route planner example, the diameter of the graph regarding only +outbound pathes. +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("routeplanner"); +arangosh> db._query("RETURN GRAPH_DIAMETER(" +........> + "'routeplanner', {direction : 'outbound', weight : 'distance'})" +........> ).toArray(); +[ + 850 +] +``` +
+ + + diff --git a/Documentation/Books/Users/Foxx/FoxxRepository.mdpp b/Documentation/Books/Users/Foxx/FoxxRepository.mdpp index 818b05d93e..a0b2b3d893 100644 --- a/Documentation/Books/Users/Foxx/FoxxRepository.mdpp +++ b/Documentation/Books/Users/Foxx/FoxxRepository.mdpp @@ -146,6 +146,7 @@ attributes are required though): * *license*: Short form of the license (MIT, GPL...) * *name*: Name of the application (Meta information) * *repository*: An object with information about where you can find the repository: *type* and *url* +* *rootElement*: Do you want to have a root element in the requests bodies? Default is false. * *setup*: Path to a setup script * *teardown*: Path to a teardown script * *thumbnail*: Path to a thumbnail that represents the application (Meta information) @@ -528,4 +529,4 @@ When the TemplateMiddleware is included, you will have access to the render func ### Render @copydetails JSF_foxx_TemplateMiddleware_response_render ---> \ No newline at end of file +--> diff --git a/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp b/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp index 1b6f83973d..01793a6c5a 100644 --- a/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp +++ b/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp @@ -78,7 +78,23 @@ alternative call: _key: "123" }; ``` +!SUBSECTION Orphan Collections +Each graph has an orphan collection. It consists of arbitrary many vertex collection (type *document*), that are not +used in an edge definition of the graph. If the graph is extended with an edge definition, which is part of the orphan +collection, it will be removed from the orphan collection automatically. + +!SUBSUBSECTION Add + + + +!SUBSUBSECTION Read + + + +!SUBSUBSECTION Remove + + !SUBSECTION Read a graph diff --git a/Installation/ARM/rc.arangodb b/Installation/ARM/rc.arangodb index c48eaa7941..ed3afc8494 100644 --- a/Installation/ARM/rc.arangodb +++ b/Installation/ARM/rc.arangodb @@ -39,11 +39,11 @@ start () { fi if [ "$1" = "--upgrade" ]; then - $DAEMON -c $CONF --uid arangodb --gid arangodb $@ + $DAEMON -c $CONF --uid arangodb --gid arangodb --no-server $@ RETVAL=$? log_end_msg $RETVAL else - $DAEMON -c $CONF --uid arangodb --gid arangodb --check-version + $DAEMON -c $CONF --uid arangodb --gid arangodb --no-server --check-version RETVAL=$? if test $RETVAL -eq 0; then diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 04d6b2a7ca..752303515e 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -721,6 +721,27 @@ int ArangoServer::startupServer () { if (_applicationServer->programOptions().has("no-server")) { startServer = false; } + + // check version + bool checkVersion = false; + + if (_applicationServer->programOptions().has("check-version")) { + checkVersion = true; + } + + // run upgrade script + bool performUpgrade = false; + + if (_applicationServer->programOptions().has("upgrade")) { + performUpgrade = true; + } + + // skip an upgrade even if VERSION is missing + bool skipUpgrade = false; + + if (_applicationServer->programOptions().has("no-upgrade")) { + skipUpgrade = true; + } // special treatment for the write-ahead log // the log must exist before all other server operations can start @@ -747,7 +768,7 @@ int ArangoServer::startupServer () { LOG_WARNING("no shutdown info found. scanning datafiles for last tick..."); } - openDatabases(iterateMarkersOnOpen); + openDatabases(checkVersion, performUpgrade, iterateMarkersOnOpen); if (! wal::LogfileManager::instance()->open()) { LOG_FATAL_AND_EXIT("Unable to finish WAL recovery procedure"); @@ -781,18 +802,6 @@ int ArangoServer::startupServer () { _applicationV8->setVocbase(vocbase); _applicationV8->setConcurrency(concurrency); - bool performUpgrade = false; - - if (_applicationServer->programOptions().has("upgrade")) { - performUpgrade = true; - } - - // skip an upgrade even if VERSION is missing - bool skipUpgrade = false; - - if (_applicationServer->programOptions().has("no-upgrade")) { - skipUpgrade = true; - } // ............................................................................. // prepare everything @@ -818,7 +827,7 @@ int ArangoServer::startupServer () { _applicationServer->prepare2(); // run version check - if (_applicationServer->programOptions().has("check-version")) { + if (checkVersion) { _applicationV8->runUpgradeCheck(); } @@ -1065,7 +1074,9 @@ int ArangoServer::runScript (TRI_vocbase_t* vocbase) { /// @brief opens all databases //////////////////////////////////////////////////////////////////////////////// -void ArangoServer::openDatabases (bool iterateMarkersOnOpen) { +void ArangoServer::openDatabases (bool checkVersion, + bool performUpgrade, + bool iterateMarkersOnOpen) { TRI_vocbase_defaults_t defaults; // override with command-line options @@ -1093,10 +1104,13 @@ void ArangoServer::openDatabases (bool iterateMarkersOnOpen) { LOG_FATAL_AND_EXIT("cannot create server instance: out of memory"); } - bool const isUpgrade = _applicationServer->programOptions().has("upgrade"); - res = TRI_StartServer(_server, isUpgrade); + res = TRI_StartServer(_server, checkVersion, performUpgrade); if (res != TRI_ERROR_NO_ERROR) { + if (checkVersion && res == TRI_ERROR_ARANGO_EMPTY_DATADIR) { + TRI_EXIT_FUNCTION(EXIT_SUCCESS, NULL); + } + LOG_FATAL_AND_EXIT("cannot start server: %s", TRI_errno_string(res)); } diff --git a/arangod/RestServer/ArangoServer.h b/arangod/RestServer/ArangoServer.h index 4899124825..a3c0f42463 100644 --- a/arangod/RestServer/ArangoServer.h +++ b/arangod/RestServer/ArangoServer.h @@ -154,7 +154,9 @@ namespace triagens { /// @brief opens all system databases //////////////////////////////////////////////////////////////////////////////// - void openDatabases (bool); + void openDatabases (bool, + bool, + bool); //////////////////////////////////////////////////////////////////////////////// /// @brief closes all database diff --git a/arangod/VocBase/server.cpp b/arangod/VocBase/server.cpp index b6861f790b..796d3f0be8 100644 --- a/arangod/VocBase/server.cpp +++ b/arangod/VocBase/server.cpp @@ -300,12 +300,16 @@ static int WriteServerId (char const* filename) { /// @brief read / create the server id on startup //////////////////////////////////////////////////////////////////////////////// -static int DetermineServerId (TRI_server_t* server) { +static int DetermineServerId (TRI_server_t* server, bool checkVersion) { int res; res = ReadServerId(server->_serverIdFilename); if (res == TRI_ERROR_FILE_NOT_FOUND) { + if (checkVersion) { + return TRI_ERROR_ARANGO_EMPTY_DATADIR; + } + // id file does not yet exist. now create it res = GenerateServerId(); @@ -1274,7 +1278,8 @@ static int Move14AlphaDatabases (TRI_server_t* server) { //////////////////////////////////////////////////////////////////////////////// static int InitDatabases (TRI_server_t* server, - bool isUpgrade) { + bool checkVersion, + bool performUpgrade) { TRI_ASSERT(server != nullptr); @@ -1287,7 +1292,7 @@ static int InitDatabases (TRI_server_t* server, if (names._length == 0) { char* name; - if (! isUpgrade && HasOldCollections(server)) { + if (! performUpgrade && HasOldCollections(server)) { LOG_ERROR("no databases found. Please start the server with the --upgrade option"); return TRI_ERROR_ARANGO_DATADIR_INVALID; @@ -1305,7 +1310,7 @@ static int InitDatabases (TRI_server_t* server, } } - if (res == TRI_ERROR_NO_ERROR && isUpgrade) { + if (res == TRI_ERROR_NO_ERROR && performUpgrade) { char const* systemName; TRI_ASSERT(names._length > 0); @@ -1649,7 +1654,10 @@ TRI_server_id_t TRI_GetIdServer () { //////////////////////////////////////////////////////////////////////////////// int TRI_StartServer (TRI_server_t* server, - bool isUpgrade) { + bool checkVersion, + bool performUpgrade) { + int res; + if (! TRI_IsDirectory(server->_basePath)) { LOG_ERROR("database path '%s' is not a directory", server->_basePath); @@ -1669,7 +1677,7 @@ int TRI_StartServer (TRI_server_t* server, // check that the database is not locked and lock it // ............................................................................. - int res = TRI_VerifyLockFile(server->_lockFilename); + res = TRI_VerifyLockFile(server->_lockFilename); if (res != TRI_ERROR_NO_ERROR) { LOG_ERROR("database is locked, please check the lock file '%s'", @@ -1696,7 +1704,11 @@ int TRI_StartServer (TRI_server_t* server, // read the server id // ............................................................................. - res = DetermineServerId(server); + res = DetermineServerId(server, checkVersion); + + if (res == TRI_ERROR_ARANGO_EMPTY_DATADIR) { + return res; + } if (res != TRI_ERROR_NO_ERROR) { LOG_ERROR("reading/creating server file failed: %s", @@ -1733,7 +1745,11 @@ int TRI_StartServer (TRI_server_t* server, // perform an eventual migration of the databases. // ............................................................................. - res = InitDatabases(server, isUpgrade); + res = InitDatabases(server, checkVersion, performUpgrade); + + if (res == TRI_ERROR_ARANGO_EMPTY_DATADIR) { + return res; + } if (res != TRI_ERROR_NO_ERROR) { LOG_ERROR("unable to initialise databases: %s", @@ -1748,7 +1764,7 @@ int TRI_StartServer (TRI_server_t* server, if (server->_appPath != nullptr && strlen(server->_appPath) > 0 && ! TRI_IsDirectory(server->_appPath)) { - if (! isUpgrade) { + if (! performUpgrade) { LOG_ERROR("specified --javascript.app-path directory '%s' does not exist. " "Please start again with --upgrade option to create it.", server->_appPath); @@ -1768,7 +1784,7 @@ int TRI_StartServer (TRI_server_t* server, if (server->_devAppPath != nullptr && strlen(server->_devAppPath) > 0 && ! TRI_IsDirectory(server->_devAppPath)) { - if (! isUpgrade) { + if (! performUpgrade) { LOG_ERROR("specified --javascript.dev-app-path directory '%s' does not exist. " "Please start again with --upgrade option to create it.", server->_devAppPath); @@ -1818,7 +1834,7 @@ int TRI_StartServer (TRI_server_t* server, // ............................................................................. // scan all databases - res = OpenDatabases(server, isUpgrade); + res = OpenDatabases(server, performUpgrade); if (res != TRI_ERROR_NO_ERROR) { LOG_ERROR("could not iterate over all databases: %s", @@ -2500,6 +2516,10 @@ bool TRI_MSync (int fd, return true; } +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" diff --git a/arangod/VocBase/server.h b/arangod/VocBase/server.h index dab6a80792..90b74f1790 100644 --- a/arangod/VocBase/server.h +++ b/arangod/VocBase/server.h @@ -144,7 +144,8 @@ TRI_server_id_t TRI_GetIdServer (void); //////////////////////////////////////////////////////////////////////////////// int TRI_StartServer (TRI_server_t*, - bool); + bool checkVersion, + bool performUpgrade); //////////////////////////////////////////////////////////////////////////////// /// @brief stop the server diff --git a/js/Makefile.files b/js/Makefile.files index 84eaba480f..4ed81958f0 100644 --- a/js/Makefile.files +++ b/js/Makefile.files @@ -15,7 +15,9 @@ JAVASCRIPT_BROWSER = \ js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-query-cursor.js \ js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-statement.js \ js/apps/system/aardvark/frontend/js/modules/org/arangodb/arangosh.js \ + js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js \ js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph.js \ + js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-blueprint.js \ js/apps/system/aardvark/frontend/js/modules/org/arangodb/simple-query.js \ \ js/apps/system/aardvark/frontend/js/modules/org/arangodb-common.js \ @@ -31,6 +33,7 @@ JAVASCRIPT_BROWSER = \ js/apps/system/aardvark/frontend/js/bootstrap/errors.js \ js/apps/system/aardvark/frontend/js/bootstrap/monkeypatches.js \ js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js \ + js/apps/system/aardvark/frontend/js/bootstrap/module-console.js \ \ js/apps/system/aardvark/frontend/js/client/client.js \ js/apps/system/aardvark/frontend/js/client/bootstrap/module-internal.js diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/errors.js b/js/apps/system/aardvark/frontend/js/bootstrap/errors.js index 324b9bd9e2..49167af3a6 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/errors.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/errors.js @@ -104,6 +104,7 @@ "ERROR_ARANGO_INDEX_DOCUMENT_ATTRIBUTE_MISSING" : { "code" : 1234, "message" : "index insertion warning - attribute missing in document" }, "ERROR_ARANGO_INDEX_CREATION_FAILED" : { "code" : 1235, "message" : "index creation failed" }, "ERROR_ARANGO_DATAFILE_FULL" : { "code" : 1300, "message" : "datafile full" }, + "ERROR_ARANGO_EMPTY_DATADIR" : { "code" : 1301, "message" : "server database directory is empty" }, "ERROR_REPLICATION_NO_RESPONSE" : { "code" : 1400, "message" : "no response" }, "ERROR_REPLICATION_INVALID_RESPONSE" : { "code" : 1401, "message" : "invalid response" }, "ERROR_REPLICATION_MASTER_ERROR" : { "code" : 1402, "message" : "master error" }, @@ -206,13 +207,17 @@ "ERROR_GRAPH_COULD_NOT_CHANGE_EDGE" : { "code" : 1908, "message" : "could not change edge" }, "ERROR_GRAPH_TOO_MANY_ITERATIONS" : { "code" : 1909, "message" : "too many iterations" }, "ERROR_GRAPH_INVALID_FILTER_RESULT" : { "code" : 1910, "message" : "invalid filter result" }, - "ERROR_GRAPH_COLLECTION_MULTI_USE" : { "code" : 1920, "message" : "an edge collection may only be used once in one edge definition of a graph." }, - "ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS" : { "code" : 1921, "message" : " is already used by another graph in a different edge definition." }, - "ERROR_GRAPH_CREATE_MISSING_NAME" : { "code" : 1922, "message" : "a graph name is required to create a graph." }, - "ERROR_GRAPH_CREATE_MISSING_EDGE_DEFINITION" : { "code" : 1923, "message" : "at least one edge definition is required to create a graph." }, - "ERROR_GRAPH_EDGE_COLLECTION_NOT_USED" : { "code" : 1924, "message" : "The edge collection is not used in the edge definitions." }, - "ERROR_SESSION_UNKNOWN" : { "code" : 1950, "message" : "unknown session" }, - "ERROR_SESSION_EXPIRED" : { "code" : 1951, "message" : "session expired" }, + "ERROR_GRAPH_COLLECTION_MULTI_USE" : { "code" : 1920, "message" : "multi use of edge collection in edge def" }, + "ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS" : { "code" : 1921, "message" : "edge collection already used in edge def" }, + "ERROR_GRAPH_CREATE_MISSING_NAME" : { "code" : 1922, "message" : "missing graph name" }, + "ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION" : { "code" : 1923, "message" : "malformed edge def" }, + "ERROR_GRAPH_NOT_FOUND" : { "code" : 1924, "message" : "graph not found" }, + "ERROR_GRAPH_DUPLICATE" : { "code" : 1925, "message" : "graph already exists" }, + "ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST" : { "code" : 1926, "message" : "collection does not exist" }, + "ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX" : { "code" : 1927, "message" : "not a vertex collection" }, + "ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION" : { "code" : 1928, "message" : "not in orphan collection" }, + "ERROR_SESSION_UNKNOWN" : { "code" : 1950, "message" : "unknown session" }, + "ERROR_SESSION_EXPIRED" : { "code" : 1951, "message" : "session expired" }, "SIMPLE_CLIENT_UNKNOWN_ERROR" : { "code" : 2000, "message" : "unknown client error" }, "SIMPLE_CLIENT_COULD_NOT_CONNECT" : { "code" : 2001, "message" : "could not connect to server" }, "SIMPLE_CLIENT_COULD_NOT_WRITE" : { "code" : 2002, "message" : "could not write to server" }, @@ -222,7 +227,6 @@ "ERROR_ARANGO_INDEX_BITARRAY_INSERT_ITEM_UNSUPPORTED_VALUE" : { "code" : 3413, "message" : "bitarray index insert failure - document attribute value unsupported in index" }, "ERROR_ARANGO_INDEX_BITARRAY_CREATION_FAILURE_DUPLICATE_ATTRIBUTES" : { "code" : 3415, "message" : "bitarray index creation failure - one or more index attributes are duplicated." }, "ERROR_ARANGO_INDEX_BITARRAY_CREATION_FAILURE_DUPLICATE_VALUES" : { "code" : 3417, "message" : "bitarray index creation failure - one or more index attribute values are duplicated." }, - "ERROR_APP_ALREADY_EXISTS" : { "code": 4000, "message": "App is already installed" }, "RESULT_KEY_EXISTS" : { "code" : 10000, "message" : "element not inserted into structure, because key already exists" }, "RESULT_ELEMENT_EXISTS" : { "code" : 10001, "message" : "element not inserted into structure, because it already exists" }, "RESULT_KEY_NOT_FOUND" : { "code" : 10002, "message" : "key not found in structure" }, diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js new file mode 100644 index 0000000000..c31023e380 --- /dev/null +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js @@ -0,0 +1,431 @@ +/*jslint indent: 2, maxlen: 120, vars: true, white: true, plusplus: true, nonpropdel: true, sloppy: true */ +/*global require, SYS_GETLINE, SYS_LOG, jqconsole */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief module "console" +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler +/// @author Copyright 2010-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +(function () { + // cannot use strict here as we are going to delete globals + + var exports = require("console"); + + var sprintf = require("internal").sprintf; + var inspect = require("internal").inspect; + +// ----------------------------------------------------------------------------- +// --SECTION-- Module "console" +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief group level +//////////////////////////////////////////////////////////////////////////////// + + var groupLevel = ""; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief timers +//////////////////////////////////////////////////////////////////////////////// + + var timers = { }; + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal logging +//////////////////////////////////////////////////////////////////////////////// + + var log; + + try { + // this will work when we are in arangod but not in the browser / web interface + log = SYS_LOG; + delete SYS_LOG; + } + catch (err) { + // this will work in the web interface + log = function (level, message) { + if (jqconsole) { + jqconsole.Write(message + "\n", 'jssuccess'); + } + }; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal logging with group level +//////////////////////////////////////////////////////////////////////////////// + + function logGroup (level, msg) { + 'use strict'; + + log(level, groupLevel + msg); + } + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief assert +//////////////////////////////////////////////////////////////////////////////// + + exports.assert = function (condition) { + 'use strict'; + + if (condition) { + return; + } + + var args = Array.prototype.slice.call(arguments, 1); + var msg; + + try { + msg = sprintf.apply(sprintf, args); + } + catch (err) { + msg = err + ": " + args; + } + + logGroup("error", msg); + + require('assert').ok(condition, msg); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief debug +//////////////////////////////////////////////////////////////////////////////// + + exports.debug = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + logGroup("debug", msg); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief debugLines +//////////////////////////////////////////////////////////////////////////////// + + exports.debugLines = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + var a = msg.split("\n"); + var i; + + for (i = 0; i < a.length; ++i) { + logGroup("debug", a[i]); + } + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief dir +//////////////////////////////////////////////////////////////////////////////// + + exports.dir = function (object) { + 'use strict'; + + logGroup("info", inspect(object)); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief error +//////////////////////////////////////////////////////////////////////////////// + + exports.error = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + logGroup("error", msg); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief errorLines +//////////////////////////////////////////////////////////////////////////////// + + exports.errorLines = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + var a = msg.split("\n"); + var i; + + for (i = 0; i < a.length; ++i) { + logGroup("error", a[i]); + } + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief getline +//////////////////////////////////////////////////////////////////////////////// + + if (typeof SYS_GETLINE !== "undefined") { + exports.getline = SYS_GETLINE; + delete SYS_GETLINE; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief group +//////////////////////////////////////////////////////////////////////////////// + + exports.group = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + groupLevel = groupLevel + " "; + logGroup("info", msg); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief groupCollapsed +//////////////////////////////////////////////////////////////////////////////// + + exports.groupCollapsed = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + logGroup("info", msg); + groupLevel = groupLevel + " "; + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief groupEnd +//////////////////////////////////////////////////////////////////////////////// + + exports.groupEnd = function () { + 'use strict'; + + groupLevel = groupLevel.substr(2); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief info +//////////////////////////////////////////////////////////////////////////////// + + exports.info = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + logGroup("info", msg); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief infoLines +//////////////////////////////////////////////////////////////////////////////// + + exports.infoLines = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + var a = msg.split("\n"); + var i; + + for (i = 0; i < a.length; ++i) { + logGroup("info", a[i]); + } + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief log +//////////////////////////////////////////////////////////////////////////////// + + exports.log = exports.info; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief logLines +//////////////////////////////////////////////////////////////////////////////// + + exports.logLines = exports.infoLines; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief time +//////////////////////////////////////////////////////////////////////////////// + + exports.time = function (label) { + 'use strict'; + + if (typeof label !== 'string') { + throw new Error('label must be a string'); + } + + timers[label] = Date.now(); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief timeEnd +//////////////////////////////////////////////////////////////////////////////// + + exports.timeEnd = function(label) { + 'use strict'; + + var time = timers[label]; + + if (! time) { + throw new Error('No such label: ' + label); + } + + var duration = Date.now() - time; + + delete timers[label]; + + logGroup('%s: %dms', label, duration); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief trace +//////////////////////////////////////////////////////////////////////////////// + + exports.trace = function () { + var err = new Error(); + err.name = 'trace'; + err.message = sprintf.apply(sprintf, arguments); + Error.captureStackTrace(err, exports.trace); + logGroup("info", err.stack); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief warn +//////////////////////////////////////////////////////////////////////////////// + + exports.warn = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + logGroup("warning", msg); + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief warnLines +//////////////////////////////////////////////////////////////////////////////// + + exports.warnLines = function () { + 'use strict'; + + var msg; + + try { + msg = sprintf.apply(sprintf, arguments); + } + catch (err) { + msg = err + ": " + arguments; + } + + var a = msg.split("\n"); + var i; + + for (i = 0; i < a.length; ++i) { + logGroup("warning", a[i]); + } + }; + +}()); + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint" +// End: diff --git a/js/apps/system/aardvark/frontend/js/client/bootstrap/module-internal.js b/js/apps/system/aardvark/frontend/js/client/bootstrap/module-internal.js index 4150e8c275..e845ee0f69 100644 --- a/js/apps/system/aardvark/frontend/js/client/bootstrap/module-internal.js +++ b/js/apps/system/aardvark/frontend/js/client/bootstrap/module-internal.js @@ -1,5 +1,5 @@ /*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true, nonpropdel: true */ -/*global require, ArangoConnection, print, SYS_ARANGO */ +/*global require, ArangoConnection, print, SYS_ARANGO, window */ //////////////////////////////////////////////////////////////////////////////// /// @brief module "internal" @@ -300,6 +300,32 @@ internal.output(level, ": ", msg, "\n"); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief sprintf wrapper +//////////////////////////////////////////////////////////////////////////////// + + try { + if (window) { + internal.sprintf = function (format) { + var n = arguments.length; + if (n === 0) { + return ""; + } + if (n <= 1) { + return String(format); + } + + var i = 0; + + return format.replace(/%[dfs]/, function (match) { + return String(arguments[++i]); + }); + }; + } + } + catch (err) { + } + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js new file mode 100644 index 0000000000..9ca319f715 --- /dev/null +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js @@ -0,0 +1,2863 @@ +module.define("org/arangodb/general-graph", function(exports, module) { +/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true */ +/*global require, exports, Graph, arguments */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Graph functionality +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2014 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Florian Bartels, Michael Hackstein, Guido Schwab +/// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + + +var arangodb = require("org/arangodb"), + internal = require("internal"), + ArangoCollection = arangodb.ArangoCollection, + ArangoError = arangodb.ArangoError, + db = arangodb.db, + errors = arangodb.errors, + _ = require("underscore"); + + +// ----------------------------------------------------------------------------- +// --SECTION-- module "org/arangodb/general-graph" +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief transform a string into an array. +//////////////////////////////////////////////////////////////////////////////// + + +var stringToArray = function (x) { + if (typeof x === "string") { + return [x]; + } + return _.clone(x); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks if a parameter is not defined, an empty string or an empty +// array +//////////////////////////////////////////////////////////////////////////////// + + +var isValidCollectionsParameter = function (x) { + if (!x) { + return false; + } + if (Array.isArray(x) && x.length === 0) { + return false; + } + if (typeof x !== "string" && !Array.isArray(x)) { + return false; + } + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief find or create a collection by name +//////////////////////////////////////////////////////////////////////////////// + +var findOrCreateCollectionByName = function (name, type, noCreate) { + var col = db._collection(name), + res = false; + if (col === null && !noCreate) { + if (type === ArangoCollection.TYPE_DOCUMENT) { + col = db._create(name); + } else { + col = db._createEdgeCollection(name); + } + res = true; + } else if (!(col instanceof ArangoCollection)) { + throw "<" + name + "> must be an ArangoCollection "; + } + return res; +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief find or create a collection by name +//////////////////////////////////////////////////////////////////////////////// + +var findOrCreateCollectionsByEdgeDefinitions = function (edgeDefinitions, noCreate) { + var vertexCollections = {}, + edgeCollections = {}; + edgeDefinitions.forEach(function (e) { + e.from.concat(e.to).forEach(function (v) { + findOrCreateCollectionByName(v, ArangoCollection.TYPE_DOCUMENT, noCreate); + vertexCollections[v] = db[v]; + }); + findOrCreateCollectionByName(e.collection, ArangoCollection.TYPE_EDGE, noCreate); + edgeCollections[e.collection] = db[e.collection]; + }); + return [ + vertexCollections, + edgeCollections + ]; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal function to get graphs collection +//////////////////////////////////////////////////////////////////////////////// + +var getGraphCollection = function() { + var gCol = db._graphs; + if (gCol === null || gCol === undefined) { + throw "_graphs collection does not exist."; + } + return gCol; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal function to print edge definitions in _PRINT +//////////////////////////////////////////////////////////////////////////////// + +var printEdgeDefinitions = function(defs) { + return _.map(defs, function(d) { + var out = d.collection; + out += ": ["; + out += d.from.join(", "); + out += "] -> ["; + out += d.to.join(", "); + out += "]"; + return out; + }); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal function to wrap arango collections for overwrite +//////////////////////////////////////////////////////////////////////////////// + +var wrapCollection = function(col) { + var wrapper = {}; + _.each(_.functions(col), function(func) { + wrapper[func] = function() { + return col[func].apply(col, arguments); + }; + }); + return wrapper; +}; + + +var transformExample = function(example) { + if (example === undefined) { + return {}; + } + if (typeof example === "string") { + return {_id: example}; + } + if (typeof example === "object") { + if (Array.isArray(example)) { + return _.map(example, function(e) { + if (typeof e === "string") { + return {_id: e}; + } + return e; + }); + } + return example; + } + throw "Invalid example type. Has to be String, Array or Object"; +}; + +var checkAllowsRestriction = function(colList, rest, msg) { + var unknown = []; + _.each(rest, function(r) { + if (!colList[r]) { + unknown.push(r); + } + }); + if (unknown.length > 0) { + var err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code; + err.errorMessage = msg + ": " + + unknown.join(" and ") + + " are not known to the graph"; + throw err; + } + return true; +}; + + +// ----------------------------------------------------------------------------- +// --SECTION-- module "org/arangodb/general-graph" +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- Fluent AQL Interface +// ----------------------------------------------------------------------------- + +var AQLStatement = function(query, type) { + this.query = query; + if (type) { + this.type = type; + } +}; + +AQLStatement.prototype.printQuery = function() { + return this.query; +}; + +AQLStatement.prototype.isPathQuery = function() { + return this.type === "path"; +}; + +AQLStatement.prototype.isPathVerticesQuery = function() { + return this.type === "pathVertices"; +}; + +AQLStatement.prototype.isPathEdgesQuery = function() { + return this.type === "pathEdges"; +}; + +AQLStatement.prototype.isEdgeQuery = function() { + return this.type === "edge"; +}; + +AQLStatement.prototype.isVertexQuery = function() { + return this.type === "vertex"; +}; + +AQLStatement.prototype.isNeighborQuery = function() { + return this.type === "neighbor"; +}; + +AQLStatement.prototype.allowsRestrict = function() { + return this.isEdgeQuery() + || this.isVertexQuery() + || this.isNeighborQuery(); +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- AQL Generator for fluent interface +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Starting point of the fluent interface. +/// +/// Only for internal use. +//////////////////////////////////////////////////////////////////////////////// + +var AQLGenerator = function(graph) { + this.stack = []; + this.callStack = []; + this.bindVars = { + "graphName": graph.__name + }; + this.graph = graph; + this.cursor = null; + this.lastVar = ""; + this._path = []; + this._pathVertices = []; + this._pathEdges = []; + this._getPath = false; +}; + +AQLGenerator.prototype._addToPrint = function(name) { + var args = Array.prototype.slice.call(arguments); + args.shift(); // The Name + var stackEntry = {}; + stackEntry.name = name; + if (args.length > 0 && args[0] !== undefined) { + stackEntry.params = args; + } else { + stackEntry.params = []; + } + this.callStack.push(stackEntry); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Print the call stack of this query +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype._PRINT = function(context) { + context.output = "[ GraphAQL "; + context.output += this.graph.__name; + _.each(this.callStack, function(call) { + if(context.prettyPrint) { + context.output += "\n"; + } + context.output += "."; + context.output += call.name; + context.output += "("; + var i = 0; + for(i = 0; i < call.params.length; ++i) { + if (i > 0) { + context.output += ", "; + } + internal.printRecursive(call.params[i], context); + } + context.output += ")"; + }); + context.output += " ] "; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Dispose and reset the current cursor of the query +/// +/// Only for internal use. +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype._clearCursor = function() { + if (this.cursor) { + this.cursor.dispose(); + this.cursor = null; + } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Execute the query and keep the cursor +/// +/// Only for internal use. +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype._createCursor = function() { + if (!this.cursor) { + this.cursor = this.execute(); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief General edge query, takes direction as parameter +/// +/// This will create the general AQL statement to load edges +/// connected to the vertices selected in the step before. +/// Will also bind the options into bindVars. +/// +/// Only for internal use, user gets different functions for directions +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype._edges = function(edgeExample, options) { + this._clearCursor(); + this.options = options || {}; + var ex = transformExample(edgeExample); + var edgeName = "edges_" + this.stack.length; + var query = "FOR " + edgeName + + ' IN GRAPH_EDGES(@graphName'; + if (!this.getLastVar()) { + query += ',{}'; + } else { + query += ',' + this.getLastVar(); + } + query += ',@options_' + + this.stack.length + ')'; + if (!Array.isArray(ex)) { + ex = [ex]; + } + this.options.edgeExamples = ex; + this.bindVars["options_" + this.stack.length] = this.options; + var stmt = new AQLStatement(query, "edge"); + this.stack.push(stmt); + this.lastVar = edgeName; + this._path.push(edgeName); + this._pathEdges.push(edgeName); + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_edges +/// Select all edges for the vertices selected before. +/// +/// `graph-query.edges(examples)` +/// +/// Creates an AQL statement to select all edges for each of the vertices selected +/// in the step before. +/// This will include *inbound* as well as *outbound* edges. +/// The resulting set of edges can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all edges are valid. +/// * A string, only the edge having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only edges having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All edges matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered edges: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLEdgesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.edges().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered edges by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLEdgesFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.edges({type: "married"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered edges by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLEdgesFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.edges([{type: "married"}, {type: "friend"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.edges = function(example) { + this._addToPrint("edges", example); + return this._edges(example, {direction: "any"}); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_outEdges +/// Select all outbound edges for the vertices selected before. +/// +/// `graph-query.outEdges(examples)` +/// +/// Creates an AQL statement to select all *outbound* edges for each of the vertices selected +/// in the step before. +/// The resulting set of edges can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all edges are valid. +/// * A string, only the edge having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only edges having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All edges matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered outbound edges: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLOutEdgesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.outEdges().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered outbound edges by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLOutEdgesFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.outEdges({type: "married"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered outbound edges by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLOutEdgesFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.outEdges([{type: "married"}, {type: "friend"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.outEdges = function(example) { + this._addToPrint("outEdges", example); + return this._edges(example, {direction: "outbound"}); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_inEdges +/// Select all inbound edges for the vertices selected before. +/// +/// `graph-query.inEdges(examples)` +/// +/// Creates an AQL statement to select all *inbound* edges for each of the vertices selected +/// in the step before. +/// The resulting set of edges can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all edges are valid. +/// * A string, only the edge having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only edges having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All edges matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered inbound edges: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLInEdgesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.inEdges().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered inbound edges by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLInEdgesFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.inEdges({type: "married"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered inbound edges by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLInEdgesFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices([{name: "Alice"}, {name: "Bob"}]); +/// query.inEdges([{type: "married"}, {type: "friend"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.inEdges = function(example) { + this._addToPrint("inEdges", example); + return this._edges(example, {direction: "inbound"}); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief General vertex query, takes direction as parameter +/// +/// This will create the general AQL statement to load vertices +/// connected to the edges selected in the step before. +/// Will also bind the options into bindVars. +/// +/// Only for internal use, user gets different functions for directions +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype._vertices = function(example, options) { + this._clearCursor(); + this.options = options || {}; + var ex = transformExample(example); + var vertexName = "vertices_" + this.stack.length; + var query = "FOR " + vertexName + + " IN GRAPH_VERTICES(@graphName,@vertexExample_" + + this.stack.length + ',@options_' + + this.stack.length + ')'; + this.bindVars["vertexExample_" + this.stack.length] = ex; + this.bindVars["options_" + this.stack.length] = this.options; + var stmt = new AQLStatement(query, "vertex"); + this.stack.push(stmt); + this.lastVar = vertexName; + this._path.push(vertexName); + this._pathVertices.push(vertexName); + return this; + +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_vertices +/// Select all vertices connected to the edges selected before. +/// +/// `graph-query.vertices(examples)` +/// +/// Creates an AQL statement to select all vertices for each of the edges selected +/// in the step before. +/// This includes all vertices contained in *_from* as well as *_to* attribute of the edges. +/// The resulting set of vertices can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all vertices are valid. +/// * A string, only the vertex having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only vertices having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All vertices matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered vertices: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLVerticesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.vertices().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered vertices by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLVerticesFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.vertices({name: "Alice"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered vertices by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLVerticesFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.vertices([{name: "Alice"}, {name: "Charly"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.vertices = function(example) { + this._addToPrint("vertices", example); + if (!this.getLastVar()) { + return this._vertices(example); + } + var edgeVar = this.getLastVar(); + this._vertices(example); + var vertexVar = this.getLastVar(); + var query = "FILTER " + edgeVar + + "._from == " + vertexVar + + "._id || " + edgeVar + + "._to == " + vertexVar + + "._id"; + var stmt = new AQLStatement(query); + this.stack.push(stmt); + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_fromVertices +/// Select all vertices where the edges selected before start. +/// +/// `graph-query.vertices(examples)` +/// +/// Creates an AQL statement to select the set of vertices where the edges selected +/// in the step before start at. +/// This includes all vertices contained in *_from* attribute of the edges. +/// The resulting set of vertices can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all vertices are valid. +/// * A string, only the vertex having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only vertices having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All vertices matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered starting vertices: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLFromVerticesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.fromVertices().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered starting vertices by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLFromVerticesFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.fromVertices({name: "Alice"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered starting vertices by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLFromVerticesFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.fromVertices([{name: "Alice"}, {name: "Charly"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.fromVertices = function(example) { + this._addToPrint("fromVertices", example); + if (!this.getLastVar()) { + return this._vertices(example); + } + var edgeVar = this.getLastVar(); + this._vertices(example); + var vertexVar = this.getLastVar(); + var query = "FILTER " + edgeVar + + "._from == " + vertexVar + + "._id"; + var stmt = new AQLStatement(query); + this.stack.push(stmt); + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_toVertices +/// Select all vertices targeted by the edges selected before. +/// +/// `graph-query.vertices(examples)` +/// +/// Creates an AQL statement to select the set of vertices where the edges selected +/// in the step before end in. +/// This includes all vertices contained in *_to* attribute of the edges. +/// The resulting set of vertices can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all vertices are valid. +/// * A string, only the vertex having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only vertices having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All vertices matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered starting vertices: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLToVerticesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered starting vertices by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLToVerticesFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices({name: "Alice"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered starting vertices by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLToVerticesFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices([{name: "Alice"}, {name: "Charly"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.toVertices = function(example) { + this._addToPrint("toVertices", example); + if (!this.getLastVar()) { + return this._vertices(example); + } + var edgeVar = this.getLastVar(); + this._vertices(example); + var vertexVar = this.getLastVar(); + var query = "FILTER " + edgeVar + + "._to == " + vertexVar + + "._id"; + var stmt = new AQLStatement(query); + this.stack.push(stmt); + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Get the variable holding the last result +/// +/// Only for internal use. +/// The return statement of the AQL query has to return +/// this value. +/// Also chaining has to use this variable to restrict +/// queries in the next step to only values from this set. +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.getLastVar = function() { + if (this.lastVar === "") { + return false; + } + return this.lastVar; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_path +/// The result of the query is the path to all elements. +/// +/// By defaut the result of the generated AQL query is the set of elements passing the last matches. +/// So having a `vertices()` query as the last step the result will be set of vertices. +/// Using `path()` as the last action before requesting the result +/// will modify the result such that the path required to find the set vertices is returned. +/// +/// @EXAMPLES +/// +/// Request the iteratively explored path using vertices and edges: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLPathSimple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.outEdges().toVertices().path().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// When requesting neighbors the path to these neighbors is expanded: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLPathNeighbors} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.neighbors().path().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.path = function() { + this._clearCursor(); + var statement = new AQLStatement("", "path"); + this.stack.push(statement); + return this; +}; + +AQLGenerator.prototype.pathVertices = function() { + this._clearCursor(); + var statement = new AQLStatement("", "pathVertices"); + this.stack.push(statement); + return this; +}; + +AQLGenerator.prototype.pathEdges = function() { + this._clearCursor(); + var statement = new AQLStatement("", "pathEdges"); + this.stack.push(statement); + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_neighbors +/// Select all neighbors of the vertices selected in the step before. +/// +/// `graph-query.neighbors(examples)` +/// +/// Creates an AQL statement to select all neighbors for each of the vertices selected +/// in the step before. +/// The resulting set of vertices can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all vertices are valid. +/// * A string, only the vertex having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only vertices having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All vertices matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// To request unfiltered neighbors: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLNeighborsUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.neighbors().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered neighbors by a single example: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLNeighborsFilteredSingle} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.neighbors({name: "Bob"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered neighbors by multiple examples: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLNeighborsFilteredMultiple} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.vertices([{name: "Bob"}, {name: "Charly"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.neighbors = function(vertexExample, options) { + this._addToPrint("neighbors", vertexExample, options); + var ex = transformExample(vertexExample); + var resultName = "neighbors_" + this.stack.length; + var query = "FOR " + resultName + + " IN GRAPH_NEIGHBORS(@graphName," + + this.getLastVar() + + ',@options_' + + this.stack.length + ')'; + var opts; + if (options) { + opts = _.clone(options); + } else { + opts = {}; + } + opts.neighborExamples = ex; + this.bindVars["options_" + this.stack.length] = opts; + var stmt = new AQLStatement(query, "neighbor"); + this.stack.push(stmt); + this.lastVar = resultName + ".vertex"; + this._path.push(resultName + ".path"); + this._pathVertices.push("SLICE(" + resultName + ".path.vertices, 1)"); + this._pathEdges.push(resultName + ".path.edges"); + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Get the last statement that can be restricted to collections +/// +/// Only for internal use. +/// This returnes the last statement that can be restricted to +/// specific collections. +/// Required to allow a chaining of `restrict` after `filter` for instance. +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype._getLastRestrictableStatementInfo = function() { + var i = this.stack.length - 1; + while (!this.stack[i].allowsRestrict()) { + i--; + } + return { + statement: this.stack[i], + options: this.bindVars["options_" + i] + }; +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_restrict +/// Restricts the last statement in the chain to return +/// only elements of a specified set of collections +/// +/// `graph-query.restrict(restrictions)` +/// +/// By default all collections in the graph are searched for matching elements +/// whenever vertices and edges are requested. +/// Using *restrict* after such a statement allows to restrict the search +/// to a specific set of collections within the graph. +/// Restriction is only applied to this one part of the query. +/// It does not effect earlier or later statements. +/// +/// *restrictions* can have the following values: +/// +/// * A string defining the name of one specific collection in the graph. +/// Only elements from this collection are used for matching +/// * A list of strings defining a set of collection names. +/// Elements from all collections in this set are used for matching +/// +/// @EXAMPLES +/// +/// Request all directly connected vertices unrestricted: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLUnrestricted} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.edges().vertices().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// Apply a restriction to the directly connected vertices: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLRestricted} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.edges().vertices().restrict("female").toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// Restriction of a query is only valid for collections known to the graph: +// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLRestricted} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices({name: "Alice"}); +/// query.edges().vertices().restrict(["female", "male", "products"]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.restrict = function(restrictions) { + this._addToPrint("restrict", restrictions); + this._clearCursor(); + var rest = stringToArray(restrictions); + var lastQueryInfo = this._getLastRestrictableStatementInfo(); + var lastQuery = lastQueryInfo.statement; + var opts = lastQueryInfo.options; + var restricts; + if (lastQuery.isEdgeQuery()) { + checkAllowsRestriction( + this.graph.__edgeCollections, + rest, + "edge collections" + ); + restricts = opts.edgeCollectionRestriction || []; + opts.edgeCollectionRestriction = restricts.concat(restrictions); + } else if (lastQuery.isVertexQuery() || lastQuery.isNeighborQuery()) { + checkAllowsRestriction( + this.graph.__vertexCollections, + rest, + "vertex collections" + ); + restricts = opts.vertexCollectionRestriction || []; + opts.vertexCollectionRestriction = restricts.concat(restrictions); + } + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_filter +/// Filter the result of the query +/// +/// `graph-query.filter(examples)` +/// +/// This can be used to further specfiy the expected result of the query. +/// The result set is reduced to the set of elements that matches the given *examples*. +/// +/// *examples* can have the following values: +/// +/// * A string, only the elements having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only elements having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All elements matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// Request vertices unfiltered: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLUnfilteredVertices} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// Request vertices filtered: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLFilteredVertices} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices().filter({name: "Alice"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// Request edges unfiltered: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLUnfilteredEdges} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices().outEdges().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// Request edges filtered: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLFilteredEdges} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._edges({type: "married"}); +/// query.toVertices().outEdges().filter({type: "married"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.filter = function(example) { + this._addToPrint("filter", example); + this._clearCursor(); + var ex = []; + if (Object.prototype.toString.call(example) !== "[object Array]") { + if (Object.prototype.toString.call(example) !== "[object Object]") { + throw "The example has to be an Object, or an Array"; + } + ex = [example]; + } else { + ex = example; + } + var query = "FILTER MATCHES(" + this.getLastVar() + "," + JSON.stringify(ex) + ")"; + this.stack.push(new AQLStatement(query)); + return this; +}; + +AQLGenerator.prototype.printQuery = function() { + return this.stack.map(function(stmt) { + return stmt.printQuery(); + }).join(" "); +}; + +AQLGenerator.prototype.execute = function() { + this._clearCursor(); + var query = this.printQuery(); + var bindVars = this.bindVars; + if (this.stack[this.stack.length-1].isPathQuery()) { + query += " RETURN [" + this._path + "]"; + } else if (this.stack[this.stack.length-1].isPathVerticesQuery()) { + query += " RETURN FLATTEN([" + this._pathVertices + "])"; + } else if (this.stack[this.stack.length-1].isPathEdgesQuery()) { + query += " RETURN FLATTEN([" + this._pathEdges + "])"; + } else { + query += " RETURN " + this.getLastVar(); + } + return db._query(query, bindVars, {count: true}); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_toArray +/// Returns an array containing the complete result. +/// +/// `graph-query.toArray()` +/// +/// This function executes the generated query and returns the +/// entire result as one array. +/// ToArray does not return the generated query anymore and +/// hence can only be the endpoint of a query. +/// However keeping a reference to the query before +/// executing allows to chain further statements to it. +/// +/// @EXAMPLES +/// +/// To collect the entire result of a query toArray can be used: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLToArray} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices(); +/// query.toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.toArray = function() { + this._createCursor(); + return this.cursor.toArray(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_count +/// Returns the number of returned elements if the query is executed. +/// +/// `graph-query.count()` +/// +/// This function determines the amount of elements to be expected within the result of the query. +/// It can be used at the beginning of execution of the query +/// before using *next()* or in between *next()* calls. +/// The query object maintains a cursor of the query for you. +/// *count()* does not change the cursor position. +/// +/// @EXAMPLES +/// +/// To count the number of matched elements: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLCount} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices(); +/// query.count(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.count = function() { + this._createCursor(); + return this.cursor.count(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_hasNext +/// Checks if the query has further results. +/// +/// `graph-query.neighbors(examples)` +/// +/// The generated statement maintains a cursor for you. +/// If this cursor is already present *hasNext()* will +/// use this cursors position to determine if there are +/// further results available. +/// If the query has not yet been executed *hasNext()* +/// will execute it and create the cursor for you. +/// +/// @EXAMPLES +/// +/// Start query execution with hasNext: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLHasNext} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices(); +/// query.hasNext(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// Iterate over the result as long as it has more elements: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLHasNextIteration} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.hasNext = function() { + this._createCursor(); + return this.cursor.hasNext(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_fluent_aql_next +/// Request the next element in the result. +/// +/// `graph-query.next()` +/// +/// The generated statement maintains a cursor for you. +/// If this cursor is already present *next()* will +/// use this cursors position to deliver the next result. +/// Also the cursor position will be moved by one. +/// If the query has not yet been executed *next()* +/// will execute it and create the cursor for you. +/// It will throw an error of your query has no further results. +/// +/// @EXAMPLES +/// +/// Request some elements with next: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLNext} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices(); +/// query.next(); +/// query.next(); +/// query.next(); +/// query.next(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// The cursor is recreated if the query is changed: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLNextRecreate} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// var query = g._vertices(); +/// query.next(); +/// query.edges(); +/// query.next(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + +AQLGenerator.prototype.next = function() { + this._createCursor(); + return this.cursor.next(); +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_undirectedRelationDefinition +/// +/// `general-graph._undirectedRelationDefinition(relationName, vertexCollections)` +/// *Define an undirected relation.* +/// +/// Defines an undirected relation with the name *relationName* using the +/// list of *vertexCollections*. This relation allows the user to store +/// edges in any direction between any pair of vertices within the +/// *vertexCollections*. +/// +/// @EXAMPLES +/// +/// To define simple relation with only one vertex collection: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphUndirectedRelationDefinition1} +/// var graph = require("org/arangodb/general-graph"); +/// graph._undirectedRelationDefinition("friend", "user"); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To define a relation between several vertex collections: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphUndirectedRelationDefinition2} +/// var graph = require("org/arangodb/general-graph"); +/// graph._undirectedRelationDefinition("marriage", ["female", "male"]); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + + +var _undirectedRelationDefinition = function (relationName, vertexCollections) { + + if (arguments.length < 2) { + throw "method _undirectedRelationDefinition expects 2 arguments"; + } + + if (typeof relationName !== "string" || relationName === "") { + throw " must be a not empty string"; + } + + if (!isValidCollectionsParameter(vertexCollections)) { + throw " must be a not empty string or array"; + } + + return { + collection: relationName, + from: stringToArray(vertexCollections), + to: stringToArray(vertexCollections) + }; +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// Define an directed relation. +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_directedRelationDefinition +/// +/// `general-graph._directedRelationDefinition(relationName, fromVertexCollections, toVertexCollections)` +/// *Define a directed relation.* +/// +/// The *relationName* defines the name of this relation and references to the underlying edge collection. +/// The *fromVertexCollections* is an Array of document collections holding the start vertices. +/// The *toVertexCollections* is an Array of document collections holding the target vertices. +/// Relations are only allowed in the direction from any collection in *fromVertexCollections* +/// to any collection in *toVertexCollections*. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphDirectedRelationDefinition} +/// var graph = require("org/arangodb/general-graph"); +/// graph._directedRelationDefinition("has_bought", ["Customer", "Company"], ["Groceries", "Electronics"]); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + + +var _directedRelationDefinition = function ( + relationName, fromVertexCollections, toVertexCollections) { + + if (arguments.length < 3) { + throw "method _directedRelationDefinition expects 3 arguments"; + } + + if (typeof relationName !== "string" || relationName === "") { + throw " must be a not empty string"; + } + + if (!isValidCollectionsParameter(fromVertexCollections)) { + throw " must be a not empty string or array"; + } + + if (!isValidCollectionsParameter(toVertexCollections)) { + throw " must be a not empty string or array"; + } + + return { + collection: relationName, + from: stringToArray(fromVertexCollections), + to: stringToArray(toVertexCollections) + }; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a list of edge definitions +//////////////////////////////////////////////////////////////////////////////// + + +var _edgeDefinitions = function () { + + var res = [], args = arguments; + Object.keys(args).forEach(function (x) { + res.push(args[x]); + }); + + return res; + +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extend a list of edge definitions +//////////////////////////////////////////////////////////////////////////////// + + +var _extendEdgeDefinitions = function (edgeDefinition) { + + var args = arguments, i = 0; + + Object.keys(args).forEach(function (x) { + i++; + if (i === 1) {return;} + edgeDefinition.push(args[x]); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a new graph +//////////////////////////////////////////////////////////////////////////////// + + +var _create = function (graphName, edgeDefinitions) { + + var gdb = getGraphCollection(), + err, + graphAlreadyExists = true, + collections; + if (!graphName) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_CREATE_MISSING_NAME.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MISSING_NAME.message; + throw err; + } + edgeDefinitions = edgeDefinitions || []; + if (!Array.isArray(edgeDefinitions)) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.message; + throw err; + } + //check, if a collection is already used in a different edgeDefinition + var tmpCollections = []; + var tmpEdgeDefinitions = {}; + edgeDefinitions.forEach( + function(edgeDefinition) { + var col = edgeDefinition.collection; + if (tmpCollections.indexOf(col) !== -1) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message; + throw err; + } + tmpCollections.push(col); + tmpEdgeDefinitions[col] = edgeDefinition; + } + ); + gdb.toArray().forEach( + function(singleGraph) { + var sGEDs = singleGraph.edgeDefinitions; + sGEDs.forEach( + function(sGED) { + var col = sGED.collection; + if (tmpCollections.indexOf(col) !== -1) { + if (JSON.stringify(sGED) !== JSON.stringify(tmpEdgeDefinitions[col])) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code; + err.errorMessage = col + + arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.message; + throw err; + } + } + } + ); + } + ); + + try { + var g = gdb.document(graphName); + } catch (e) { + if (e.errorNum !== errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) { + throw e; + } + graphAlreadyExists = false; + } + + if (graphAlreadyExists) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_DUPLICATE.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_DUPLICATE.message; + throw err; + } + + collections = findOrCreateCollectionsByEdgeDefinitions(edgeDefinitions, false); + + gdb.save({ + 'edgeDefinitions' : edgeDefinitions, + '_key' : graphName + }); + + return new Graph(graphName, edgeDefinitions, collections[0], collections[1]); + +}; + +var createHiddenProperty = function(obj, name, value) { + Object.defineProperty(obj, name, { + enumerable: false, + writable: true + }); + obj[name] = value; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructor. +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_vertex_collection_save +/// Creates and saves a new vertex in collection *vertexCollectionName* +/// +/// `general-graph.vertexCollectionName.save(data)` +/// +/// *data*: json - data of vertex +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVertexCollectionSave} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.male.save({name: "Floyd", _key: "floyd"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_vertex_collection_replace +/// Replaces the data of a vertex in collection *vertexCollectionName* +/// +/// `general-graph.vertexCollectionName.replace(vertexId, data, options)` +/// +/// *vertexId*: string - id of the vertex +/// *data*: json - data of vertex +/// *options*: json - (optional) - see collection documentation +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVertexCollectionReplace} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.male.save({neym: "Jon", _key: "john"}); +/// g.male.replace("male/john", {name: "John"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_vertex_collection_update +/// Updates the data of a vertex in collection *vertexCollectionName* +/// +/// `general-graph.vertexCollectionName.update(vertexId, data, options)` +/// +/// *vertexId*: string - id of the vertex +/// *data*: json - data of vertex +/// *options*: json - (optional) - see collection documentation +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVertexCollectionUpdate} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.female.save({name: "Lynda", _key: "linda"}); +/// g.female.update({name: "Linda", _key: "linda"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_vertex_collection_remove +/// Removes a vertex in collection *vertexCollectionName* +/// +/// `general-graph.vertexCollectionName.remove(vertexId, options)` +/// +/// Additionally removes all ingoing and outgoing edges of the vertex recursively +/// (see [edge remove](#edge.remove)). +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVertexCollectionRemove} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.male.save({name: "Kermit", _key: "kermit"}); +/// db._exists("male/kermit") +/// g.male.remove("male/kermit") +/// db._exists("male/kermit") +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_edge_collection_save +/// Creates and saves a new edge from vertex *from* to vertex *to* in +/// collection *edgeCollectionName* +/// +/// `general-graph.edgeCollectionName.save(from, to, data)` +/// +/// *from*: string - id of outgoing vertex +/// *to*: string - of ingoing vertex +/// *data*: json - data of edge +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeCollectionSave1} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.relation.save("male/bob", "female/alice", {type: "married", _key: "bobAndAlice"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// If the collections of *from* and *to* are not defined in an edgeDefinition of the graph, +/// the edge will not be stored. +/// +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeCollectionSave2} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.relation.save("relation/aliceAndBob", "female/alice", {type: "married", _key: "bobAndAlice"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_edge_collection_replace +/// Replaces the data of an edge in collection *edgeCollectionName* +/// +/// `general-graph.edgeCollectionName.replace(edgeId, data, options)` +/// +/// *edgeId*: string - id of the edge +/// *data*: json - data of edge +/// *options*: json - (optional) - see collection documentation +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeCollectionReplace} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.relation.save("female/alice", "female/diana", {typo: "nose", _key: "aliceAndDiana"}); +/// g.relation.replace("relation/aliceAndDiana", {type: "knows"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_edge_collection_update +/// Updates the data of an edge in collection *edgeCollectionName* +/// +/// `general-graph.edgeCollectionName.update(edgeId, data, options)` +/// +/// *edgeId*: string - id of the edge +/// *data*: json - data of edge +/// *options*: json - (optional) - see collection documentation +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeCollectionUpdate} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.relation.save("female/alice", "female/diana", {type: "knows", _key: "aliceAndDiana"}); +/// g.relation.update("relation/aliceAndDiana", {type: "quarrelled", _key: "aliceAndDiana"}); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_edge_collection_remove +/// Removes an edge in collection *edgeCollectionName* +/// +/// `general-graph.edgeCollectionName.remove(edgeId, options)` +/// +/// If this edge is used as a vertex by another edge, the other edge will be removed (recursively). +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeCollectionRemove} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g.relation.save("female/alice", "female/diana", {_key: "aliceAndDiana"}); +/// db._exists("relation/aliceAndDiana") +/// g.relation.remove("relation/aliceAndDiana") +/// db._exists("relation/aliceAndDiana") +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollections) { + var self = this; + // Create Hidden Properties + createHiddenProperty(this, "__name", graphName); + createHiddenProperty(this, "__vertexCollections", vertexCollections); + createHiddenProperty(this, "__edgeCollections", edgeCollections); + createHiddenProperty(this, "__edgeDefinitions", edgeDefinitions); + createHiddenProperty(this, "__idsToRemove", []); + createHiddenProperty(this, "__collectionsToLock", []); + createHiddenProperty(this, "__orphanCollections", []); + + // fills this.__idsToRemove and this.__collectionsToLock + var removeEdge = function (edgeId, options) { + options = options || {}; + var edgeCollection = edgeId.split("/")[0]; + var graphs = getGraphCollection().toArray(); + self.__idsToRemove.push(edgeId); + self.__collectionsToLock.push(edgeCollection); + graphs.forEach( + function(graph) { + var edgeDefinitions = graph.edgeDefinitions; + if (graph.edgeDefinitions) { + edgeDefinitions.forEach( + function(edgeDefinition) { + var from = edgeDefinition.from; + var to = edgeDefinition.to; + var collection = edgeDefinition.collection; + // if collection of edge to be deleted is in from or to + if (from.indexOf(edgeCollection) !== -1 || to.indexOf(edgeCollection) !== -1) { + //search all edges of the graph + var edges = db[collection].toArray(); + edges.forEach( + function (edge) { + // if from is + if(self.__idsToRemove.indexOf(edge._id) === -1) { + if (edge._from === edgeId || edge._to === edgeId) { + removeEdge(edge._id, options); + } + } + } + ); + } + } + ); + } + } + ); + return; + }; + + + _.each(vertexCollections, function(obj, key) { + var result; + var wrap = wrapCollection(obj); + var old_remove = wrap.remove; + wrap.remove = function(vertexId, options) { + //delete all edges using the vertex in all graphs + var graphs = getGraphCollection().toArray(); + var vertexCollectionName = vertexId.split("/")[0]; + self.__collectionsToLock.push(vertexCollectionName); + graphs.forEach( + function(graph) { + var edgeDefinitions = graph.edgeDefinitions; + if (graph.edgeDefinitions) { + edgeDefinitions.forEach( + function(edgeDefinition) { + var from = edgeDefinition.from; + var to = edgeDefinition.to; + var collection = edgeDefinition.collection; + if (from.indexOf(vertexCollectionName) !== -1 + || to.indexOf(vertexCollectionName) !== -1 + ) { + var edges = db._collection(collection).toArray(); + edges.forEach( + function(edge) { + if (edge._from === vertexId || edge._to === vertexId) { + removeEdge(edge._id, options); + } + } + ); + } + } + ); + } + } + ); + + try { + db._executeTransaction({ + collections: { + write: self.__collectionsToLock + }, + action: function (params) { + var db = require("internal").db; + params.ids.forEach( + function(edgeId) { + if (params.options) { + db._remove(edgeId, params.options); + } else { + db._remove(edgeId); + } + } + ); + if (params.options) { + db._remove(params.vertexId, params.options); + } else { + db._remove(params.vertexId); + } + }, + params: { + ids: self.__idsToRemove, + options: options, + vertexId: vertexId + } + }); + result = true; + } catch (e) { + result = false; + } + self.__idsToRemove = []; + self.__collectionsToLock = []; + + return result; + }; + + self[key] = wrap; + }); + + _.each(edgeCollections, function(obj, key) { + var wrap = wrapCollection(obj); + // save + var old_save = wrap.save; + wrap.save = function(from, to, data) { + //check, if edge is allowed + edgeDefinitions.forEach( + function(edgeDefinition) { + if (edgeDefinition.collection === key) { + var fromCollection = from.split("/")[0]; + var toCollection = to.split("/")[0]; + if (! _.contains(edgeDefinition.from, fromCollection) + || ! _.contains(edgeDefinition.to, toCollection)) { + throw "Edge is not allowed between " + from + " and " + to + "."; + } + } + } + ); + return old_save(from, to, data); + }; + + // remove + wrap.remove = function(edgeId, options) { + var result; + //if _key make _id (only on 1st call) + if (edgeId.indexOf("/") === -1) { + edgeId = key + "/" + edgeId; + } + removeEdge(edgeId, options); + + try { + db._executeTransaction({ + collections: { + write: self.__collectionsToLock + }, + action: function (params) { + var db = require("internal").db; + params.ids.forEach( + function(edgeId) { + if (params.options) { + db._remove(edgeId, params.options); + } else { + db._remove(edgeId); + } + } + ); + }, + params: { + ids: self.__idsToRemove, + options: options + } + }); + result = true; + } catch (e) { + result = false; + } + self.__idsToRemove = []; + self.__collectionsToLock = []; + return result; + }; + + self[key] = wrap; + }); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief load a graph. +//////////////////////////////////////////////////////////////////////////////// + +var _graph = function(graphName) { + + var gdb = getGraphCollection(), + g, collections; + + try { + g = gdb.document(graphName); + } + catch (e) { + if (e.errorNum !== errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) { + throw e; + } + var err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message; + throw err; + } + + collections = findOrCreateCollectionsByEdgeDefinitions(g.edgeDefinitions, true); + + return new Graph(graphName, g.edgeDefinitions, collections[0], collections[1]); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief check if a graph exists. +//////////////////////////////////////////////////////////////////////////////// + +var _exists = function(graphId) { + var gCol = getGraphCollection(); + return gCol.exists(graphId); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Helper for dropping collections of a graph. +//////////////////////////////////////////////////////////////////////////////// + +var checkIfMayBeDropped = function(colName, graphName, graphs) { + var result = true; + graphs.forEach( + function(graph) { + if (graph._key === graphName) { + return; + } + var edgeDefinitions = graph.edgeDefinitions; + if (graph.edgeDefinitions) { + edgeDefinitions.forEach( + function(edgeDefinition) { + var from = edgeDefinition.from; + var to = edgeDefinition.to; + var collection = edgeDefinition.collection; + if (collection === colName + || from.indexOf(colName) !== -1 + || to.indexOf(colName) !== -1 + ) { + result = false; + } + } + ); + } + var orphanCollections = graph.orphanCollections; + if (orphanCollections) { + if (orphanCollections.indexOf(colName) !== -1) { + return false; + } + } + } + ); + + return result; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief drop a graph. +//////////////////////////////////////////////////////////////////////////////// + +var _drop = function(graphId, dropCollections) { + + var gdb = getGraphCollection(); + + if (!gdb.exists(graphId)) { + var err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_FOUND.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_FOUND.message; + throw err; + } + + if (dropCollections !== false) { + var graph = gdb.document(graphId); + var edgeDefinitions = graph.edgeDefinitions; + edgeDefinitions.forEach( + function(edgeDefinition) { + var from = edgeDefinition.from; + var to = edgeDefinition.to; + var collection = edgeDefinition.collection; + var graphs = getGraphCollection().toArray(); + if (checkIfMayBeDropped(collection, graph._key, graphs)) { + db._drop(collection); + } + from.forEach( + function(col) { + if (checkIfMayBeDropped(col, graph._key, graphs)) { + db._drop(col); + } + } + ); + to.forEach( + function(col) { + if (checkIfMayBeDropped(col, graph._key, graphs)) { + db._drop(col); + } + } + ); + } + ); + } + + gdb.remove(graphId); + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return all edge collections of the graph. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._edgeCollections = function() { + return _.values(this.__edgeCollections); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return all vertex collections of the graph. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._vertexCollections = function() { + return _.values(this.__vertexCollections); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief _EDGES(vertexId). +//////////////////////////////////////////////////////////////////////////////// + +// might be needed from AQL itself +Graph.prototype._EDGES = function(vertexId) { + if (vertexId.indexOf("/") === -1) { + throw vertexId + " is not a valid id"; + } + var collection = vertexId.split("/")[0]; + if (!db._collection(collection)) { + throw collection + " does not exist."; + } + + var edgeCollections = this._edgeCollections(); + var result = []; + + edgeCollections.forEach( + function(edgeCollection) { + result = result.concat(edgeCollection.edges(vertexId)); + } + ); + return result; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief INEDGES(vertexId). +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._INEDGES = function(vertexId) { + if (vertexId.indexOf("/") === -1) { + throw vertexId + " is not a valid id"; + } + var collection = vertexId.split("/")[0]; + if (!db._collection(collection)) { + throw collection + " does not exist."; + } + + var edgeCollections = this._edgeCollections(); + var result = []; + + + edgeCollections.forEach( + function(edgeCollection) { + result = result.concat(edgeCollection.inEdges(vertexId)); + } + ); + return result; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief outEdges(vertexId). +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._OUTEDGES = function(vertexId) { + if (vertexId.indexOf("/") === -1) { + throw vertexId + " is not a valid id"; + } + var collection = vertexId.split("/")[0]; + if (!db._collection(collection)) { + throw collection + " does not exist."; + } + + var edgeCollections = this._edgeCollections(); + var result = []; + + + edgeCollections.forEach( + function(edgeCollection) { + result = result.concat(edgeCollection.outEdges(vertexId)); + } + ); + return result; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_edges +/// Select some edges from the graph. +/// +/// `graph.edges(examples)` +/// +/// Creates an AQL statement to select a subset of the edges stored in the graph. +/// This is one of the entry points for the fluent AQL interface. +/// It will return a mutable AQL statement which can be further refined, using the +/// functions described below. +/// The resulting set of edges can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all edges are valid. +/// * A string, only the edge having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only edges having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All edges matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// In the examples the *toArray* function is used to print the result. +/// The description of this module can be found below. +/// +/// To request unfiltered edges: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g._edges().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered edges: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgesFiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g._edges({type: "married"}).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._edges = function(edgeExample) { + var AQLStmt = new AQLGenerator(this); + // If no direction is specified all edges are duplicated. + // => For initial requests a direction has to be set + return AQLStmt.outEdges(edgeExample); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_vertices +/// Select some vertices from the graph. +/// +/// `graph.vertices(examples)` +/// +/// Creates an AQL statement to select a subset of the vertices stored in the graph. +/// This is one of the entry points for the fluent AQL interface. +/// It will return a mutable AQL statement which can be further refined, using the +/// functions described below. +/// The resulting set of edges can be filtered by defining one or more *examples*. +/// +/// *examples* can have the following values: +/// +/// * Empty, there is no matching executed all vertices are valid. +/// * A string, only the vertex having this value as it's id is returned. +/// * An example object, defining a set of attributes. +/// Only vertices having these attributes are matched. +/// * A list containing example objects and/or strings. +/// All vertices matching at least one of the elements in the list are returned. +/// +/// @EXAMPLES +/// +/// In the examples the *toArray* function is used to print the result. +/// The description of this module can be found below. +/// +/// To request unfiltered vertices: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVerticesUnfiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g._vertices().toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To request filtered vertices: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVerticesFiltered} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g._vertices([{name: "Alice"}, {name: "Bob"}]).toArray(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._vertices = function(example) { + var AQLStmt = new AQLGenerator(this); + return AQLStmt.vertices(example); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_getFromVertex +/// Get the vertex of an edge defined as *_from* +/// +/// `general-graph._getFromVertex(edgeId)` +/// +/// Returns the vertex defined with the attribute *_from* of the edge with *edgeId* as its *_id*. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphGetFromVertex} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g._getFromVertex("relation/aliceAndBob") +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._getFromVertex = function(edgeId) { + var edgeCollection = this._getEdgeCollectionByName(edgeId.split("/")[0]); + var document = edgeCollection.document(edgeId); + if (document) { + var vertexId = document._from; + var vertexCollection = this._getVertexCollectionByName(vertexId.split("/")[0]); + return vertexCollection.document(vertexId); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_getToVertex +/// Get the vertex of an edge defined as *_to* +/// +/// `general-graph._getToVertex(edgeId)` +/// +/// Returns the vertex defined with the attribute *_to* of the edge with *edgeId* as its *_id*. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphGetToVertex} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var g = examples.loadGraph("social"); +/// g._getToVertex("relation/aliceAndBob") +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._getToVertex = function(edgeId) { + var edgeCollection = this._getEdgeCollectionByName(edgeId.split("/")[0]); + var document = edgeCollection.document(edgeId); + if (document) { + var vertexId = document._to; + var vertexCollection = this._getVertexCollectionByName(vertexId.split("/")[0]); + return vertexCollection.document(vertexId); + } +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get edge collection by name. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._getEdgeCollectionByName = function(name) { + if (this.__edgeCollections[name]) { + return this.__edgeCollections[name]; + } + throw "Collection " + name + " does not exist in graph."; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get vertex collection by name. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._getVertexCollectionByName = function(name) { + if (this.__vertexCollections[name]) { + return this.__vertexCollections[name]; + } + throw "Collection " + name + " does not exist in graph."; +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get common neighbors of two vertices in the graph. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._listCommonNeighbors = function(vertex1Example, vertex2Example, optionsVertex1, optionsVertex2) { + + var ex1 = transformExample(vertex1Example); + var ex2 = transformExample(vertex2Example); + var query = "FOR e" + + " IN GRAPH_COMMON_NEIGHBORS(@graphName" + + ',@ex1' + + ',@ex2' + + ',@options1' + + ',@options2' + + ') SORT ATTRIBUTES(e)[0] RETURN e'; + optionsVertex1 = optionsVertex1 || {}; + optionsVertex2 = optionsVertex2 || {}; + var bindVars = { + "graphName": this.__name, + "options1": optionsVertex1, + "options2": optionsVertex2, + "ex1": ex1, + "ex2": ex2 + }; + return db._query(query, bindVars, {count: true}).toArray(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get amount of common neighbors of two vertices in the graph. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._amountCommonNeighbors = function(vertex1Example, vertex2Example, optionsVertex1, optionsVertex2) { + var ex1 = transformExample(vertex1Example); + var ex2 = transformExample(vertex2Example); + var query = "FOR e" + + " IN GRAPH_COMMON_NEIGHBORS(@graphName" + + ',@ex1' + + ',@ex2' + + ',@options1' + + ',@options2' + + ') FOR a in ATTRIBUTES(e) FOR b in ATTRIBUTES(e[a]) ' + + 'SORT ATTRIBUTES(e)[0] RETURN [a, b, LENGTH(e[a][b]) ]'; + optionsVertex1 = optionsVertex1 || {}; + optionsVertex2 = optionsVertex2 || {}; + var bindVars = { + "graphName": this.__name, + "options1": optionsVertex1, + "options2": optionsVertex2, + "ex1": ex1, + "ex2": ex2 + }; + var result = db._query(query, bindVars, {count: true}).toArray(), + tmp = {}, tmp2={}, returnHash = []; + result.forEach(function (r) { + if (!tmp[r[0]]) { + tmp[r[0]] = []; + } + tmp2 = {}; + tmp2[r[1]] = r[2]; + tmp[r[0]].push(tmp2); + }); + Object.keys(tmp).forEach(function(w) { + tmp2 = {}; + tmp2[w] = tmp[w]; + returnHash.push(tmp2); + }); + return returnHash; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get common properties of two vertices in the graph. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._listCommonProperties = function(vertex1Example, vertex2Example, options) { + + var ex1 = transformExample(vertex1Example); + var ex2 = transformExample(vertex2Example); + var query = "FOR e" + + " IN GRAPH_COMMON_PROPERTIES(@graphName" + + ',@ex1' + + ',@ex2' + + ',@options' + + ') SORT ATTRIBUTES(e)[0] RETURN e'; + options = options || {}; + var bindVars = { + "graphName": this.__name, + "options": options, + "ex1": ex1, + "ex2": ex2 + }; + return db._query(query, bindVars, {count: true}).toArray(); + +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get amount of common properties of two vertices in the graph. +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._amountCommonProperties = function(vertex1Example, vertex2Example, options) { + var ex1 = transformExample(vertex1Example); + var ex2 = transformExample(vertex2Example); + var query = "FOR e" + + " IN GRAPH_COMMON_PROPERTIES(@graphName" + + ',@ex1' + + ',@ex2' + + ',@options' + + ') FOR a in ATTRIBUTES(e) SORT ATTRIBUTES(e)[0] RETURN [ ATTRIBUTES(e)[0], LENGTH(e[a]) ]'; + options = options || {}; + var bindVars = { + "graphName": this.__name, + "options": options, + "ex1": ex1, + "ex2": ex2 + }; + var result = db._query(query, bindVars, {count: true}).toArray(), returnHash = []; + result.forEach(function (r) { + var tmp = {}; + tmp[r[0]] = r[1]; + returnHash.push(tmp); + }); + return returnHash; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__extendEdgeDefinitions +/// Extends the edge definitions of a graph. If the edge collection of the edge definition +/// to add is already used in the graph or used in a different graph with different from +/// an to collections an error is thrown. +/// +/// `general-graph._extendEdgeDefinitions(edgeDefinition)` +/// +/// *edgeDefinition* - [string] : the edge definition to extend the graph +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__extendEdgeDefinitions} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = examples._directedRelationDefinition("myEC2", ["myVC1"], ["myVC3"]); +/// var g = examples._create("myGraph", [ed1]); +/// g._extendEdgeDefinitions(ed2); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) { + var self = this; + var err; + //check if edgeCollection not already used + var eC = edgeDefinition.collection; + // ... in same graph + if (this.__edgeCollections[eC] !== undefined) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_MULTI_USE.message; + throw err; + } + //in different graph + db._graphs.toArray().forEach( + function(singleGraph) { + var sGEDs = singleGraph.edgeDefinitions; + sGEDs.forEach( + function(sGED) { + var col = sGED.collection; + if (col === eC) { + if (JSON.stringify(sGED) !== JSON.stringify(edgeDefinition)) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code; + err.errorMessage = col + + arangodb.errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.message; + throw err; + } + } + } + ); + } + ); + + findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]); + + this.__edgeDefinitions.push(edgeDefinition); + this.__edgeCollections[edgeDefinition.collection] = db[edgeDefinition.collection]; + edgeDefinition.from.forEach( + function(vc) { + if (self.__vertexCollections[vc] === undefined) { + self.__vertexCollections[vc] = db[vc]; + } + } + ); + edgeDefinition.to.forEach( + function(vc) { + if (self.__vertexCollections[vc] === undefined) { + self.__vertexCollections[vc] = db[vc]; + } + } + ); + +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__editEdgeDefinition +/// Edits the edge definitions of a graph. The edge definition used as argument will +/// replace the existing edge definition the corresponding edge definition in graphs +/// edge definitions. Other graphs with the same edge definition will be modified, too. +/// +/// `general-graph._editEdgeDefinition(edgeDefinition, dropCollections)` +/// +/// *edgeDefinition* - [string] : the edge definition to replace the existing edge +/// definition with the same attribut *collection*. +/// *dropCollections* - bool : True, all collections that are not used anymore in any +/// graph will be removed. Default: true. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__editEdgeDefinition} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = examples._directedRelationDefinition("myEC1", ["myVC2"], ["myVC3"]); +/// var g = examples._create("myGraph", [ed1, ed2]); +/// g._editEdgeDefinition(ed2, true); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// +Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) { + var self = this; + var dropCandidates; + var currentEdgeDefinition = {}; + + + //check, if in graphs edge definition + if (this.__edgeCollections[edgeDefinition.collection] === undefined) { + var err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message; + throw err; + } + + //change definition for ALL graphs + var graphs = getGraphCollection().toArray(); + graphs.forEach( + function(graph) { + var eDs = graph.edgeDefinitions; + eDs.forEach( + function(eD, id) { + if(eD.collection === edgeDefinition.collection) { + currentEdgeDefinition.from = eD.from; + currentEdgeDefinition.to = eD.to; + eDs[id].from = edgeDefinition.from; + eDs[id].to = edgeDefinition.to; + db._graphs.update(graph._key, {edgeDefinitions: eDs}); + if (graph._key === self.__name) { + self.__edgeDefinitions[id].from = edgeDefinition.from; + self.__edgeDefinitions[id].to = edgeDefinition.to; + } + } + } + ); + } + ); + + findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]); + + if (dropCollections !== false) { + graphs = getGraphCollection().toArray(); + //eval collection to be dropped + dropCandidates = currentEdgeDefinition.from; + currentEdgeDefinition.to.forEach( + function (col) { + if (dropCandidates.indexOf(col) === -1) { + dropCandidates.push(col); + } + } + ); + dropCandidates.forEach( + function(dc) { + if (checkIfMayBeDropped(dc, null, graphs)) { + db._drop(dc); + } + } + ); + } + + + //push "new" collections into vertexCollections + edgeDefinition.from.forEach( + function(vc) { + if (self.__vertexCollections[vc] === undefined) { + self.__vertexCollections[vc] = db[vc]; + } + } + ); + edgeDefinition.to.forEach( + function(vc) { + if (self.__vertexCollections[vc] === undefined) { + self.__vertexCollections[vc] = db[vc]; + } + } + ); + + //remove "old" collections from vertexCollections + dropCandidates.forEach( + function(dropCanditate) { + var drop = true; + self.__edgeDefinitions.forEach( + function(eD) { + eD.from.forEach( + function(vC) { + if (vC === dropCanditate) { + drop = false; + } + } + ); + } + ); + if (drop) { + delete self.__vertexCollections[dropCanditate]; + } + } + ); + +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__deleteEdgeDefinition +/// Deletes an edge definition defined by the edge collection of a graph. If the +/// collections defined in the edge definition (collection, from, to) are not used +/// in another graph, they will be removed. +/// +/// `general-graph._deleteEdgeDefinition(edgeCollectionName, dropCollections)` +/// +/// *edgeCollectionName* - string : name of edge collection defined in *collection* of the edge +/// definition. +/// *dropCollections* - bool : True, all collections are removed, if not used in another edge +/// definition (including other graphs). Default: true. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__deleteEdgeDefinition} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = examples._directedRelationDefinition("myEC2", ["myVC1"], ["myVC3"]); +/// var g = examples._create("myGraph", [ed1, ed2]); +/// g._deleteEdgeDefinition("myEC1", true); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections) { + var edgeDefinitions = this.__edgeDefinitions, + vertexCollections = [], + definitionFound = false, + index; + + edgeDefinitions.forEach( + function(edgeDefinition, idx) { + if (edgeDefinition.collection === edgeCollection) { + definitionFound = true; + if (dropCollections !== false) { + //get all vertex collections + var vertexCols = edgeDefinition.from.concat(edgeDefinition.to); + vertexCols.forEach( + function(vertexCol) { + if (vertexCollections.indexOf(vertexCol) === -1) { + vertexCollections.push(vertexCol); + } + } + ); + } + } + } + ); + if (definitionFound) { + edgeDefinitions.splice(index, 1); + this.__edgeDefinitions = edgeDefinitions; + db._graphs.update(this.__name, {edgeDefinitions: this.__edgeDefinitions}); + } + if (dropCollections !== false) { + if (checkIfMayBeDropped(edgeCollection, this.__name, getGraphCollection().toArray())) { + db._drop(edgeCollection); + } + vertexCollections.forEach( + function(vC) { + if (checkIfMayBeDropped(vC, this.__name, getGraphCollection().toArray())) { + db._drop(vC); + } + } + ); + + } + +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__addOrphanCollection +/// Adds a vertex collection to the set of orphan collections of the graph. If the +/// collection does not exist, it will be created. +/// +/// `general-graph._addOrphanCollection(orphanCollectionName, createCollection)` +/// +/// *orphanCollectionName* - string : name of vertex collection. +/// *createCollection* - bool : if true the collection will be created if it does not exist. Default: true. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__addOrphanCollection} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = examples._create("myGraph", [ed1, ed2]); +/// g._addOrphanCollection("myVC3", true); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._addOrphanCollection = function(vertexCollection, createCollection) { + //check edgeCollection + var ec = db._collection(vertexCollection); + var err; + if (ec === null) { + if (createCollection !== false) { + db._create(vertexCollection); + } else { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code; + err.errorMessage = vertexCollection + arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message; + throw err; + } + } else if (ec.type() !== 2) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message; + throw err; + } + + this.__orphanCollections.push(vertexCollection); + db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections}); + +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__getOrphanCollections +/// Returns all vertex collections of the graph, that are not used in an edge definition. +/// +/// `general-graph._getOrphanCollections()` +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__getOrphanCollections} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = examples._create("myGraph", [ed1]); +/// g._addOrphanCollection("myVC3, true); +/// g._getOrphanCollections(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._getOrphanCollections = function() { + return this.__orphanCollections; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__removeOrphanCollection +/// Removes an orphan collection from the graph and deletes the collection, if it is not +/// used in any graph. +/// +/// `general-graph._removeOrphanCollection()` +/// +/// *orphanCollectionName* - string : name of vertex collection. +/// *dropCollection* - bool : if true the collection will be dropped if it is not used in any graph. +/// Default: true. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__getOrphanCollections} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = examples._create("myGraph", [ed1]); +/// g._addOrphanCollection("myVC3, true); +/// g._addOrphanCollection("myVC4, true); +/// g._getOrphanCollections(); +/// g._removeOrphanCollection("myVC3"); +/// g._getOrphanCollections(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._removeOrphanCollection = function(orphanCollectionName, dropCollection) { + var err; + if (db._collection(orphanCollectionName) === null) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message; + throw err; + } + var index = this.__orphanCollections.indexOf(orphanCollectionName); + if (index === -1) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.message; + throw err; + } + this.__orphanCollections.splice(index, 1); + + if (dropCollection !== false) { + var graphs = getGraphCollection().toArray(); + if (checkIfMayBeDropped(orphanCollectionName, null, graphs)) { + db._drop(orphanCollectionName); + } + } +}; + + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief print basic information for the graph +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._PRINT = function(context) { + var name = this.__name; + var edgeDefs = printEdgeDefinitions(this.__edgeDefinitions); + context.output += "[ Graph "; + context.output += name; + context.output += " EdgeDefinitions: "; + internal.printRecursive(edgeDefs, context); + context.output += " ]"; +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- MODULE EXPORTS +// ----------------------------------------------------------------------------- + +exports._undirectedRelationDefinition = _undirectedRelationDefinition; +exports._directedRelationDefinition = _directedRelationDefinition; +exports._graph = _graph; +exports._edgeDefinitions = _edgeDefinitions; +exports._extendEdgeDefinitions = _extendEdgeDefinitions; +exports._create = _create; +exports._drop = _drop; +exports._exists = _exists; + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: +}); diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-blueprint.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-blueprint.js new file mode 100644 index 0000000000..b1e27e7da7 --- /dev/null +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/graph-blueprint.js @@ -0,0 +1,432 @@ +module.define("org/arangodb/graph-blueprint", function(exports, module) { +/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */ +/*global require, exports */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Graph functionality +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler, Lucas Dohmen +/// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var arangodb = require("org/arangodb"), + is = require("org/arangodb/is"), + common = require("org/arangodb/graph-common"), + Edge = common.Edge, + Graph = common.Graph, + Vertex = common.Vertex, + GraphArray = common.GraphArray, + Iterator = common.Iterator, + GraphAPI = require ("org/arangodb/api/graph").GraphAPI; + + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoGraph +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief changes a property of an edge +//////////////////////////////////////////////////////////////////////////////// + +Edge.prototype.setProperty = function (name, value) { + var results, + update = this._properties; + + update[name] = value; + this._graph.emptyCachedPredecessors(); + + results = GraphAPI.putEdge(this._graph._properties._key, this._properties._key, update); + + this._properties = results.edge; + + return name; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- Vertex +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoGraph +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a new vertex object +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoGraph +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inbound and outbound edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.edges = function (direction, labels) { + var edge, + edges = new GraphArray(), + cursor; + + cursor = GraphAPI.postEdges(this._graph._vertices._database, this._graph._properties._key, this, { + filter : { direction : direction, labels: labels } + }); + + while (cursor.hasNext()) { + edge = new Edge(this._graph, cursor.next()); + edges.push(edge); + } + + return edges; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inbound edges with given label +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.getInEdges = function () { + var labels = Array.prototype.slice.call(arguments); + return this.edges("in", labels); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief outbound edges with given label +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.getOutEdges = function () { + var labels = Array.prototype.slice.call(arguments); + return this.edges("out", labels); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief in- or outbound edges with given label +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.getEdges = function () { + var labels = Array.prototype.slice.call(arguments); + return this.edges("any", labels); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inbound edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.inbound = function () { + return this.getInEdges(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief outbound edges +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.outbound = function () { + return this.getOutEdges(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief changes a property of a vertex +//////////////////////////////////////////////////////////////////////////////// + +Vertex.prototype.setProperty = function (name, value) { + var results, + update = this._properties; + + update[name] = value; + + results = GraphAPI.putVertex(this._graph._properties._key, this._properties._key, update); + + this._properties = results.vertex; + + return name; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoGraph +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief constructs a new graph object +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.initialize = function (name, vertices, edges) { + var results; + + if (is.notExisty(vertices) && is.notExisty(edges)) { + results = GraphAPI.getGraph(name); + } else { + if (typeof vertices === 'object' && typeof vertices.name === 'function') { + vertices = vertices.name(); + } + if (typeof edges === 'object' && typeof edges.name === 'function') { + edges = edges.name(); + } + + results = GraphAPI.postGraph({ + _key: name, + vertices: vertices, + edges: edges + }); + } + + this._properties = results.graph; + this._vertices = arangodb.db._collection(this._properties.edgeDefinitions[0].from[0]); + this._edges = arangodb.db._collection(this._properties.edgeDefinitions[0].collection); + + // and dictionary for vertices and edges + this._verticesCache = {}; + this._edgesCache = {}; + + // and store the cashes + this.predecessors = {}; + this.distances = {}; + + return this; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoGraph +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return all graphs +//////////////////////////////////////////////////////////////////////////////// + +Graph.getAll = function () { + return GraphAPI.getAllGraphs(); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief static delete method +//////////////////////////////////////////////////////////////////////////////// + +Graph.drop = function (name) { + GraphAPI.deleteGraph(name); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief drops the graph, the vertices, and the edges +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.drop = function () { + GraphAPI.deleteGraph(this._properties._key); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief saves an edge to the graph +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._saveEdge = function(id, out_vertex_id, in_vertex_id, params) { + var results; + + this.emptyCachedPredecessors(); + + params._key = id; + params._from = out_vertex_id; + params._to = in_vertex_id; + + results = GraphAPI.postEdge(this._properties._key, params); + return new Edge(this, results.edge); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief saves a vertex to the graph +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._saveVertex = function (id, params) { + var results; + + if (is.existy(id)) { + params._key = id; + } + + results = GraphAPI.postVertex(this._properties._key, params); + return new Vertex(this, results.vertex); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief replace a vertex in the graph +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._replaceVertex = function (id, data) { + GraphAPI.putVertex(this._properties._key, id, data); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief replace an edge in the graph +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._replaceEdge = function (id, data) { + GraphAPI.putEdge(this._properties._key, id, data); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns a vertex given its id +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.getVertex = function (id) { + var results = GraphAPI.getVertex(this._properties._key, id); + + if (is.notExisty(results)) { + return null; + } + + return new Vertex(this, results.vertex); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns an iterator for all vertices +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.getVertices = function () { + var cursor = GraphAPI.getVertices(this._vertices._database, this._properties._key, {}), + graph = this, + wrapper = function(object) { + return new Vertex(graph, object); + }; + + return new Iterator(wrapper, cursor, "[vertex iterator]"); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns an edge given its id +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.getEdge = function (id) { + var results = GraphAPI.getEdge(this._properties._key, id); + + if (is.notExisty(results)) { + return null; + } + + return new Edge(this, results.edge); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns an iterator for all edges +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.getEdges = function () { + var cursor = GraphAPI.getEdges(this._vertices._database, this._properties._key, {}), + graph = this, + wrapper = function(object) { + return new Edge(graph, object); + }; + return new Iterator(wrapper, cursor, "[edge iterator]"); +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes a vertex and all in- or out-bound edges +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.removeVertex = function (vertex) { + this.emptyCachedPredecessors(); + GraphAPI.deleteVertex(this._properties._key, vertex._properties._key); + vertex._properties = undefined; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an edge +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype.removeEdge = function (edge) { + this.emptyCachedPredecessors(); + GraphAPI.deleteEdge(this._properties._key, edge._properties._key); + this._edgesCache[edge._properties._id] = undefined; + edge._properties = undefined; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- MODULE EXPORTS +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoGraph +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +exports.Edge = Edge; +exports.Graph = Graph; +exports.Vertex = Vertex; +exports.GraphArray = GraphArray; + + +require("org/arangodb/graph/algorithms-common"); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: +}); diff --git a/js/apps/system/aardvark/index.html b/js/apps/system/aardvark/index.html index 8c0946bc6c..9df14073fb 100644 --- a/js/apps/system/aardvark/index.html +++ b/js/apps/system/aardvark/index.html @@ -5,7 +5,7 @@ ArangoDB Web Interface - + diff --git a/js/apps/system/aardvark/manifest.json b/js/apps/system/aardvark/manifest.json index 7284407d12..f910eda002 100644 --- a/js/apps/system/aardvark/manifest.json +++ b/js/apps/system/aardvark/manifest.json @@ -102,6 +102,7 @@ "frontend/js/bootstrap/module-internal.js", "frontend/js/client/bootstrap/module-internal.js", "frontend/js/client/client.js", + "frontend/js/bootstrap/module-console.js", "frontend/js/models/arangoCollectionModel.js", "frontend/js/models/arangoDatabase.js", "frontend/js/models/arangoDocument.js", diff --git a/js/client/bootstrap/module-internal.js b/js/client/bootstrap/module-internal.js index 4150e8c275..e845ee0f69 100644 --- a/js/client/bootstrap/module-internal.js +++ b/js/client/bootstrap/module-internal.js @@ -1,5 +1,5 @@ /*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true, nonpropdel: true */ -/*global require, ArangoConnection, print, SYS_ARANGO */ +/*global require, ArangoConnection, print, SYS_ARANGO, window */ //////////////////////////////////////////////////////////////////////////////// /// @brief module "internal" @@ -300,6 +300,32 @@ internal.output(level, ": ", msg, "\n"); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief sprintf wrapper +//////////////////////////////////////////////////////////////////////////////// + + try { + if (window) { + internal.sprintf = function (format) { + var n = arguments.length; + if (n === 0) { + return ""; + } + if (n <= 1) { + return String(format); + } + + var i = 0; + + return format.replace(/%[dfs]/, function (match) { + return String(arguments[++i]); + }); + }; + } + } + catch (err) { + } + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/js/common/bootstrap/errors.js b/js/common/bootstrap/errors.js index 1fb6732900..49167af3a6 100644 --- a/js/common/bootstrap/errors.js +++ b/js/common/bootstrap/errors.js @@ -104,6 +104,7 @@ "ERROR_ARANGO_INDEX_DOCUMENT_ATTRIBUTE_MISSING" : { "code" : 1234, "message" : "index insertion warning - attribute missing in document" }, "ERROR_ARANGO_INDEX_CREATION_FAILED" : { "code" : 1235, "message" : "index creation failed" }, "ERROR_ARANGO_DATAFILE_FULL" : { "code" : 1300, "message" : "datafile full" }, + "ERROR_ARANGO_EMPTY_DATADIR" : { "code" : 1301, "message" : "server database directory is empty" }, "ERROR_REPLICATION_NO_RESPONSE" : { "code" : 1400, "message" : "no response" }, "ERROR_REPLICATION_INVALID_RESPONSE" : { "code" : 1401, "message" : "invalid response" }, "ERROR_REPLICATION_MASTER_ERROR" : { "code" : 1402, "message" : "master error" }, @@ -169,8 +170,6 @@ "ERROR_QUERY_GEO_INDEX_MISSING" : { "code" : 1570, "message" : "no suitable geo index found for geo restriction on '%s'" }, "ERROR_QUERY_FULLTEXT_INDEX_MISSING" : { "code" : 1571, "message" : "no suitable fulltext index found for fulltext query on '%s'" }, "ERROR_QUERY_INVALID_DATE_VALUE" : { "code" : 1572, "message" : "invalid date value" }, - "ERROR_QUERY_MULTI_MODIFY" : { "code" : 1573, "message" : "multi-modify query" }, - "ERROR_QUERY_MODIFY_IN_SUBQUERY" : { "code" : 1574, "message" : "modify in subquery" }, "ERROR_QUERY_FUNCTION_INVALID_NAME" : { "code" : 1580, "message" : "invalid user function name" }, "ERROR_QUERY_FUNCTION_INVALID_CODE" : { "code" : 1581, "message" : "invalid user function code" }, "ERROR_QUERY_FUNCTION_NOT_FOUND" : { "code" : 1582, "message" : "user function '%s()' not found" }, @@ -179,7 +178,6 @@ "ERROR_TRANSACTION_NESTED" : { "code" : 1651, "message" : "nested transactions detected" }, "ERROR_TRANSACTION_UNREGISTERED_COLLECTION" : { "code" : 1652, "message" : "unregistered collection used in transaction" }, "ERROR_TRANSACTION_DISALLOWED_OPERATION" : { "code" : 1653, "message" : "disallowed operation inside transaction" }, - "ERROR_TRANSACTION_ABORTED" : { "code" : 1654, "message" : "transaction aborted" }, "ERROR_USER_INVALID_NAME" : { "code" : 1700, "message" : "invalid user name" }, "ERROR_USER_INVALID_PASSWORD" : { "code" : 1701, "message" : "invalid password" }, "ERROR_USER_DUPLICATE" : { "code" : 1702, "message" : "duplicate user" }, @@ -209,6 +207,15 @@ "ERROR_GRAPH_COULD_NOT_CHANGE_EDGE" : { "code" : 1908, "message" : "could not change edge" }, "ERROR_GRAPH_TOO_MANY_ITERATIONS" : { "code" : 1909, "message" : "too many iterations" }, "ERROR_GRAPH_INVALID_FILTER_RESULT" : { "code" : 1910, "message" : "invalid filter result" }, + "ERROR_GRAPH_COLLECTION_MULTI_USE" : { "code" : 1920, "message" : "multi use of edge collection in edge def" }, + "ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS" : { "code" : 1921, "message" : "edge collection already used in edge def" }, + "ERROR_GRAPH_CREATE_MISSING_NAME" : { "code" : 1922, "message" : "missing graph name" }, + "ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION" : { "code" : 1923, "message" : "malformed edge def" }, + "ERROR_GRAPH_NOT_FOUND" : { "code" : 1924, "message" : "graph not found" }, + "ERROR_GRAPH_DUPLICATE" : { "code" : 1925, "message" : "graph already exists" }, + "ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST" : { "code" : 1926, "message" : "collection does not exist" }, + "ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX" : { "code" : 1927, "message" : "not a vertex collection" }, + "ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION" : { "code" : 1928, "message" : "not in orphan collection" }, "ERROR_SESSION_UNKNOWN" : { "code" : 1950, "message" : "unknown session" }, "ERROR_SESSION_EXPIRED" : { "code" : 1951, "message" : "session expired" }, "SIMPLE_CLIENT_UNKNOWN_ERROR" : { "code" : 2000, "message" : "unknown client error" }, diff --git a/js/common/bootstrap/module-console.js b/js/common/bootstrap/module-console.js index 90e12d3b7b..c31023e380 100644 --- a/js/common/bootstrap/module-console.js +++ b/js/common/bootstrap/module-console.js @@ -1,5 +1,5 @@ /*jslint indent: 2, maxlen: 120, vars: true, white: true, plusplus: true, nonpropdel: true, sloppy: true */ -/*global require, SYS_GETLINE, SYS_LOG */ +/*global require, SYS_GETLINE, SYS_LOG, jqconsole */ //////////////////////////////////////////////////////////////////////////////// /// @brief module "console" @@ -64,8 +64,21 @@ /// @brief internal logging //////////////////////////////////////////////////////////////////////////////// - var log = SYS_LOG; - delete SYS_LOG; + var log; + + try { + // this will work when we are in arangod but not in the browser / web interface + log = SYS_LOG; + delete SYS_LOG; + } + catch (err) { + // this will work in the web interface + log = function (level, message) { + if (jqconsole) { + jqconsole.Write(message + "\n", 'jssuccess'); + } + }; + } //////////////////////////////////////////////////////////////////////////////// /// @brief internal logging with group level diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index 4525d9e87e..c5c3ba3223 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -1709,6 +1709,7 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti createHiddenProperty(this, "__edgeDefinitions", edgeDefinitions); createHiddenProperty(this, "__idsToRemove", []); createHiddenProperty(this, "__collectionsToLock", []); + createHiddenProperty(this, "__orphanCollections", []); // fills this.__idsToRemove and this.__collectionsToLock var removeEdge = function (edgeId, options) { @@ -1957,6 +1958,12 @@ var checkIfMayBeDropped = function(colName, graphName, graphs) { } ); } + var orphanCollections = graph.orphanCollections; + if (orphanCollections) { + if (orphanCollections.indexOf(colName) !== -1) { + return false; + } + } } ); @@ -2557,6 +2564,7 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]); if (dropCollections !== false) { + graphs = getGraphCollection().toArray(); //eval collection to be dropped dropCandidates = currentEdgeDefinition.from; currentEdgeDefinition.to.forEach( @@ -2689,31 +2697,129 @@ Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections }; //////////////////////////////////////////////////////////////////////////////// -/// @startDocuBlock JSF_general_graph__addVertexCollection -/// Adds a vertex collection to the graph. If the collection does not exist, it will be created. +/// @startDocuBlock JSF_general_graph__addOrphanCollection +/// Adds a vertex collection to the set of orphan collections of the graph. If the +/// collection does not exist, it will be created. /// -/// `general-graph._addVertexCollection(vertexCollectionName, createCollection)` +/// `general-graph._addOrphanCollection(orphanCollectionName, createCollection)` /// -/// *vertexCollectionName* - string : name of vertex collection. +/// *orphanCollectionName* - string : name of vertex collection. /// *createCollection* - bool : if true the collection will be created if it does not exist. Default: true. /// /// @EXAMPLES /// -/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__addeleteVertexCollection} +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__addOrphanCollection} /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); /// var g = examples._create("myGraph", [ed1, ed2]); -/// g._addVertexCollection("myVC3", true); +/// g._addOrphanCollection("myVC3", true); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// /// @endDocuBlock /// //////////////////////////////////////////////////////////////////////////////// -Graph.prototype._addVertexCollection = function(edgeCollection, dropCollections) { +Graph.prototype._addOrphanCollection = function(vertexCollection, createCollection) { + //check edgeCollection + var ec = db._collection(vertexCollection); + var err; + if (ec === null) { + if (createCollection !== false) { + db._create(vertexCollection); + } else { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code; + err.errorMessage = vertexCollection + arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message; + throw err; + } + } else if (ec.type() !== 2) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message; + throw err; + } + + this.__orphanCollections.push(vertexCollection); + db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections}); }; +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__getOrphanCollections +/// Returns all vertex collections of the graph, that are not used in an edge definition. +/// +/// `general-graph._getOrphanCollections()` +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__getOrphanCollections} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = examples._create("myGraph", [ed1]); +/// g._addOrphanCollection("myVC3, true); +/// g._getOrphanCollections(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._getOrphanCollections = function() { + return this.__orphanCollections; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph__removeOrphanCollection +/// Removes an orphan collection from the graph and deletes the collection, if it is not +/// used in any graph. +/// +/// `general-graph._removeOrphanCollection()` +/// +/// *orphanCollectionName* - string : name of vertex collection. +/// *dropCollection* - bool : if true the collection will be dropped if it is not used in any graph. +/// Default: true. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__getOrphanCollections} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = examples._create("myGraph", [ed1]); +/// g._addOrphanCollection("myVC3, true); +/// g._addOrphanCollection("myVC4, true); +/// g._getOrphanCollections(); +/// g._removeOrphanCollection("myVC3"); +/// g._getOrphanCollections(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// + +Graph.prototype._removeOrphanCollection = function(orphanCollectionName, dropCollection) { + var err; + if (db._collection(orphanCollectionName) === null) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message; + throw err; + } + var index = this.__orphanCollections.indexOf(orphanCollectionName); + if (index === -1) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.message; + throw err; + } + this.__orphanCollections.splice(index, 1); + + if (dropCollection !== false) { + var graphs = getGraphCollection().toArray(); + if (checkIfMayBeDropped(orphanCollectionName, null, graphs)) { + db._drop(orphanCollectionName); + } + } +}; diff --git a/js/common/tests/shell-general-graph.js b/js/common/tests/shell-general-graph.js index 4d459318c0..033d79ad46 100644 --- a/js/common/tests/shell-general-graph.js +++ b/js/common/tests/shell-general-graph.js @@ -307,7 +307,7 @@ function GeneralGraphCreationSuite() { ); fail(); } catch (err) { - assertEqual(err.errorMessage, "a graph name is required to create a graph."); + assertEqual(err.errorMessage, ERRORS.ERROR_GRAPH_CREATE_MISSING_NAME.message); } }, @@ -2497,6 +2497,118 @@ function GeneralGraphCommonNeighborsSuite() { }; } +function OrphanCollectionSuite() { + var prefix = "UnitTestGraphVertexCollection", + g1, + g2, + gN1 = prefix + "Graph1", + gN2 = prefix + "Graph2", + eC1 = prefix + "EdgeCollection1", + eC2 = prefix + "EdgeCollection2", + vC1 = prefix + "VertexCollection1", + vC2 = prefix + "VertexCollection2", + vC3 = prefix + "VertexCollection3", + vC4 = prefix + "VertexCollection4", + vC5 = prefix + "VertexCollection5"; + + return { + + setUp : function() { + try { + arangodb.db._collection("_graphs").remove(gN1); + } catch (ignore) { + } + try { + arangodb.db._collection("_graphs").remove(gN2); + } catch (ignore) { + } + g1 = graph._create( + gN1, + graph._edgeDefinitions( + graph._directedRelationDefinition( + eC1, [vC1], [vC1, vC2] + ) + ) + ); + g2 = graph._create( + gN2, + graph._edgeDefinitions( + graph._directedRelationDefinition( + eC2, [vC3], [vC1] + ) + ) + ); + }, + + tearDown : function() { + graph._drop(gN1); + graph._drop(gN2); + try {db[vC1].drop()} catch (e) {} + try {db[vC4].drop()} catch (e) {} + }, + + test_getOrphanCollection: function() { + assertEqual(g1._getOrphanCollections(), []); + }, + + test_addOrphanCollection1: function() { + g1._addOrphanCollection(vC1, false); + assertEqual(g1._getOrphanCollections(), [vC1]); + }, + + test_addOrphanCollection2: function() { + try { + g1._addOrphanCollection(vC4, false); + } catch (e) { + assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code); + assertEqual(e.errorMessage, vC4 + ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message); + } + assertTrue(db._collection(vC4) === null); + assertEqual(g1._getOrphanCollections(), []); + }, + + test_addOrphanCollection3: function() { + try { + g1._addOrphanCollection(eC1, false); + } catch (e) { + assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code); + assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message); + } + assertTrue(db._collection(vC4) === null); + assertEqual(g1._getOrphanCollections(), []); + }, + + test_removeOrphanCollection1: function() { + var name = "completelyNonsenseNameForACollectionBLUBBBBB" + try { + g1._removeOrphanCollection(name); + } catch (e) { + assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code); + assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message); + } + }, + + test_removeOrphanCollection2: function() { + g1._addOrphanCollection(vC4, true); + g1._addOrphanCollection(vC5, true); + assertEqual(g1._getOrphanCollections(), [vC4, vC5]); + g1._removeOrphanCollection(vC4, false); + assertEqual(g1._getOrphanCollections(), [vC5]); + try { + g1._removeOrphanCollection(vC4); + } catch (e) { + assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code); + assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.message); + } + } + + + }; + + + +} + // ----------------------------------------------------------------------------- // --SECTION-- main @@ -2511,7 +2623,7 @@ jsunity.run(GeneralGraphAQLQueriesSuite); jsunity.run(EdgesAndVerticesSuite); jsunity.run(GeneralGraphCreationSuite); jsunity.run(ChainedFluentAQLResultsSuite); -//jsunity.run(VertexCollectionChainedFluentAQLResultsSuite); +jsunity.run(OrphanCollectionSuite); return jsunity.done(); diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index ac91d46e11..a99a95d66e 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -5130,7 +5130,7 @@ function IS_EXAMPLE_SET (example) { /// @startDocuBlock JSF_ahuacatl_general_graph_shortest_paths /// /// `GRAPH_SHORTEST_PATH (graphName, startVertexExample, endVertexExample, options)` -/// *The GRAPH\_SHORTEST\_PATH function returns all paths of a graph.* +/// *The GRAPH\_SHORTEST\_PATH function returns all shortest paths of a graph.* /// /// This function determines all shortest paths in a graph identified by *graphName*. /// The function accepts an id, an example, a list of examples @@ -5138,7 +5138,7 @@ function IS_EXAMPLE_SET (example) { /// start and end vertex. If one wants to calls this function to receive nearly all /// shortest paths for a graph the /// option *algorithm* should be set to *Floyd-Warshall* to increase performance. -/// If no algorithm is given in the options the function chooses the appropriate +/// If no algorithm is provided in the options the function chooses the appropriate /// one (either *Floyd-Warshall* or *Dijsktra*) according to its parameters. /// The length of a path is by default the amount of edges from one start vertex to /// an end vertex. The option weight allows the user to define an edge attribute @@ -5384,6 +5384,7 @@ function GRAPH_TRAVERSAL_TREE (vertexCollection, /// *The GRAPH\_DISTANCE\_TO function returns all paths and there distance within a graph.* /// /// This function is a wrapper of [GRAPH\_SHORTEST\_PATH](#SUBSECTION GRAPH_SHORTEST_PATH). +/// It does not return the actual path but only the distance between two vertices. /// //////////////////////////////////////////////////////////////////////////////// function GENERAL_GRAPH_DISTANCE_TO (graphName, @@ -5689,7 +5690,7 @@ function GENERAL_GRAPH_NEIGHBORS (graphName, } //////////////////////////////////////////////////////////////////////////////// -/// @startDocuBlock JSF_ahuacatl_general_graph_neighbors +/// @startDocuBlock JSF_ahuacatl_general_graph_edges /// /// `GRAPH_EDGES (graphName, vertexExample, options)` /// *The GRAPH\_EDGES function returns all edges of vertices.* @@ -5919,28 +5920,27 @@ function TRANSFER_GENERAL_GRAPH_NEIGHBORS_RESULT (result) { /// /// @EXAMPLES /// -/// A route planner example, all neighbors of locations with a distance of -/// either 700 or 600.: +/// A route planner example, all common neighbors of capitals. /// /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVertices1} /// ~ var db = require("internal").db; /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("routeplanner"); /// |db._query("FOR e IN GRAPH_COMMON_NEIGHBORS(" -/// |+"'routeplanner', {}) RETURN e" +/// |+"'routeplanner', {isCapital : true}, {isCapital : true}) RETURN e" /// ).toArray(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// -/// A route planner example, all outbound neighbors of munich with a maximal -/// depth of 2 : +/// A route planner example, all common outbound neighbors of munich with any other location +/// which have a maximal depth of 2 : /// /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphVertices2} /// ~ var db = require("internal").db; /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("routeplanner"); /// |db._query("FOR e IN GRAPH_COMMON_NEIGHBORS(" -/// |+"'routeplanner', {}, {direction : 'any', vertexCollectionRestriction" + -/// |" : 'city'}) RETURN e" +/// |+"'routeplanner', 'city/Munich', {}, {direction : 'outbound', maxDepth : 2}, "+ +/// | "{direction : 'outbound', maxDepth : 2}) RETURN e" /// ).toArray(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -6149,7 +6149,7 @@ function GENERAL_GRAPH_COMMON_PROPERTIES ( /// /// `GRAPH_ABSOLUTE_ECCENTRICITY (graphName, vertexExample, options)` /// *The GRAPH\_ABSOLUTE\_ECCENTRICITY function returns the -/// [eccentricity](http://en.wikipedia.org/wiki/Distance_(graph_theory)) +/// [eccentricity](http://en.wikipedia.org/wiki/Distance_%28graph_theory%29) /// of the vertices defined by the examples. /// /// The function accepts an id, an example, a list of examples or even an empty @@ -6260,7 +6260,7 @@ function GENERAL_GRAPH_ABSOLUTE_ECCENTRICITY (graphName, vertexExample, options) /// /// `GRAPH_ECCENTRICITY (graphName, options)` /// *The GRAPH\_ECCENTRICITY function returns the normalized -/// [eccentricity](http://en.wikipedia.org/wiki/Distance_(graph_theory)) +/// [eccentricity](http://en.wikipedia.org/wiki/Distance_%28graph_theory%29) /// of the graphs vertices /// /// * String *graphName* : The name of the graph. @@ -6737,7 +6737,7 @@ function GENERAL_GRAPH_BETWEENNESS (graphName, options) { /// /// `GRAPH_RADIUS (graphName, options)` /// *The GRAPH\_RADIUS function returns the -/// [radius](http://en.wikipedia.org/wiki/Eccentricity_(graph_theory)) +/// [radius](http://en.wikipedia.org/wiki/Eccentricity_%28graph_theory%29) /// of a graph. /// /// * String *graphName* : The name of the graph. @@ -6829,7 +6829,7 @@ function GENERAL_GRAPH_RADIUS (graphName, options) { /// /// `GRAPH_DIAMETER (graphName, options)` /// *The GRAPH\_DIAMETER function returns the -/// [diameter](http://en.wikipedia.org/wiki/Eccentricity_(graph_theory)) +/// [diameter](http://en.wikipedia.org/wiki/Eccentricity_%28graph_theory%29) /// of a graph. /// /// * String *graphName* : The name of the graph. diff --git a/js/server/modules/org/arangodb/database-version.js b/js/server/modules/org/arangodb/database-version.js index d6b47b8030..f7fb45ae46 100644 --- a/js/server/modules/org/arangodb/database-version.js +++ b/js/server/modules/org/arangodb/database-version.js @@ -30,10 +30,9 @@ /// @author Copyright 2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -var internal = require("internal"); var cluster = require("org/arangodb/cluster"); var fs = require("fs"); -var db = internal.db; +var db = require("org/arangodb").db; var console = require("console"); // ----------------------------------------------------------------------------- @@ -124,7 +123,7 @@ exports.databaseVersion = function () { } // path to the VERSION file - var versionFile = internal.db._path() + "/VERSION"; + var versionFile = db._path() + "/VERSION"; var lastVersion = null; // VERSION file exists, read its contents @@ -152,11 +151,11 @@ exports.databaseVersion = function () { } // extract server version - var currentServerVersion = internal.db._version().match(/^(\d+\.\d+).*$/); + var currentServerVersion = db._version().match(/^(\d+\.\d+).*$/); // server version is invalid for some reason if (! currentServerVersion) { - logger.error("Unexpected ArangoDB server version: " + internal.db._version()); + logger.error("Unexpected ArangoDB server version: " + db._version()); return { result: exports.NO_SERVER_VERSION }; } diff --git a/js/server/modules/org/arangodb/foxx.js b/js/server/modules/org/arangodb/foxx.js index 7cd00006a2..6562c8a9c8 100644 --- a/js/server/modules/org/arangodb/foxx.js +++ b/js/server/modules/org/arangodb/foxx.js @@ -30,11 +30,16 @@ var Controller = require("org/arangodb/foxx/controller").Controller, Model = require("org/arangodb/foxx/model").Model, - Repository = require("org/arangodb/foxx/repository").Repository; + Repository = require("org/arangodb/foxx/repository").Repository, + manager = require("org/arangodb/foxx/manager"), + arangodb = require("org/arangodb"); exports.Controller = Controller; exports.Model = Model; exports.Repository = Repository; +exports.requireApp = function(path) { + return manager.mountedApp(arangodb.normalizeURL('/' + path)); +}; // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE diff --git a/js/server/modules/org/arangodb/foxx/controller.js b/js/server/modules/org/arangodb/foxx/controller.js index 85728e3c5b..597c8821ce 100644 --- a/js/server/modules/org/arangodb/foxx/controller.js +++ b/js/server/modules/org/arangodb/foxx/controller.js @@ -115,6 +115,12 @@ Controller = function (context, options) { context.foxxes.push(this); + if (is.existy(context.manifest.rootElement)) { + this.rootElement =context.manifest.rootElement; + } else { + this.rootElement = false; + } + this.applicationContext = context; }; @@ -123,22 +129,8 @@ extend(Controller.prototype, { collection: function (name) { 'use strict'; - var collection, cname, prefix; - prefix = this.collectionPrefix; - - if (prefix === "") { - cname = name; - } else { - cname = prefix + "_" + name; - } - cname = cname.replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '').substr(0, 64); - - collection = db._collection(cname); - - if (!collection) { - throw new Error("collection with name '" + cname + "' does not exist."); - } - return collection; + require('console').log('Controller#collection is deprecated, use appContext#collection instead'); + return this.applicationContext.collection(name); }, //////////////////////////////////////////////////////////////////////////////// @@ -156,7 +148,7 @@ extend(Controller.prototype, { handleRequest: function (method, route, callback) { 'use strict'; var newRoute = internal.constructRoute(method, route, callback), - requestContext = new RequestContext(this.allRoutes, this.models, newRoute), + requestContext = new RequestContext(this.allRoutes, this.models, newRoute, this.rootElement), summary; this.routingInfo.routes.push(newRoute); @@ -294,7 +286,7 @@ extend(Controller.prototype, { /// /// This handles requests from the HTTP verb `delete`. See above for the /// arguments you can give. -/// +/// /// @warning Do not forget that `delete` is a reserved word in JavaScript and /// therefore needs to be called as app['delete']. There is also an alias `del` /// for this very reason. diff --git a/js/server/modules/org/arangodb/foxx/manager.js b/js/server/modules/org/arangodb/foxx/manager.js index 12f896918b..75a3172d47 100644 --- a/js/server/modules/org/arangodb/foxx/manager.js +++ b/js/server/modules/org/arangodb/foxx/manager.js @@ -102,7 +102,7 @@ function checkManifest (filename, mf) { // add a default (empty) description mf.description = ""; } - + if (mf.hasOwnProperty("apps")) { console.warn("Manifest '%s' still contains the deprecated 'apps' attribute. " + "Please change the attribute name to 'controllers'.", filename); @@ -121,7 +121,7 @@ function checkManifest (filename, mf) { "assets": [ false, "object" ], "author": [ false, "string" ], "contributors": [ false, "array" ], - "controllers": [ true, "object" ], + "controllers": [ false, "object" ], "defaultDocument": [ false, "string" ], "description": [ true, "string" ], "engines": [ false, "object" ], @@ -136,6 +136,7 @@ function checkManifest (filename, mf) { "teardown": [ false, "string" ], "thumbnail": [ false, "string" ], "version": [ true, "string" ], + "rootElement": [ false, "boolean" ], "exports": [ false, "object" ] }; @@ -148,8 +149,8 @@ function checkManifest (filename, mf) { var actualType = Array.isArray(mf[att]) ? "array" : typeof(mf[att]); if (actualType !== expectedType) { - console.error("Manifest '%s' uses an invalid data type (%s) for %s attribute '%s'", - filename, + console.error("Manifest '%s' uses an invalid data type (%s) for %s attribute '%s'", + filename, actualType, expectedType, att); @@ -160,8 +161,8 @@ function checkManifest (filename, mf) { // attribute not present in manifest if (expected[att][0]) { // required attribute - console.error("Manifest '%s' does not provide required attribute '%s'", - filename, + console.error("Manifest '%s' does not provide required attribute '%s'", + filename, att); failed = true; @@ -169,7 +170,7 @@ function checkManifest (filename, mf) { } } } - + if (failed) { throw new Error("Manifest '%s' is invalid/incompatible. Please check the error logs."); } @@ -178,8 +179,8 @@ function checkManifest (filename, mf) { for (att in mf) { if (mf.hasOwnProperty(att)) { if (! expected.hasOwnProperty(att)) { - console.warn("Manifest '%s' contains an unknown attribute '%s'", - filename, + console.warn("Manifest '%s' contains an unknown attribute '%s'", + filename, att); } } @@ -208,6 +209,10 @@ function extendContext (context, app, root) { return replaced; }; + context.collection = function (name) { + return arangodb.db._collection(this.collectionName(name)); + }; + context.path = function (name) { return fs.join(root, app._path, name); }; @@ -279,7 +284,7 @@ function buildAssetContent (app, assets, basePath) { var excludeFile = function (name) { var parts = name.split('/'); - + if (parts.length > 0) { var last = parts[parts.length - 1]; @@ -303,8 +308,8 @@ function buildAssetContent (app, assets, basePath) { if (match !== null) { m = fs.listTree(fs.join(basePath, match[1])); - - // files are sorted in file-system order. + + // files are sorted in file-system order. // this makes the order non-portable // we'll be sorting the files now using JS sort // so the order is more consistent across multiple platforms @@ -312,7 +317,7 @@ function buildAssetContent (app, assets, basePath) { for (i = 0; i < m.length; ++i) { var filename = fs.join(basePath, match[1], m[i]); - + if (! excludeFile(m[i])) { if (fs.isFile(filename)) { files.push(filename); @@ -322,7 +327,7 @@ function buildAssetContent (app, assets, basePath) { } else { match = reAll.exec(asset); - + if (match !== null) { throw new Error("Not implemented"); } @@ -367,7 +372,7 @@ function buildFileAsset (app, path, basePath, asset) { type = asset.contentType; } - // path contains a dot, derive content type from path + // path contains a dot, derive content type from path else if (path.match(/\.[a-zA-Z0-9]+$/)) { type = arangodb.guessContentType(path); } @@ -399,7 +404,7 @@ function buildDevelopmentAssetRoute (app, path, basePath, asset) { return { url: { match: path }, - action: { + action: { callback: function (req, res) { var c = buildFileAsset(app, path, basePath, asset); @@ -495,7 +500,7 @@ function installAssets (app, routes) { function executeAppScript (app, name, mount, prefix) { var desc = app._manifest; - + if (! desc) { throw new Error("Invalid application manifest, app " + arangodb.inspect(app)); } @@ -527,6 +532,7 @@ function executeAppScript (app, name, mount, prefix) { appContext.isDevelopment = devel; appContext.isProduction = ! devel; + appContext.manifest = app._manifest; extendContext(appContext, app, root); @@ -557,10 +563,10 @@ function teardownApp (app, mount, prefix) { function upsertAalAppEntry (manifest, thumbnail, path) { var aal = getStorage(); - var doc = aal.firstExample({ - type: "app", - name: manifest.name, - version: manifest.version + var doc = aal.firstExample({ + type: "app", + name: manifest.name, + version: manifest.version }); if (doc === null) { @@ -610,7 +616,7 @@ function mountAalApp (app, mount, options) { var find = aal.firstExample({ type: "mount", mount: mount, active: true }); if (find !== null) { - throw new Error("Cannot use mount path '" + mount + "', already used by '" + throw new Error("Cannot use mount path '" + mount + "', already used by '" + find.app + "' (" + find._key + ")"); } @@ -747,6 +753,8 @@ function routingAalApp (app, mount, options) { appContextTempl.isDevelopment = devel; appContextTempl.isProduction = ! devel; + appContextTempl.manifest = app._manifest; + var appContext; var file; @@ -815,13 +823,13 @@ function routingAalApp (app, mount, options) { for (j = 0; j < rt.length; ++j) { route = rt[j]; - + if (route.hasOwnProperty("url")) { route.url.match = arangodb.normalizeURL(p + "/" + route.url.match); } route.context = i; - + routes[key].push(route); } } @@ -910,16 +918,16 @@ function scanDirectory (path) { exports.scanAppDirectory = function () { 'use strict'; - + var aal = getStorage(); // remove all loaded apps first aal.removeByExample({ type: "app" }); // now re-scan, starting with system apps - scanDirectory(module.systemAppPath()); + scanDirectory(module.systemAppPath()); // now scan database-specific apps - scanDirectory(module.appPath()); + scanDirectory(module.appPath()); }; //////////////////////////////////////////////////////////////////////////////// @@ -1214,7 +1222,7 @@ exports.devSetup = function (filename) { var root = module.devAppPath(); var m = fs.join(root, filename, "manifest.json"); - + if (fs.exists(m)) { try { var mf = JSON.parse(fs.read(m)); @@ -1262,7 +1270,7 @@ exports.devTeardown = function (filename) { var root = module.devAppPath(); var m = fs.join(root, filename, "manifest.json"); - + if (fs.exists(m)) { try { var mf = JSON.parse(fs.read(m)); @@ -1296,7 +1304,7 @@ exports.devTeardown = function (filename) { exports.appRoutes = function () { 'use strict'; - + var aal = getStorage(); return arangodb.db._executeTransaction({ @@ -1327,7 +1335,7 @@ exports.appRoutes = function () { var r = routingAalApp(app, mount, options); if (r === null) { - throw new Error("Cannot compute the routing table for Foxx application '" + throw new Error("Cannot compute the routing table for Foxx application '" + app._id + "', check the log file for errors!"); } @@ -1367,7 +1375,7 @@ exports.developmentRoutes = function () { if (fs.exists(m)) { try { var mf = JSON.parse(fs.read(m)); - + checkManifest(m, mf); var appId = "dev:" + mf.name + ":" + files[j]; @@ -1387,7 +1395,7 @@ exports.developmentRoutes = function () { var r = routingAalApp(app, mount, options); if (r === null) { - throw new Error("Cannot compute the routing table for Foxx application '" + throw new Error("Cannot compute the routing table for Foxx application '" + app._id + "', check the log file for errors!"); } diff --git a/js/server/modules/org/arangodb/foxx/request_context.js b/js/server/modules/org/arangodb/foxx/request_context.js index 07d081f957..9e0bac5c50 100644 --- a/js/server/modules/org/arangodb/foxx/request_context.js +++ b/js/server/modules/org/arangodb/foxx/request_context.js @@ -40,14 +40,46 @@ var RequestContext, createBodyParamBubbleWrap, addCheck; -createBodyParamBubbleWrap = function (handler, paramName, Proto) { +var elementExtractFactory = function(paramName, rootElement) { + var extractElement; + + if (rootElement) { + extractElement = function (req) { + return req.body()[paramName]; + }; + } else { + extractElement = function (req) { + return req.body(); + }; + } + + return extractElement; +}; + +var bubbleWrapFactory = function (handler, paramName, Proto, extractElement, multiple) { 'use strict'; + if (multiple) { + Proto = Proto[0]; + } + return function (req, res) { - req.parameters[paramName] = new Proto(req.body()); + if (multiple) { + req.parameters[paramName] = _.map(extractElement(req), function (raw) { + return new Proto(raw); + }); + } else { + req.parameters[paramName] = new Proto(extractElement(req)); + } handler(req, res); }; }; +createBodyParamBubbleWrap = function (handler, paramName, Proto, rootElement) { + 'use strict'; + var extractElement = elementExtractFactory(paramName, rootElement); + return bubbleWrapFactory(handler, paramName, Proto, extractElement, is.array(Proto)); +}; + createErrorBubbleWrap = function (handler, errorClass, code, reason, errorHandler) { 'use strict'; if (is.notExisty(errorHandler)) { @@ -137,14 +169,15 @@ extend(SwaggerDocs.prototype, { /// Used for documenting and constraining the routes. //////////////////////////////////////////////////////////////////////////////// -RequestContext = function (executionBuffer, models, route) { +RequestContext = function (executionBuffer, models, route, rootElement) { 'use strict'; this.route = route; this.typeToRegex = { "int": "/[0-9]+/", "integer": "/[0-9]+/", - "string": "/.+/" + "string": "/[^/]+/" }; + this.rootElement = rootElement; this.docs = new SwaggerDocs(this.route.docs, models); this.docs.addNickname(route.docs.httpMethod, route.url.match); @@ -249,14 +282,30 @@ extend(RequestContext.prototype, { /// This will initialize a `Model` with the data and provide it to you via the /// params as `paramName`. /// For information about how to annotate your models, see the Model section. +/// If you provide the Model in an array, the response will take multiple models +/// instead of one. +/// +/// If you wrap the provided model in an array, the body param is always an array +/// and accordingly the return value of the `params` for the body call will also +/// return an array of models. +/// +/// The behavior of `bodyParam` changes depending on the `rootElement` option +/// set in the manifest. If it is set to true, it is expected that the body is an +/// object with a key of the same name as the `paramName` argument. +/// The value of this object is either a single object or in the case of a multi +/// element an array of objects. //////////////////////////////////////////////////////////////////////////////// bodyParam: function (paramName, description, Proto) { 'use strict'; var handler = this.route.action.callback; - this.docs.addBodyParam(paramName, description, Proto.toJSONSchema(paramName)); - this.route.action.callback = createBodyParamBubbleWrap(handler, paramName, Proto); + if (is.array(Proto)) { + this.docs.addBodyParam(paramName, description, Proto[0].toJSONSchema(paramName)); + } else { + this.docs.addBodyParam(paramName, description, Proto.toJSONSchema(paramName)); + } + this.route.action.callback = createBodyParamBubbleWrap(handler, paramName, Proto, this.rootElement); return this; }, diff --git a/js/server/tests/shell-foxx.js b/js/server/tests/shell-foxx.js index af9972371c..0eacee735a 100644 --- a/js/server/tests/shell-foxx.js +++ b/js/server/tests/shell-foxx.js @@ -3,7 +3,9 @@ require("internal").flushModuleCache(); var jsunity = require("jsunity"), FoxxController = require("org/arangodb/foxx").Controller, db = require("org/arangodb").db, + _ = require("underscore"), fakeContext, + fakeContextWithRootElement, stub_and_mock = require("org/arangodb/stub_and_mock"), stub = stub_and_mock.stub, allow = stub_and_mock.allow, @@ -13,6 +15,21 @@ fakeContext = { prefix: "", foxxes: [], comments: [], + manifest: { + rootElement: false + }, + clearComments: function () {}, + comment: function () {}, + collectionName: function () {} +}; + +fakeContextWithRootElement = { + prefix: "", + foxxes: [], + comments: [], + manifest: { + rootElement: true + }, clearComments: function () {}, comment: function () {}, collectionName: function () {} @@ -283,7 +300,7 @@ function DocumentationAndConstraintsSpec () { assertEqual(routes.length, 1); - assertEqual(routes[0].url.constraint.foxx, "/.+/"); + assertEqual(routes[0].url.constraint.foxx, "/[^/]+/"); assertEqual(routes[0].docs.parameters[0].paramType, "path"); assertEqual(routes[0].docs.parameters[0].name, "foxx"); assertEqual(routes[0].docs.parameters[0].description, "Kind of Foxx"); @@ -309,7 +326,7 @@ function DocumentationAndConstraintsSpec () { assertEqual(routes.length, 1); - assertEqual(routes[0].url.constraint.foxxParam, "/.+/"); + assertEqual(routes[0].url.constraint.foxxParam, "/[^/]+/"); assertEqual(routes[0].docs.parameters[0].paramType, "path"); assertEqual(routes[0].docs.parameters[0].name, "foxxParam"); assertEqual(routes[0].docs.parameters[0].description, "Kind of Foxx"); @@ -362,6 +379,27 @@ function DocumentationAndConstraintsSpec () { assertEqual(routes[0].docs.parameters[0].dataType, jsonSchema.id); }, + testAddBodyParamWithMultipleItems: function () { + var paramName = stub(), + description = stub(), + ModelPrototype = stub(), + jsonSchema = { id: 'a', required: [], properties: {} }; + + allow(ModelPrototype) + .toReceive("toJSONSchema") + .andReturn(jsonSchema); + + app.get('/foxx', function () { + //nothing + }).bodyParam(paramName, description, [ModelPrototype]); + + assertEqual(routes.length, 1); + assertEqual(routes[0].docs.parameters[0].name, paramName); + assertEqual(routes[0].docs.parameters[0].paramType, "body"); + assertEqual(routes[0].docs.parameters[0].description, description); + assertEqual(routes[0].docs.parameters[0].dataType, jsonSchema.id); + }, + testDefineBodyParamAddsJSONSchemaToModels: function () { var paramName = stub(), description = stub(), @@ -406,6 +444,34 @@ function DocumentationAndConstraintsSpec () { ModelPrototype.assertIsSatisfied(); }, + testSetParamForBodyParamWithMultipleItems: function () { + var req = { parameters: {} }, + res = {}, + paramName = stub(), + description = stub(), + rawElement = stub(), + requestBody = [rawElement], + ModelPrototype = stub(), + jsonSchemaId = stub(), + called = false; + + allow(req) + .toReceive("body") + .andReturn(requestBody); + + ModelPrototype = mockConstructor(rawElement); + ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; }; + + app.get('/foxx', function (providedReq) { + called = (providedReq.parameters[paramName][0] instanceof ModelPrototype); + }).bodyParam(paramName, description, [ModelPrototype]); + + routes[0].action.callback(req, res); + + assertTrue(called); + ModelPrototype.assertIsSatisfied(); + }, + testDocumentationForErrorResponse: function () { var CustomErrorClass = function () {}; @@ -639,36 +705,6 @@ function CommentDrivenDocumentationSpec () { }; } -function HelperFunctionSpec () { - var app; - - return { - setUp: function () { - fakeContext.collectionPrefix = "fancy"; - app = new FoxxController(fakeContext); - }, - - testGetACollection: function () { - db._create("fancy_pants"); - - assertEqual(app.collection("pants"), db._collection("fancy_pants")); - }, - - testGetACollectionThatDoesNotExist: function () { - var err; - db._drop("fancy_pants"); - - try { - app.collection("pants"); - } catch(e) { - err = e; - } - - assertEqual(err.message, "collection with name 'fancy_pants' does not exist."); - } - }; -} - function SetupAuthorization () { var app; @@ -767,12 +803,79 @@ function SetupAuthorization () { }; } +function FoxxControllerWithRootElement () { + var app; + + return { + setUp: function () { + app = new FoxxController(fakeContextWithRootElement); + routes = app.routingInfo.routes; + }, + + testBodyParamWithOneElement: function () { + var req = { parameters: {} }, + res = {}, + paramName = 'myBodyParam', + description = stub(), + rawElement = stub(), + requestBody = { myBodyParam: rawElement }, + ModelPrototype = stub(), + jsonSchemaId = stub(), + called = false; + + allow(req) + .toReceive("body") + .andReturn(requestBody); + + ModelPrototype = mockConstructor(rawElement); + ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; }; + + app.get('/foxx', function (providedReq) { + called = (providedReq.parameters[paramName] instanceof ModelPrototype); + }).bodyParam(paramName, description, ModelPrototype); + + routes[0].action.callback(req, res); + + assertTrue(called); + ModelPrototype.assertIsSatisfied(); + }, + + testBodyParamWithMultipleElement: function () { + var req = { parameters: {} }, + res = {}, + paramName = 'myBodyParam', + description = stub(), + rawElement = stub(), + requestBody = { myBodyParam: [rawElement] }, + ModelPrototype = stub(), + jsonSchemaId = stub(), + called = false; + + allow(req) + .toReceive("body") + .andReturn(requestBody); + + ModelPrototype = mockConstructor(rawElement); + ModelPrototype.toJSONSchema = function () { return { id: jsonSchemaId }; }; + + app.get('/foxx', function (providedReq) { + called = (providedReq.parameters[paramName][0] instanceof ModelPrototype); + }).bodyParam(paramName, description, [ModelPrototype]); + + routes[0].action.callback(req, res); + + assertTrue(called); + ModelPrototype.assertIsSatisfied(); + } + }; +} + jsunity.run(CreateFoxxControllerSpec); jsunity.run(SetRoutesFoxxControllerSpec); jsunity.run(DocumentationAndConstraintsSpec); jsunity.run(AddMiddlewareFoxxControllerSpec); jsunity.run(CommentDrivenDocumentationSpec); -jsunity.run(HelperFunctionSpec); jsunity.run(SetupAuthorization); +jsunity.run(FoxxControllerWithRootElement); return jsunity.done(); diff --git a/lib/BasicsC/errors.dat b/lib/BasicsC/errors.dat index 815a7fd696..799da69125 100755 --- a/lib/BasicsC/errors.dat +++ b/lib/BasicsC/errors.dat @@ -129,6 +129,7 @@ ERROR_ARANGO_INDEX_CREATION_FAILED,1235,"index creation failed","Will be raised ################################################################################ ERROR_ARANGO_DATAFILE_FULL,1300,"datafile full","Will be raised when the datafile reaches its limit." +ERROR_ARANGO_EMPTY_DATADIR,1301,"server database directory is empty","Will be raised when encoutering an empty server database directory." ################################################################################ ## ArangoDB replication errors @@ -289,6 +290,15 @@ ERROR_GRAPH_COULD_NOT_CREATE_EDGE,1907,"could not create edge","Will be raised w ERROR_GRAPH_COULD_NOT_CHANGE_EDGE,1908,"could not change edge","Will be raised when the edge could not be changed." ERROR_GRAPH_TOO_MANY_ITERATIONS,1909,"too many iterations","Will be raised when too many iterations are done in a graph traversal." ERROR_GRAPH_INVALID_FILTER_RESULT,1910,"invalid filter result","Will be raised when an invalid filter result is returned in a graph traversal." +ERROR_GRAPH_COLLECTION_MULTI_USE,1920,"multi use of edge collection in edge def","an edge collection may only be used once in one edge definition of a graph.", +ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS,1921,"edge collection already used in edge def"," is already used by another graph in a different edge definition.", +ERROR_GRAPH_CREATE_MISSING_NAME,1922,"missing graph name","a graph name is required to create a graph.", +ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION,1923,"malformed edge def","the edge definition is malformed. It has to be an array of objects.", +ERROR_GRAPH_NOT_FOUND,1924,"graph not found","a graph with this name could not be found.", +ERROR_GRAPH_DUPLICATE,1925,"graph already exists","a graph with this name already exists.", +ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST,1926,"collection does not exist"," does not exist.", +ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX,1927,"not a vertex collection","the collection is not a vertex collection.", +ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION,1928,"not in orphan collection","Vertex collection not in orphan collection of the graph.", ################################################################################ ## Session errors diff --git a/lib/BasicsC/voc-errors.c b/lib/BasicsC/voc-errors.c index 2af20d4a8e..05645408dc 100644 --- a/lib/BasicsC/voc-errors.c +++ b/lib/BasicsC/voc-errors.c @@ -100,6 +100,7 @@ void TRI_InitialiseErrorMessages (void) { REG_ERROR(ERROR_ARANGO_INDEX_DOCUMENT_ATTRIBUTE_MISSING, "index insertion warning - attribute missing in document"); REG_ERROR(ERROR_ARANGO_INDEX_CREATION_FAILED, "index creation failed"); REG_ERROR(ERROR_ARANGO_DATAFILE_FULL, "datafile full"); + REG_ERROR(ERROR_ARANGO_EMPTY_DATADIR, "server database directory is empty"); REG_ERROR(ERROR_REPLICATION_NO_RESPONSE, "no response"); REG_ERROR(ERROR_REPLICATION_INVALID_RESPONSE, "invalid response"); REG_ERROR(ERROR_REPLICATION_MASTER_ERROR, "master error"); @@ -205,6 +206,15 @@ void TRI_InitialiseErrorMessages (void) { REG_ERROR(ERROR_GRAPH_COULD_NOT_CHANGE_EDGE, "could not change edge"); REG_ERROR(ERROR_GRAPH_TOO_MANY_ITERATIONS, "too many iterations"); REG_ERROR(ERROR_GRAPH_INVALID_FILTER_RESULT, "invalid filter result"); + REG_ERROR(ERROR_GRAPH_COLLECTION_MULTI_USE, "multi use of edge collection in edge def"); + REG_ERROR(ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS, "edge collection already used in edge def"); + REG_ERROR(ERROR_GRAPH_CREATE_MISSING_NAME, "missing graph name"); + REG_ERROR(ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION, "malformed edge def"); + REG_ERROR(ERROR_GRAPH_NOT_FOUND, "graph not found"); + REG_ERROR(ERROR_GRAPH_DUPLICATE, "graph already exists"); + REG_ERROR(ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST, "collection does not exist"); + REG_ERROR(ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX, "not a vertex collection"); + REG_ERROR(ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION, "not in orphan collection"); REG_ERROR(ERROR_SESSION_UNKNOWN, "unknown session"); REG_ERROR(ERROR_SESSION_EXPIRED, "session expired"); REG_ERROR(SIMPLE_CLIENT_UNKNOWN_ERROR, "unknown client error"); diff --git a/lib/BasicsC/voc-errors.h b/lib/BasicsC/voc-errors.h index 5a755d8494..7c15ad0b34 100644 --- a/lib/BasicsC/voc-errors.h +++ b/lib/BasicsC/voc-errors.h @@ -213,6 +213,8 @@ extern "C" { /// Will be raised when an attempt to create an index has failed. /// - 1300: @LIT{datafile full} /// Will be raised when the datafile reaches its limit. +/// - 1301: @LIT{server database directory is empty} +/// Will be raised when encoutering an empty server database directory. /// - 1400: @LIT{no response} /// Will be raised when the replication applier does not receive any or an /// incomplete response from the master. @@ -485,6 +487,25 @@ extern "C" { /// - 1910: @LIT{invalid filter result} /// Will be raised when an invalid filter result is returned in a graph /// traversal. +/// - 1920: @LIT{multi use of edge collection in edge def} +/// an edge collection may only be used once in one edge definition of a +/// graph. +/// - 1921: @LIT{edge collection already used in edge def} +/// is already used by another graph in a different edge definition. +/// - 1922: @LIT{missing graph name} +/// a graph name is required to create a graph. +/// - 1923: @LIT{malformed edge def} +/// the edge definition is malformed. It has to be an array of objects. +/// - 1924: @LIT{graph not found} +/// a graph with this name could not be found. +/// - 1925: @LIT{graph already exists} +/// a graph with this name already exists. +/// - 1926: @LIT{collection does not exist} +/// does not exist. +/// - 1927: @LIT{not a vertex collection} +/// the collection is not a vertex collection. +/// - 1928: @LIT{not in orphan collection} +/// Vertex collection not in orphan collection of the graph. /// - 1950: @LIT{unknown session} /// Will be raised when an invalid/unknown session id is passed to the server. /// - 1951: @LIT{session expired} @@ -1452,6 +1473,16 @@ void TRI_InitialiseErrorMessages (void); #define TRI_ERROR_ARANGO_DATAFILE_FULL (1300) +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1301: ERROR_ARANGO_EMPTY_DATADIR +/// +/// server database directory is empty +/// +/// Will be raised when encoutering an empty server database directory. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_ARANGO_EMPTY_DATADIR (1301) + //////////////////////////////////////////////////////////////////////////////// /// @brief 1400: ERROR_REPLICATION_NO_RESPONSE /// @@ -2557,6 +2588,96 @@ void TRI_InitialiseErrorMessages (void); #define TRI_ERROR_GRAPH_INVALID_FILTER_RESULT (1910) +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1920: ERROR_GRAPH_COLLECTION_MULTI_USE +/// +/// multi use of edge collection in edge def +/// +/// an edge collection may only be used once in one edge definition of a graph. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_COLLECTION_MULTI_USE (1920) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1921: ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS +/// +/// edge collection already used in edge def +/// +/// is already used by another graph in a different edge definition. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS (1921) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1922: ERROR_GRAPH_CREATE_MISSING_NAME +/// +/// missing graph name +/// +/// a graph name is required to create a graph. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_CREATE_MISSING_NAME (1922) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1923: ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION +/// +/// malformed edge def +/// +/// the edge definition is malformed. It has to be an array of objects. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION (1923) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1924: ERROR_GRAPH_NOT_FOUND +/// +/// graph not found +/// +/// a graph with this name could not be found. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_NOT_FOUND (1924) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1925: ERROR_GRAPH_DUPLICATE +/// +/// graph already exists +/// +/// a graph with this name already exists. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_DUPLICATE (1925) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1926: ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST +/// +/// collection does not exist +/// +/// does not exist. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST (1926) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1927: ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX +/// +/// not a vertex collection +/// +/// the collection is not a vertex collection. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX (1927) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1928: ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION +/// +/// not in orphan collection +/// +/// Vertex collection not in orphan collection of the graph. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION (1928) + //////////////////////////////////////////////////////////////////////////////// /// @brief 1950: ERROR_SESSION_UNKNOWN ///