diff --git a/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp b/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp index 01793a6c5a..ea193d3c7b 100644 --- a/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp +++ b/Documentation/Books/Users/General-Graphs/GeneralGraphFunctions.mdpp @@ -26,12 +26,8 @@ There are different types of edge defintions: !SUBSECTION Edge Definitions -The edge definitions for a graph is an Array containing arbitrary many directed and/or undirected relations as defined below. -The edge definitons array is initialised with the following call: + -```js -> var edgeDefinitions = graph._edgeDefinitions(edgeDefinition1,......edgeDefinitionN); -``` To add further edge definitions to the array one must call: @@ -42,11 +38,87 @@ To add further edge definitions to the array one must call: !SUBSUBSECTION Undirected Relation - + +
+`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: +
+ +``` +arangosh> var graph = require("org/arangodb/general-graph"); +arangosh> graph._undirectedRelationDefinition("friend", "user"); +{ + "collection" : "friend", + "from" : [ + "user" + ], + "to" : [ + "user" + ] +} +``` +
+To define a relation between several vertex collections: +
+ +``` +arangosh> var graph = require("org/arangodb/general-graph"); +arangosh> graph._undirectedRelationDefinition("marriage", ["female", "male"]); +{ + "collection" : "marriage", + "from" : [ + "female", + "male" + ], + "to" : [ + "female", + "male" + ] +} +``` + !SUBSUBSECTION Directed Relation - + +
+`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 +
+ +``` +arangosh> var graph = require("org/arangodb/general-graph"); +arangosh> graph._directedRelationDefinition("has_bought", ["Customer", "Company"], ["Groceries", "Electronics"]); +{ + "collection" : "has_bought", + "from" : [ + "Customer", + "Company" + ], + "to" : [ + "Groceries", + "Electronics" + ] +} +``` + !SUBSUBSECTION Complete Example to create a graph @@ -81,20 +153,74 @@ alternative call: !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. +used in an edge definition of the graph. If the graph is extended with an edge definition using one of the orphans, +it will be removed from the orphan collection automatically. !SUBSUBSECTION Add - + +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 +
+ +``` +arangosh> var graph = require("org/arangodb/general-graph") +arangosh> var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +arangosh> var g = graph._create("myGraph", [ed1, ed2]); +ReferenceError: ed2 is not defined +``` +
+ !SUBSUBSECTION Read - + +Returns all vertex collections of the graph, that are not used in an edge definition. +
+`general-graph._getOrphanCollections()` +
+@EXAMPLES +
+ +``` +arangosh> var graph = require("org/arangodb/general-graph") +arangosh> var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +arangosh> var g = graph._create("myGraph", [ed1]); +[ArangoError 1925: graph already exists] +``` +
+ !SUBSUBSECTION Remove - + +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 +
+ +``` +arangosh> var graph = require("org/arangodb/general-graph") +arangosh> var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +arangosh> var g = graph._create("myGraph", [ed1]); +[ArangoError 1925: graph already exists] +``` +
+ !SUBSECTION Read a graph @@ -126,47 +252,324 @@ dropCollections: bool - optional. *true* all collections of the graph will be de !SUBSECTION Save - + +Creates and saves a new vertex in collection *vertexCollectionName* +
+`general-graph.vertexCollectionName.save(data)` +
+*data*: json - data of vertex +
+@EXAMPLES +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.male.save({name: "Floyd", _key: "floyd"}); +{ + "error" : false, + "_id" : "male/floyd", + "_rev" : "91260521", + "_key" : "floyd" +} +``` +
+ !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.male.save({neym: "Jon", _key: "john"}); +{ + "error" : false, + "_id" : "male/john", + "_rev" : "31360617", + "_key" : "john" +} +arangosh> g.male.replace("male/john", {name: "John"}); +{ + "error" : false, + "_id" : "male/john", + "_rev" : "31557225", + "_key" : "john" +} +``` +
+ !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.female.save({name: "Lynda", _key: "linda"}); +{ + "error" : false, + "_id" : "female/linda", + "_rev" : "79201897", + "_key" : "linda" +} +arangosh> g.female.update({name: "Linda", _key: "linda"}); +TypeError: Object # has no method 'split' +``` +
+ !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.male.save({name: "Kermit", _key: "kermit"}); +{ + "error" : false, + "_id" : "male/kermit", + "_rev" : "83068521", + "_key" : "kermit" +} +arangosh> db._exists("male/kermit") +true +arangosh> g.male.remove("male/kermit") +true +arangosh> db._exists("male/kermit") +false +``` +
+ !SECTION Edge !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.relation.save("male/bob", "female/alice", {type: "married", _key: "bobAndAlice"}); +{ + "error" : false, + "_id" : "relation/bobAndAlice", + "_rev" : "45909609", + "_key" : "bobAndAlice" +} +``` +
+If the collections of *from* and *to* are not defined in an edgeDefinition of the graph, +the edge will not be stored. +
+
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.relation.save("relation/aliceAndBob", "female/alice", {type: "married", _key: "bobAndAlice"}); +Edge is not allowed between relation/aliceAndBob and female/alice. +``` + !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.relation.save("female/alice", "female/diana", {typo: "nose", _key: "aliceAndDiana"}); +{ + "error" : false, + "_id" : "relation/aliceAndDiana", + "_rev" : "27362921", + "_key" : "aliceAndDiana" +} +arangosh> g.relation.replace("relation/aliceAndDiana", {type: "knows"}); +{ + "error" : false, + "_id" : "relation/aliceAndDiana", + "_rev" : "27559529", + "_key" : "aliceAndDiana" +} +``` +
+ !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.relation.save("female/alice", "female/diana", {type: "knows", _key: "aliceAndDiana"}); +{ + "error" : false, + "_id" : "relation/aliceAndDiana", + "_rev" : "127108713", + "_key" : "aliceAndDiana" +} +arangosh> g.relation.update("relation/aliceAndDiana", {type: "quarrelled", _key: "aliceAndDiana"}); +{ + "error" : false, + "_id" : "relation/aliceAndDiana", + "_rev" : "127305321", + "_key" : "aliceAndDiana" +} +``` +
+ !SUBSECTION 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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g.relation.save("female/alice", "female/diana", {_key: "aliceAndDiana"}); +{ + "error" : false, + "_id" : "relation/aliceAndDiana", + "_rev" : "141657705", + "_key" : "aliceAndDiana" +} +arangosh> db._exists("relation/aliceAndDiana") +true +arangosh> g.relation.remove("relation/aliceAndDiana") +true +arangosh> db._exists("relation/aliceAndDiana") +false +``` +
+ !SECTION Vertices !SUBSECTION Get vertex *from* of an edge - + +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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g._getFromVertex("relation/aliceAndBob") +{ + "name" : "Alice", + "_id" : "female/alice", + "_rev" : "175670889", + "_key" : "alice" +} +``` +
+ !SUBSECTION Get vertex *to* of an edge - + +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 +
+ +``` +arangosh> var examples = require("org/arangodb/graph-examples/example-graph.js"); +arangosh> var g = examples.loadGraph("social"); +arangosh> g._getToVertex("relation/aliceAndBob") +{ + "name" : "Bob", + "_id" : "male/bob", + "_rev" : "40666729", + "_key" : "bob" +} +``` +
diff --git a/arangosh/ArangoShell/ArangoClient.cpp b/arangosh/ArangoShell/ArangoClient.cpp index 2816fb1959..d905e057ac 100644 --- a/arangosh/ArangoShell/ArangoClient.cpp +++ b/arangosh/ArangoShell/ArangoClient.cpp @@ -307,6 +307,7 @@ void ArangoClient::parse (ProgramOptions& options, int argc, char* argv[], string const& initFilename) { + // if options are invalid, exit directly if (! options.parse(description, argc, argv)) { LOG_FATAL_AND_EXIT("%s", options.lastError().c_str()); diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/errors.js b/js/apps/system/aardvark/frontend/js/bootstrap/errors.js index 49167af3a6..72b4eb39a6 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/errors.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/errors.js @@ -216,6 +216,8 @@ "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_GRAPH_COLLECTION_USED_IN_EDGE_DEF" : { "code" : 1929, "message" : "collection used in edge def" }, + "ERROR_GRAPH_EDGE_COLLECTION_NOT_USED" : { "code" : 1930, "message" : "edge collection not used in graph" }, "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/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js index 6424440fec..43850f63ae 100644 --- 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 @@ -2033,7 +2033,8 @@ var checkIfMayBeDropped = function(colName, graphName, graphs) { var _drop = function(graphId, dropCollections) { - var gdb = getGraphCollection(); + var gdb = getGraphCollection(), + graphs; if (!gdb.exists(graphId)) { var err = new ArangoError(); @@ -2050,7 +2051,7 @@ var _drop = function(graphId, dropCollections) { var from = edgeDefinition.from; var to = edgeDefinition.to; var collection = edgeDefinition.collection; - var graphs = getGraphCollection().toArray(); + graphs = getGraphCollection().toArray(); if (checkIfMayBeDropped(collection, graph._key, graphs)) { db._drop(collection); } @@ -2070,6 +2071,20 @@ var _drop = function(graphId, dropCollections) { ); } ); + //drop orphans + graphs = getGraphCollection().toArray(); + if (!graph.orphanCollections) { + graph.orphanCollections = []; + } + graph.orphanCollections.forEach( + function(oC) { + if (checkIfMayBeDropped(oC, graph._key, graphs)) { + try { + db._drop(oC); + } catch (e) {} + } + } + ); } gdb.remove(graphId); @@ -2482,9 +2497,10 @@ Graph.prototype._amountCommonProperties = function(vertex1Example, vertex2Exampl //////////////////////////////////////////////////////////////////////////////// /// @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. +/// Extends the edge definitions of a graph. If an orphan collection is used in this +/// edge definitino, it will be removed from the orphenage. 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)` /// @@ -2570,19 +2586,78 @@ Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) { }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal function for editing edge definitions +//////////////////////////////////////////////////////////////////////////////// + +var changeEdgeDefinitionsForGraph = function(graph, edgeDefinition, newCollections, possibleOrphans, self) { + + var oldCollections = []; + var graphCollections = []; + var graphObj = _graph(graph._key); + var eDs = graph.edgeDefinitions; + + //replace edgeDefintion + eDs.forEach( + function(eD, id) { + if(eD.collection === edgeDefinition.collection) { + oldCollections = _.union(oldCollections, eD.from); + oldCollections = _.union(oldCollections, 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; + } + } else { + //collect all used collections + graphCollections = _.union(graphCollections, eD.from); + graphCollections = _.union(graphCollections, eD.to); + } + } + ); + + + //remove used collection from orphanage + newCollections.forEach( + function(nc) { + if (graph._key === self.__name) { + if (self.__vertexCollections[nc] === undefined) { + self.__vertexCollections[nc] = db[nc]; + } + try { + graphObj._removeOrphanCollection(nc); + } catch (e) { + } + } + } + ); + + //move unused collections to orphanage + possibleOrphans.forEach( + function(po) { + if (graphCollections.indexOf(po) === -1) { + delete graphObj.__vertexCollections[po]; + graphObj._addOrphanCollection(po); + } + } + ); +}; //////////////////////////////////////////////////////////////////////////////// /// @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. +/// replace the existing edge definition of the graph which has the same collection. +/// Vertex Collections of the replaced edge definition, that are not used in the new +/// definition will transform to an orphan. Orphans that are used in this new edge +/// definition will be deleted from the list of orphans. Other graphs with the same edge +/// definition will be modified, too. /// -/// `general-graph._editEdgeDefinition(edgeDefinition, dropCollections)` +/// `general-graph._editEdgeDefinition(edgeDefinition)` /// /// *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. +/// definition with the same attribute *collection*. /// /// @EXAMPLES /// @@ -2597,13 +2672,9 @@ Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) { /// @endDocuBlock /// //////////////////////////////////////////////////////////////////////////////// -Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) { - var self = this; - var dropCandidates; - var currentEdgeDefinition = {}; - var exOrphanCandidates = []; - var effectedGraphs = []; +Graph.prototype._editEdgeDefinitions = function(edgeDefinition) { + var self = this; //check, if in graphs edge definition if (this.__edgeCollections[edgeDefinition.collection] === undefined) { @@ -2613,108 +2684,33 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) throw err; } + findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]); + + //evaluate collections to add to orphanage + var possibleOrphans = []; + var currentEdgeDefinition; + this.__edgeDefinitions.forEach( + function(ed) { + if (edgeDefinition.collection === ed.collection) { + currentEdgeDefinition = ed; + } + } + ); + + var currentCollections = _.union(currentEdgeDefinition.from, currentEdgeDefinition.to); + var newCollections = _.union(edgeDefinition.from, edgeDefinition.to); + currentCollections.forEach( + function(colName) { + if (newCollections.indexOf(colName) === -1) { + possibleOrphans.push(colName); + } + } + ); //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}); - effectedGraphs.push(graph._key); - 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) { - exOrphanCandidates.push(vc); - self.__vertexCollections[vc] = db[vc]; - } - } - ); - edgeDefinition.to.forEach( - function(vc) { - if (self.__vertexCollections[vc] === undefined) { - exOrphanCandidates.push(vc); - 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]; - } - } - ); - - //orphans treatment - effectedGraphs.forEach( - function(gN) { - var g; - if (gN === self.__name) { - g = self; - } else { - g = _graph(gN); - } - var orphans = g._getOrphanCollections(); - exOrphanCandidates.forEach( - function(eOC) { - if (orphans.indexOf(eOC) !== -1) { - g._removeOrphanCollection(eOC); - } - } - ); + changeEdgeDefinitionsForGraph(graph, edgeDefinition, newCollections, possibleOrphans, self); } ); @@ -2725,14 +2721,12 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) /// @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. +/// in another edge definition of the graph, they will be moved to the orphanage. /// -/// `general-graph._deleteEdgeDefinition(edgeCollectionName, dropCollections)` +/// `general-graph._deleteEdgeDefinition(edgeCollectionName)` /// /// *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 /// @@ -2741,56 +2735,49 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) /// 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); +/// g._deleteEdgeDefinition("myEC1"); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// /// @endDocuBlock /// //////////////////////////////////////////////////////////////////////////////// -Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections) { +Graph.prototype._deleteEdgeDefinition = function(edgeCollection) { + + //check, if in graphs edge definition + if (this.__edgeCollections[edgeCollection] === 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; + } + var edgeDefinitions = this.__edgeDefinitions, - vertexCollections = [], - definitionFound = false, + self = this, + usedVertexCollections = [], + possibleOrphans = [], 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); - } - } - ); - } + index = idx; + possibleOrphans = edgeDefinition.from; + possibleOrphans = _.union(possibleOrphans, edgeDefinition.to); + } else { + usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.from); + usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.to); } } ); - 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); - } + this.__edgeDefinitions.splice(index, 1); + possibleOrphans.forEach( + function(po) { + if (usedVertexCollections.indexOf(po) === -1) { + self.__orphanCollections.push(po); } - ); - - } - + } + ); }; //////////////////////////////////////////////////////////////////////////////// @@ -2816,17 +2803,17 @@ Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections /// //////////////////////////////////////////////////////////////////////////////// -Graph.prototype._addOrphanCollection = function(vertexCollection, createCollection) { +Graph.prototype._addOrphanCollection = function(orphanCollectionName, createCollection) { //check edgeCollection - var ec = db._collection(vertexCollection); + var ec = db._collection(orphanCollectionName); var err; if (ec === null) { if (createCollection !== false) { - db._create(vertexCollection); + db._create(orphanCollectionName); } 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; + err.errorMessage = orphanCollectionName + arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message; throw err; } } else if (ec.type() !== 2) { @@ -2835,8 +2822,14 @@ Graph.prototype._addOrphanCollection = function(vertexCollection, createCollecti err.errorMessage = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message; throw err; } + if (this.__vertexCollections[orphanCollectionName] !== undefined) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message; + throw err; + } - this.__orphanCollections.push(vertexCollection); + this.__orphanCollections.push(orphanCollectionName); db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections}); }; diff --git a/js/client/modules/org/arangodb/foxx/manager.js b/js/client/modules/org/arangodb/foxx/manager.js index 618e388efb..2c62d4cdcd 100644 --- a/js/client/modules/org/arangodb/foxx/manager.js +++ b/js/client/modules/org/arangodb/foxx/manager.js @@ -378,7 +378,7 @@ function cmdUsage () { var fm = "foxx-manager"; printf("Example usage:\n"); - printf(" %s install \n", fm); + printf(" %s install option1=value1\n", fm); printf(" %s uninstall \n\n", fm); printf("Further help:\n"); @@ -460,6 +460,43 @@ function fetchDirectoryForInstall (name) { return exports.fetch("directory", name).app; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts command-line options +//////////////////////////////////////////////////////////////////////////////// + +function extractCommandLineOptions (args) { + 'use strict'; + + var options = {}; + var nargs = []; + var i; + + var re1 = /^([\-_a-zA-Z0-9]*)=(.*)$/; + var re2 = /^(0|.0|([0-9]*(\.[0-9]*)?))$/; + + for (i = 0; i < args.length; ++i) { + var a = args[i]; + var m = re1.exec(a); + + if (m !== null) { + var k = m[1]; + var v = m[2]; + + if (re2.test(v)) { + options[k] = parseFloat(v); + } + else { + options[k] = v; + } + } + else { + nargs.push(args[i]); + } + } + + return { 'options': options, 'args': nargs }; +} + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -481,17 +518,37 @@ exports.run = function (args) { var printf = arangodb.printf; var res; + function extractOptions () { + var co = extractCommandLineOptions(args); + + if (3 < co.args.length) { + var options = JSON.parse(co.args[3]); + + if (options.hasOwnProperty("configuration")) { + var k; + + for (k in co.options) { + if (co.options.hasOwnProperty(k)) { + options.configuration[k] = co.options[k]; + } + } + } + else { + options.configuration = co.options; + } + + return options; + } + + return { configuration: co.options }; + } + try { if (type === 'fetch') { exports.fetch(args[1], args[2], args[3]); } else if (type === 'mount') { - if (3 < args.length) { - exports.mount(args[1], args[2], JSON.parse(args[3])); - } - else { - exports.mount(args[1], args[2]); - } + exports.mount(args[1], args[2], extractOptions()); } else if (type === 'rescan') { exports.rescan(); @@ -506,16 +563,13 @@ exports.run = function (args) { exports.unmount(args[1]); } else if (type === 'install') { - if (3 < args.length) { - res = exports.install(args[1], args[2], JSON.parse(args[3])); - } - else { - res = exports.install(args[1], args[2]); - } + var options = extractOptions(); + res = exports.install(args[1], args[2], options); printf("Application %s installed successfully at mount point %s\n", res.appId, res.mount); + printf("options used: %s", JSON.stringify(options)); } else if (type === 'replace') { if (3 < args.length) { diff --git a/js/common/bootstrap/errors.js b/js/common/bootstrap/errors.js index 49167af3a6..72b4eb39a6 100644 --- a/js/common/bootstrap/errors.js +++ b/js/common/bootstrap/errors.js @@ -216,6 +216,8 @@ "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_GRAPH_COLLECTION_USED_IN_EDGE_DEF" : { "code" : 1929, "message" : "collection used in edge def" }, + "ERROR_GRAPH_EDGE_COLLECTION_NOT_USED" : { "code" : 1930, "message" : "edge collection not used in graph" }, "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/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index 4058ce4ac0..ba278623d5 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -2032,7 +2032,8 @@ var checkIfMayBeDropped = function(colName, graphName, graphs) { var _drop = function(graphId, dropCollections) { - var gdb = getGraphCollection(); + var gdb = getGraphCollection(), + graphs; if (!gdb.exists(graphId)) { var err = new ArangoError(); @@ -2049,7 +2050,7 @@ var _drop = function(graphId, dropCollections) { var from = edgeDefinition.from; var to = edgeDefinition.to; var collection = edgeDefinition.collection; - var graphs = getGraphCollection().toArray(); + graphs = getGraphCollection().toArray(); if (checkIfMayBeDropped(collection, graph._key, graphs)) { db._drop(collection); } @@ -2069,6 +2070,20 @@ var _drop = function(graphId, dropCollections) { ); } ); + //drop orphans + graphs = getGraphCollection().toArray(); + if (!graph.orphanCollections) { + graph.orphanCollections = []; + } + graph.orphanCollections.forEach( + function(oC) { + if (checkIfMayBeDropped(oC, graph._key, graphs)) { + try { + db._drop(oC); + } catch (e) {} + } + } + ); } gdb.remove(graphId); @@ -2481,9 +2496,10 @@ Graph.prototype._amountCommonProperties = function(vertex1Example, vertex2Exampl //////////////////////////////////////////////////////////////////////////////// /// @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. +/// Extends the edge definitions of a graph. If an orphan collection is used in this +/// edge definitino, it will be removed from the orphenage. 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)` /// @@ -2492,10 +2508,10 @@ Graph.prototype._amountCommonProperties = function(vertex1Example, vertex2Exampl /// @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]); +/// var graph = require("org/arangodb/general-graph") +/// var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph._directedRelationDefinition("myEC2", ["myVC1"], ["myVC3"]); +/// var g = graph._create("myGraph", [ed1]); /// g._extendEdgeDefinitions(ed2); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -2569,40 +2585,95 @@ Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) { }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal function for editing edge definitions +//////////////////////////////////////////////////////////////////////////////// + +var changeEdgeDefinitionsForGraph = function(graph, edgeDefinition, newCollections, possibleOrphans, self) { + + var oldCollections = []; + var graphCollections = []; + var graphObj = _graph(graph._key); + var eDs = graph.edgeDefinitions; + + //replace edgeDefintion + eDs.forEach( + function(eD, id) { + if(eD.collection === edgeDefinition.collection) { + oldCollections = _.union(oldCollections, eD.from); + oldCollections = _.union(oldCollections, 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; + } + } else { + //collect all used collections + graphCollections = _.union(graphCollections, eD.from); + graphCollections = _.union(graphCollections, eD.to); + } + } + ); + + + //remove used collection from orphanage + newCollections.forEach( + function(nc) { + if (graph._key === self.__name) { + if (self.__vertexCollections[nc] === undefined) { + self.__vertexCollections[nc] = db[nc]; + } + try { + graphObj._removeOrphanCollection(nc); + } catch (e) { + } + } + } + ); + + //move unused collections to orphanage + possibleOrphans.forEach( + function(po) { + if (graphCollections.indexOf(po) === -1) { + delete graphObj.__vertexCollections[po]; + graphObj._addOrphanCollection(po); + } + } + ); +}; //////////////////////////////////////////////////////////////////////////////// /// @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. +/// replace the existing edge definition of the graph which has the same collection. +/// Vertex Collections of the replaced edge definition, that are not used in the new +/// definition will transform to an orphan. Orphans that are used in this new edge +/// definition will be deleted from the list of orphans. Other graphs with the same edge +/// definition will be modified, too. /// -/// `general-graph._editEdgeDefinition(edgeDefinition, dropCollections)` +/// `general-graph._editEdgeDefinition(edgeDefinition)` /// /// *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. +/// definition with the same attribute *collection*. /// /// @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]); +/// var graph = require("org/arangodb/general-graph") +/// var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph._directedRelationDefinition("myEC1", ["myVC2"], ["myVC3"]); +/// var g = graph._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 = {}; - var exOrphanCandidates = []; - var effectedGraphs = []; +Graph.prototype._editEdgeDefinitions = function(edgeDefinition) { + var self = this; //check, if in graphs edge definition if (this.__edgeCollections[edgeDefinition.collection] === undefined) { @@ -2612,108 +2683,33 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) throw err; } + findOrCreateCollectionsByEdgeDefinitions([edgeDefinition]); + + //evaluate collections to add to orphanage + var possibleOrphans = []; + var currentEdgeDefinition; + this.__edgeDefinitions.forEach( + function(ed) { + if (edgeDefinition.collection === ed.collection) { + currentEdgeDefinition = ed; + } + } + ); + + var currentCollections = _.union(currentEdgeDefinition.from, currentEdgeDefinition.to); + var newCollections = _.union(edgeDefinition.from, edgeDefinition.to); + currentCollections.forEach( + function(colName) { + if (newCollections.indexOf(colName) === -1) { + possibleOrphans.push(colName); + } + } + ); //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}); - effectedGraphs.push(graph._key); - 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) { - exOrphanCandidates.push(vc); - self.__vertexCollections[vc] = db[vc]; - } - } - ); - edgeDefinition.to.forEach( - function(vc) { - if (self.__vertexCollections[vc] === undefined) { - exOrphanCandidates.push(vc); - 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]; - } - } - ); - - //orphans treatment - effectedGraphs.forEach( - function(gN) { - var g; - if (gN === self.__name) { - g = self; - } else { - g = _graph(gN); - } - var orphans = g._getOrphanCollections(); - exOrphanCandidates.forEach( - function(eOC) { - if (orphans.indexOf(eOC) !== -1) { - g._removeOrphanCollection(eOC); - } - } - ); + changeEdgeDefinitionsForGraph(graph, edgeDefinition, newCollections, possibleOrphans, self); } ); @@ -2724,72 +2720,63 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) /// @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. +/// in another edge definition of the graph, they will be moved to the orphanage. /// -/// `general-graph._deleteEdgeDefinition(edgeCollectionName, dropCollections)` +/// `general-graph._deleteEdgeDefinition(edgeCollectionName)` /// /// *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); +/// var graph = require("org/arangodb/general-graph") +/// var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph._directedRelationDefinition("myEC2", ["myVC1"], ["myVC3"]); +/// var g = graph._create("myGraph", [ed1, ed2]); +/// g._deleteEdgeDefinition("myEC1"); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// /// @endDocuBlock /// //////////////////////////////////////////////////////////////////////////////// -Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections) { +Graph.prototype._deleteEdgeDefinition = function(edgeCollection) { + + //check, if in graphs edge definition + if (this.__edgeCollections[edgeCollection] === 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; + } + var edgeDefinitions = this.__edgeDefinitions, - vertexCollections = [], - definitionFound = false, + self = this, + usedVertexCollections = [], + possibleOrphans = [], 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); - } - } - ); - } + index = idx; + possibleOrphans = edgeDefinition.from; + possibleOrphans = _.union(possibleOrphans, edgeDefinition.to); + } else { + usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.from); + usedVertexCollections = _.union(usedVertexCollections, edgeDefinition.to); } } ); - 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); - } + this.__edgeDefinitions.splice(index, 1); + possibleOrphans.forEach( + function(po) { + if (usedVertexCollections.indexOf(po) === -1) { + self.__orphanCollections.push(po); } - ); - - } - + } + ); }; //////////////////////////////////////////////////////////////////////////////// @@ -2805,9 +2792,9 @@ Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections /// @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]); +/// var graph = require("org/arangodb/general-graph") +/// var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = graph._create("myGraph", [ed1]); /// g._addOrphanCollection("myVC3", true); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -2815,17 +2802,17 @@ Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollections /// //////////////////////////////////////////////////////////////////////////////// -Graph.prototype._addOrphanCollection = function(vertexCollection, createCollection) { +Graph.prototype._addOrphanCollection = function(orphanCollectionName, createCollection) { //check edgeCollection - var ec = db._collection(vertexCollection); + var ec = db._collection(orphanCollectionName); var err; if (ec === null) { if (createCollection !== false) { - db._create(vertexCollection); + db._create(orphanCollectionName); } 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; + err.errorMessage = orphanCollectionName + arangodb.errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.message; throw err; } } else if (ec.type() !== 2) { @@ -2834,8 +2821,14 @@ Graph.prototype._addOrphanCollection = function(vertexCollection, createCollecti err.errorMessage = arangodb.errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.message; throw err; } + if (this.__vertexCollections[orphanCollectionName] !== undefined) { + err = new ArangoError(); + err.errorNum = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code; + err.errorMessage = arangodb.errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message; + throw err; + } - this.__orphanCollections.push(vertexCollection); + this.__orphanCollections.push(orphanCollectionName); db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections}); }; @@ -2849,9 +2842,9 @@ Graph.prototype._addOrphanCollection = function(vertexCollection, createCollecti /// @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]); +/// var graph = require("org/arangodb/general-graph") +/// var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = graph._create("myGraph", [ed1]); /// g._addOrphanCollection("myVC3", true); /// g._getOrphanCollections(); /// @END_EXAMPLE_ARANGOSH_OUTPUT @@ -2878,9 +2871,9 @@ Graph.prototype._getOrphanCollections = function() { /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__removeOrphanCollections} -/// var examples = require("org/arangodb/graph-examples/example-graph.js"); -/// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); -/// var g = examples._create("myGraph", [ed1]); +/// var graph = require("org/arangodb/general-graph") +/// var ed1 = graph._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); +/// var g = graph._create("myGraph", [ed1]); /// g._addOrphanCollection("myVC3", true); /// g._addOrphanCollection("myVC4", true); /// g._getOrphanCollections(); diff --git a/js/common/tests/shell-general-graph.js b/js/common/tests/shell-general-graph.js index 86085c7873..9712a787a0 100644 --- a/js/common/tests/shell-general-graph.js +++ b/js/common/tests/shell-general-graph.js @@ -60,8 +60,52 @@ function GeneralGraphCreationSuite() { ) ); + var gN1 = "UnitTestEdgeDefDeleteGraph1", + gN2 = "UnitTestEdgeDefDeleteGraph2", + ec1 = "UnitTestEdgeDefDeleteEdgeCol1", + ec2 = "UnitTestEdgeDefDeleteEdgeCol2", + ec3 = "UnitTestEdgeDefDeleteEdgeCol3", + vc1 = "UnitTestEdgeDefDeleteVertexCol1", + vc2 = "UnitTestEdgeDefDeleteVertexCol2", + vc3 = "UnitTestEdgeDefDeleteVertexCol3", + vc4 = "UnitTestEdgeDefDeleteVertexCol4", + vc5 = "UnitTestEdgeDefDeleteVertexCol5", + vc6 = "UnitTestEdgeDefDeleteVertexCol6"; + return { + setUp: function() { + try { + graph._drop(gN1); + } catch(ignore) { + } + try { + graph._drop(gN2); + } catch(ignore) { + } + + }, + + tearDown: function() { + db._drop(ec1); + db._drop(ec2); + db._drop(ec3); + db._drop(vc1); + db._drop(vc2); + db._drop(vc3); + db._drop(vc4); + db._drop(vc5); + db._drop(vc6); + try { + graph._drop(gN1); + } catch(ignore) { + } + try { + graph._drop(gN2); + } catch(ignore) { + } + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test: Graph Creation //////////////////////////////////////////////////////////////////////////////// @@ -413,58 +457,39 @@ function GeneralGraphCreationSuite() { }, - test_deleteEdgeDefinitionFromExistingGraph: function() { - var gN1 = "UnitTestEdgeDefDeleteGraph1", - gN2 = "UnitTestEdgeDefDeleteGraph2", - ec1 = "UnitTestEdgeDefDeleteEdgeCol1", - ec2 = "UnitTestEdgeDefDeleteEdgeCol2", - ec3 = "UnitTestEdgeDefDeleteEdgeCol3", - vc1 = "UnitTestEdgeDefDeleteVertexCol1", - vc2 = "UnitTestEdgeDefDeleteVertexCol2", - vc3 = "UnitTestEdgeDefDeleteVertexCol3", - vc4 = "UnitTestEdgeDefDeleteVertexCol4", - vc5 = "UnitTestEdgeDefDeleteVertexCol5"; - try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { - } - + test_deleteEdgeDefinitionFromExistingGraph1: function() { var dr1 = graph._directedRelationDefinition(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelationDefinition(ec2, [vc3], [vc4, vc5]), - dr3 = graph._directedRelationDefinition(ec3, [vc4], [vc5]), - g1 = graph._create(gN1, [dr1, dr2, dr3]), - g2 = graph._create(gN2, [dr3]); - - g1._deleteEdgeDefinition(ec1, false); - assertEqual([dr2, dr3], g1.__edgeDefinitions); - - g1._deleteEdgeDefinition(ec2, true); - assertEqual([dr3], g1.__edgeDefinitions); - assertTrue(db._collection(vc3) === null); - assertFalse(db._collection(vc4) === null); - assertFalse(db._collection(vc5) === null); + g1 = graph._create(gN1, [dr1]); try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { + g1._deleteEdgeDefinition(ec1); + } catch (e) { + assertEqual( + e.errorMessage, + arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message + ); } }, + test_deleteEdgeDefinitionFromExistingGraph2: function() { + + var dr1 = graph._directedRelationDefinition(ec1, [vc1], [vc1, vc2]), + dr2 = graph._directedRelationDefinition(ec2, [vc3], [vc4, vc5]), + dr3 = graph._directedRelationDefinition(ec3, [vc4], [vc5]), + g1 = graph._create(gN1, [dr1, dr2, dr3]); + + assertEqual([dr1, dr2, dr3], g1.__edgeDefinitions); + g1._deleteEdgeDefinition(ec1); + assertEqual([dr2, dr3], g1.__edgeDefinitions); + assertEqual([vc1, vc2], g1._getOrphanCollections()); + + g1._deleteEdgeDefinition(ec2); + assertEqual([dr3], g1.__edgeDefinitions); + assertEqual([vc1, vc2, vc3], g1._getOrphanCollections()); + }, + test_extendEdgeDefinitionFromExistingGraph1: function() { - var gN1 = "UnitTestEdgeDefExtend1Graph1", - ec1 = "UnitTestEdgeDefExtend1EdgeCol1", - vc1 = "UnitTestEdgeDefExtend1VertexCol1", - vc2 = "UnitTestEdgeDefExtend1VertexCol2", - vc3 = "UnitTestEdgeDefExtend1VertexCol3"; try { graph._drop(gN1); @@ -492,24 +517,6 @@ function GeneralGraphCreationSuite() { }, test_extendEdgeDefinitionFromExistingGraph2: function() { - var gN1 = "UnitTestEdgeDefExtend2Graph1", - gN2 = "UnitTestEdgeDefExtend2Graph2", - ec1 = "UnitTestEdgeDefExtend2EdgeCol1", - ec2 = "UnitTestEdgeDefExtend2EdgeCol2", - ec3 = "UnitTestEdgeDefExtend2EdgeCol3", - vc1 = "UnitTestEdgeDefExtend2VertexCol1", - vc2 = "UnitTestEdgeDefExtend2VertexCol2", - vc3 = "UnitTestEdgeDefExtend2VertexCol3", - vc4 = "UnitTestEdgeDefExtend2VertexCol4", - vc5 = "UnitTestEdgeDefExtend2VertexCol5"; - try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { - } var dr1 = graph._directedRelationDefinition(ec1, [vc1], [vc1, vc2]), dr2 = graph._directedRelationDefinition(ec2, [vc3], [vc4, vc5]), @@ -538,16 +545,6 @@ function GeneralGraphCreationSuite() { }, test_extendEdgeDefinitionFromExistingGraph3: function() { - var gN1 = "UnitTestEdgeDefExtend3Graph1", - gN2 = "UnitTestEdgeDefExtend3Graph2", - ec1 = "UnitTestEdgeDefExtend3EdgeCol1", - ec2 = "UnitTestEdgeDefExtend3EdgeCol2", - ec3 = "UnitTestEdgeDefExtend3EdgeCol3", - vc1 = "UnitTestEdgeDefExtend3VertexCol1", - vc2 = "UnitTestEdgeDefExtend3VertexCol2", - vc3 = "UnitTestEdgeDefExtend3VertexCol3", - vc4 = "UnitTestEdgeDefExtend3VertexCol4", - vc5 = "UnitTestEdgeDefExtend3VertexCol5"; try { graph._drop(gN1); } catch(ignore) { @@ -572,32 +569,9 @@ function GeneralGraphCreationSuite() { g1._extendEdgeDefinitions(dr3); assertEqual([dr1, dr2, dr3], g1.__edgeDefinitions); - - try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { - } - }, test_editEdgeDefinitionFromExistingGraph1: function() { - var gN1 = "UnitTestEdgeDefEdit1Graph1", - ec1 = "UnitTestEdgeDefEdit1EdgeCol1", - ec2 = "UnitTestEdgeDefEdit1EdgeCol2", - vc1 = "UnitTestEdgeDefEdit1VertexCol1", - vc2 = "UnitTestEdgeDefEdit1VertexCol2", - vc3 = "UnitTestEdgeDefEdit1VertexCol3", - vc4 = "UnitTestEdgeDefEdit1VertexCol4", - vc5 = "UnitTestEdgeDefEdit1VertexCol5"; - try { - graph._drop(gN1); - } catch(ignore) { - } - var dr1 = graph._directedRelationDefinition(ec1, [vc1], [vc1, vc2]), dr2 = graph._directedRelationDefinition(ec2, [vc3], [vc4, vc5]), g1 = graph._create(gN1, [dr1]); @@ -607,77 +581,35 @@ function GeneralGraphCreationSuite() { } catch (e) { assertEqual( e.errorMessage, - arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED + arangodb.errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message ); } - try { - graph._drop(gN1); - } catch(ignore) { - } - }, test_editEdgeDefinitionFromExistingGraph2: function() { - var gN1 = "UnitTestEdgeDefEdit2Graph1", - gN2 = "UnitTestEdgeDefEdit2Graph2", - ec1 = "UnitTestEdgeDefEdit2EdgeCol1", - vc1 = "UnitTestEdgeDefEdit2VertexCol1", - vc2 = "UnitTestEdgeDefEdit2VertexCol2", - vc3 = "UnitTestEdgeDefEdit2VertexCol3", - vc4 = "UnitTestEdgeDefEdit2VertexCol4", - vc5 = "UnitTestEdgeDefEdit2VertexCol5"; - try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { - } - var dr1 = graph._directedRelationDefinition(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelationDefinition(ec1, [vc3], [vc4, vc5]), - g1 = graph._create(gN1, [dr1]), + var dr1 = graph._directedRelationDefinition(ec1, [vc1, vc2], [vc3, vc4]), + dr2 = graph._directedRelationDefinition(ec2, [vc1], [vc4]), + dr3 = graph._directedRelationDefinition(ec1, [vc5], [vc5]), + g1 = graph._create(gN1, [dr1, dr2]), g2 = graph._create(gN2, [dr1]); - g1._editEdgeDefinitions(dr2, true); - assertEqual([dr2], g1.__edgeDefinitions); - assertEqual([dr2], g2.__edgeDefinitions); - assertTrue(db._collection(vc1) === null); - assertTrue(db._collection(vc2) === null); - - try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { - } + g1._editEdgeDefinitions(dr3); + assertEqual([dr3, dr2], g1.__edgeDefinitions); + assertEqual([dr3], g2.__edgeDefinitions); + g1 = graph._graph(gN1); + g2 = graph._graph(gN2); + assertTrue(g1._getOrphanCollections().indexOf(vc2) !== -1); + assertTrue(g1._getOrphanCollections().indexOf(vc3) !== -1); + assertTrue(g2._getOrphanCollections().indexOf(vc1) !== -1); + assertTrue(g2._getOrphanCollections().indexOf(vc2) !== -1); + assertTrue(g2._getOrphanCollections().indexOf(vc3) !== -1); + assertTrue(g2._getOrphanCollections().indexOf(vc4) !== -1); }, test_editEdgeDefinitionFromExistingGraph3: function() { - var prefix = "UnitTestEdgeDefEdit3", - gN1 = prefix + "Graph1", - gN2 = prefix + "Graph2", - ec1 = prefix + "EdgeCol1", - ec2 = prefix + "EdgeCol2", - vc1 = prefix + "VertexCol1", - vc2 = prefix + "VertexCol2", - vc3 = prefix + "VertexCol3", - vc4 = prefix + "VertexCol4", - vc5 = prefix + "VertexCol5", - vc6 = prefix + "VertexCol6"; - try { - graph._drop(gN1); - } catch(ignore) { - } - try { - graph._drop(gN2); - } catch(ignore) { - } var dr1 = graph._directedRelationDefinition(ec1, [vc1], [vc1, vc2]), dr2 = graph._directedRelationDefinition(ec1, [vc3], [vc4, vc5]), @@ -689,13 +621,15 @@ function GeneralGraphCreationSuite() { g2._addOrphanCollection(vc5); g2._addOrphanCollection(vc6); g1._editEdgeDefinitions(dr2, true); + assertEqual([dr2, dr3], g1.__edgeDefinitions); assertEqual([dr2], g2.__edgeDefinitions); - assertTrue(db._collection(vc1) === null); - assertFalse(db._collection(vc2) === null); - assertEqual([], g1._getOrphanCollections()); + g1 = graph._graph(gN1); g2 = graph._graph(gN2); - assertEqual([vc6], g2._getOrphanCollections()); + assertEqual([vc1], g1._getOrphanCollections()); + assertTrue(g2._getOrphanCollections().indexOf(vc1) !== -1); + assertTrue(g2._getOrphanCollections().indexOf(vc2) !== -1); + assertTrue(g2._getOrphanCollections().indexOf(vc6) !== -1); try { graph._drop(gN1); @@ -2562,8 +2496,8 @@ function OrphanCollectionSuite() { }, test_addOrphanCollection1: function() { - g1._addOrphanCollection(vC1, false); - assertEqual(g1._getOrphanCollections(), [vC1]); + g1._addOrphanCollection(vC5, true); + assertEqual(g1._getOrphanCollections(), [vC5]); }, test_addOrphanCollection2: function() { @@ -2588,6 +2522,15 @@ function OrphanCollectionSuite() { assertEqual(g1._getOrphanCollections(), []); }, + test_addOrphanCollection4: function() { + try { + g1._addOrphanCollection(vC1); + } catch (e) { + assertEqual(e.errorNum, ERRORS.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code); + assertEqual(e.errorMessage, ERRORS.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.message); + } + }, + test_removeOrphanCollection1: function() { var name = "completelyNonsenseNameForACollectionBLUBBBBB" try { diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index 043d2d2a39..9fcd2dc169 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -4437,13 +4437,15 @@ function TRAVERSAL_FUNC (func, maxIterations: params.maxIterations, uniqueness: params.uniqueness, expander: direction, + direction: direction, strategy: params.strategy, order: params.order, itemOrder: params.itemOrder, startVertex : startVertex, endVertex : endVertex, weight : params.weight, - defaultWeight : params.defaultWeight + defaultWeight : params.defaultWeight, + prefill : params.prefill }; @@ -4643,18 +4645,6 @@ function DETERMINE_WEIGHT (edge, weight, defaultWeight) { return Infinity; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief visitor callback function for traversal -//////////////////////////////////////////////////////////////////////////////// - -function TRAVERSAL_SHORTEST_PATH_VISITOR (config, result, vertex, path) { - "use strict"; - - if (config.endVertex && config.endVertex === vertex._id) { - result.push(CLONE({ vertex: vertex, path: path , startVertex : config.startVertex})); - } -} - //////////////////////////////////////////////////////////////////////////////// /// @brief visitor callback function for traversal @@ -4664,18 +4654,19 @@ function TRAVERSAL_DISTANCE_VISITOR (config, result, vertex, path) { "use strict"; if (config.endVertex && config.endVertex === vertex._id) { + var subPaths = []; var dist = 0; - if (config.weight) { - path.edges.forEach(function (e) { + path.edges.forEach(function (e) { + if (config.weight) { if (typeof e[config.weight] === "number") { dist = dist + e[config.weight]; } else if (config.defaultWeight) { dist = dist + config.defaultWeight; } - }); - } else { - dist = path.edges.length; - } + } else { + dist++; + } + }); result.push( CLONE({ vertex: vertex, distance: dist , path: path , startVertex : config.startVertex}) ); @@ -4683,6 +4674,62 @@ function TRAVERSAL_DISTANCE_VISITOR (config, result, vertex, path) { } +//////////////////////////////////////////////////////////////////////////////// +/// @brief visitor callback function for traversal +//////////////////////////////////////////////////////////////////////////////// + +function TRAVERSAL_DIJSKTRA_VISITOR (config, result, vertex, path) { + "use strict"; + if (config.endVertex && config.endVertex === vertex._id) { + path.vertices.forEach(function (from) { + path.vertices.forEach(function (to) { + if (config.prefill.indexOf(JSON.stringify({ from : TO_ID(from), to : TO_ID(to)})) !== -1) { + return; + } + var positionFrom = path.vertices.indexOf(from); + var positionTo = path.vertices.indexOf(to); + if (positionFrom > positionTo && config.direction !== 'any') { + return; + } + var startVertex = from._id; + var vertex = to; + + var distance = 0; + var pathNew = {vertices : [from], edges : []}; + while (positionFrom !== positionTo) { + var edgePosition; + if (positionFrom > positionTo) { + edgePosition = positionFrom-1; + } else { + edgePosition = positionFrom; + } + if (positionFrom > positionTo) { + positionFrom = positionFrom -1; + } else { + positionFrom ++; + } + pathNew.vertices.push(path.vertices[positionFrom]); + pathNew.edges.push(path.edges[edgePosition]); + if (config.weight) { + if (path.edges[edgePosition][config.weight] && + typeof path.edges[edgePosition][config.weight] === "number") { + distance = distance + path.edges[edgePosition][config.weight]; + } else if (config.defaultWeight) { + distance = distance + config.defaultWeight; + } + } else { + distance++; + } + } + result.push( + CLONE({ vertex: vertex, distance: distance , path: pathNew , startVertex : startVertex}) + ); + }); + }); + } +} + + //////////////////////////////////////////////////////////////////////////////// /// @brief helper function to determine parameters for SHORTEST_PATH and @@ -4920,6 +4967,8 @@ function MERGE_EXAMPLES_WITH_EDGES (examples, edges) { return result; } + + //////////////////////////////////////////////////////////////////////////////// /// @brief calculate shortest paths by dijkstra //////////////////////////////////////////////////////////////////////////////// @@ -4931,17 +4980,29 @@ function CALCULATE_SHORTEST_PATHES_WITH_DIJKSTRA (graphName, graphData, options) params.weight = options.weight; params.defaultWeight = options.defaultWeight; params = SHORTEST_PATH_PARAMS(params); - params.visitor = TRAVERSAL_DISTANCE_VISITOR; + params.visitor = TRAVERSAL_DIJSKTRA_VISITOR; var result = []; + + var calculated = {}; graphData.fromVertices.forEach(function (v) { graphData.toVertices.forEach(function (t) { + if (calculated[JSON.stringify({ from : TO_ID(v), to : TO_ID(t)})]) { + result.push(calculated[JSON.stringify({ from : TO_ID(v), to : TO_ID(t)})]); + return; + } + params.prefill = Object.keys(calculated); var e = TRAVERSAL_FUNC("GENERAL_GRAPH_SHORTEST_PATH", factory, TO_ID(v), TO_ID(t), options.direction, params); - result = result.concat(e); + e.forEach(function (f) { + if (TO_ID(v) === f.startVertex && TO_ID(t) === f.vertex._id) { + result.push(f); + } + calculated[JSON.stringify({ from : f.startVertex, to : f.vertex._id})] = f; + }); }); }); result.forEach(function (r) { diff --git a/js/server/modules/org/arangodb/foxx/manager.js b/js/server/modules/org/arangodb/foxx/manager.js index 75a3172d47..57e79bede4 100644 --- a/js/server/modules/org/arangodb/foxx/manager.js +++ b/js/server/modules/org/arangodb/foxx/manager.js @@ -88,6 +88,7 @@ function getStorage () { //////////////////////////////////////////////////////////////////////////////// /// @brief check a manifest for completeness +/// /// this implements issue #590: Manifest Lint //////////////////////////////////////////////////////////////////////////////// @@ -120,14 +121,15 @@ function checkManifest (filename, mf) { var expected = { "assets": [ false, "object" ], "author": [ false, "string" ], + "configuration": [ false, "object" ], "contributors": [ false, "array" ], "controllers": [ false, "object" ], "defaultDocument": [ false, "string" ], "description": [ true, "string" ], "engines": [ false, "object" ], "files": [ false, "object" ], - "keywords": [ false, "array" ], "isSystem": [ false, "boolean" ], + "keywords": [ false, "array" ], "lib": [ false, "string" ], "license": [ false, "string" ], "name": [ true, "string" ], @@ -363,9 +365,9 @@ function buildFileAsset (app, path, basePath, asset) { var content = buildAssetContent(app, asset.files, basePath); var type; - // ----------------------------------------------------------------------------- + // ............................................................................. // content-type detection - // ----------------------------------------------------------------------------- + // ............................................................................. // contentType explicitly specified for asset if (asset.hasOwnProperty("contentType") && asset.contentType !== '') { @@ -388,9 +390,9 @@ function buildFileAsset (app, path, basePath, asset) { type = arangodb.guessContentType(""); } - // ----------------------------------------------------------------------------- + // ............................................................................. // return content - // ----------------------------------------------------------------------------- + // ............................................................................. return { contentType: type, body: content }; } @@ -528,6 +530,7 @@ function executeAppScript (app, name, mount, prefix) { appContext.mount = mount; appContext.collectionPrefix = prefix; appContext.options = app._options; + appContext.configuration = app._options.configuration; appContext.basePath = fs.join(root, app._path); appContext.isDevelopment = devel; @@ -747,6 +750,7 @@ function routingAalApp (app, mount, options) { appContextTempl.mount = mount; // global mount appContextTempl.options = options; + appContextTempl.configuration = app._options.configuration; appContextTempl.collectionPrefix = prefix; // collection prefix appContextTempl.basePath = fs.join(root, app._path); @@ -908,6 +912,68 @@ function scanDirectory (path) { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create configuration +//////////////////////////////////////////////////////////////////////////////// + +function checkConfiguration (app, options) { + 'use strict'; + + if (options === undefined || options === null) { + options = {}; + } + + if (! options.hasOwnProperty("configuration")) { + options.configuration = {}; + } + + if (! app._manifest.hasOwnProperty("configuration")) { + return options; + } + + var configuration = options.configuration; + var expected = app._manifest.configuration; + var att; + + for (att in expected) { + if (expected.hasOwnProperty(att)) { + if (configuration.hasOwnProperty(att)) { + var value = configuration[att]; + var expectedType = expected[att].type; + var actualType = Array.isArray(value) ? "array" : typeof(value); + + if (actualType !== expectedType) { + throw new Error( + "configuration for '" + app._manifest.name + "' uses " + + "an invalid data type (" + actualType + ") " + + "for " + expectedType + " attribute '" + att + "'"); + } + } + else if (expected[att].hasOwnProperty("default")) { + configuration[att] = expected[att]["default"]; + } + else { + throw new Error( + "configuration for '" + app._manifest.name + "' is " + + "missing a value for attribute '" + att + "'"); + } + } + } + + // additionally check if there are superfluous attributes in the manifest + for (att in configuration) { + if (configuration.hasOwnProperty(att)) { + if (! expected.hasOwnProperty(att)) { + console.warn("configuration for '%s' contains an unknown attribute '%s'", + app._manifest.name, + att); + } + } + } + + return options; +} + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -950,6 +1016,7 @@ exports.rescan = function () { /// * options: /// collectionPrefix: overwrites the default prefix /// reload: reload the routing info (default: true) +/// configuration: configuration options /// /// Output: /// * appId: the application identifier (must be mounted) @@ -983,7 +1050,7 @@ exports.mount = function (appId, mount, options) { // install the application // ............................................................................. - options = options || { }; + options = checkConfiguration(app, options); var doc; diff --git a/js/server/tests/ahuacatl-general-graph.js b/js/server/tests/ahuacatl-general-graph.js index 1fb7acd6bc..e9a329aebd 100644 --- a/js/server/tests/ahuacatl-general-graph.js +++ b/js/server/tests/ahuacatl-general-graph.js @@ -1475,6 +1475,16 @@ function ahuacatlQueryGeneralTraversalTestSuite() { assertEqual(actual[0]["UnitTests_Leipziger/Gerda"], 1); + actual = getQueryResults("RETURN GRAPH_ECCENTRICITY('werKenntWen')"); + assertEqual(actual[0]["UnitTests_Berliner/Anton"].toFixed(1), 0.6); + assertEqual(actual[0]["UnitTests_Berliner/Berta"].toFixed(2), 0.75); + assertEqual(actual[0]["UnitTests_Frankfurter/Emil"].toFixed(2), 0.75); + assertEqual(actual[0]["UnitTests_Frankfurter/Fritz"].toFixed(1), 0.6); + assertEqual(actual[0]["UnitTests_Hamburger/Caesar"].toFixed(1), 0.6); + assertEqual(actual[0]["UnitTests_Hamburger/Dieter"], 1); + assertEqual(actual[0]["UnitTests_Leipziger/Gerda"], 1); + + actual = getQueryResults("RETURN GRAPH_ECCENTRICITY('werKenntWen', {algorithm : 'dijkstra', direction : 'inbound'})"); assertEqual(actual[0]["UnitTests_Berliner/Anton"], 1); assertEqual(actual[0]["UnitTests_Berliner/Berta"], 1); diff --git a/lib/BasicsC/errors.dat b/lib/BasicsC/errors.dat index e502e42237..5b1b1574eb 100755 --- a/lib/BasicsC/errors.dat +++ b/lib/BasicsC/errors.dat @@ -296,6 +296,8 @@ ERROR_GRAPH_DUPLICATE,1925,"graph already exists","a graph with this name alread 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.", +ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF,1929,"collection used in edge def","The collection is already used in an edge definition of the graph.", +ERROR_GRAPH_EDGE_COLLECTION_NOT_USED,1930,"edge collection not used in graph","The edge collection is not used in any edge definition of the graph.", ################################################################################ ## Session errors diff --git a/lib/BasicsC/voc-errors.c b/lib/BasicsC/voc-errors.c index 87c251f84f..4e7d2df694 100644 --- a/lib/BasicsC/voc-errors.c +++ b/lib/BasicsC/voc-errors.c @@ -212,6 +212,8 @@ void TRI_InitialiseErrorMessages (void) { 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_GRAPH_COLLECTION_USED_IN_EDGE_DEF, "collection used in edge def"); + REG_ERROR(ERROR_GRAPH_EDGE_COLLECTION_NOT_USED, "edge collection not used in graph"); 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 61e6534c41..55106de472 100644 --- a/lib/BasicsC/voc-errors.h +++ b/lib/BasicsC/voc-errors.h @@ -498,6 +498,10 @@ extern "C" { /// the collection is not a vertex collection. /// - 1928: @LIT{not in orphan collection} /// Vertex collection not in orphan collection of the graph. +/// - 1929: @LIT{collection used in edge def} +/// The collection is already used in an edge definition of the graph. +/// - 1930: @LIT{edge collection not used in graph} +/// The edge collection is not used in any edge definition 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} @@ -2638,6 +2642,26 @@ void TRI_InitialiseErrorMessages (void); #define TRI_ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION (1928) +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1929: ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF +/// +/// collection used in edge def +/// +/// The collection is already used in an edge definition of the graph. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF (1929) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief 1930: ERROR_GRAPH_EDGE_COLLECTION_NOT_USED +/// +/// edge collection not used in graph +/// +/// The edge collection is not used in any edge definition of the graph. +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_ERROR_GRAPH_EDGE_COLLECTION_NOT_USED (1930) + //////////////////////////////////////////////////////////////////////////////// /// @brief 1950: ERROR_SESSION_UNKNOWN ///