From 449936b99e97ce66821227b7e1570365847d6f6a Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Tue, 9 Sep 2014 12:53:31 +0200 Subject: [PATCH 01/13] Added a more precise information returned from gharial for collections --- js/apps/system/gharial/gharial.js | 50 +++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/js/apps/system/gharial/gharial.js b/js/apps/system/gharial/gharial.js index c3aaa4e10a..cc59f39c59 100644 --- a/js/apps/system/gharial/gharial.js +++ b/js/apps/system/gharial/gharial.js @@ -32,6 +32,7 @@ var FoxxController = require("org/arangodb/foxx").Controller, controller = new FoxxController(applicationContext), + cluster = require("org/arangodb/cluster"), ArangoError = require("org/arangodb").ArangoError, actions = require("org/arangodb/actions"), Model = require("org/arangodb/foxx").Model, @@ -43,6 +44,41 @@ toId = function(c, k) { return c + "/" + k; }, + collectionRepresentation = function(collection, showProperties, showCount, showFigures) { + var result = {}; + result.id = collection._id; + result.name = collection.name(); + result.isSystem = (result.name.charAt(0) === '_'); + if (showProperties) { + var properties = collection.properties(); + result.doCompact = properties.doCompact; + result.isVolatile = properties.isVolatile; + result.journalSize = properties.journalSize; + result.keyOptions = properties.keyOptions; + result.waitForSync = properties.waitForSync; + if (cluster.isCoordinator()) { + result.shardKeys = properties.shardKeys; + result.numberOfShards = properties.numberOfShards; + } + } + + if (showCount) { + result.count = collection.count(); + } + + if (showFigures) { + var figures = collection.figures(); + + if (figures) { + result.figures = figures; + } + } + + result.status = collection.status(); + result.type = collection.type(); + + return result; + }, buildError = function(err, code) { return { error: true, @@ -366,9 +402,17 @@ controller.get("/:graph/vertex", function(req, res) { var name = req.params("graph"); var g = Graph._graph(name); - setResponse(res, "collections", _.map(g._vertexCollections(), function(c) { - return c.name(); - }).sort(), actions.HTTP_OK); + var mapFunc; + if (req.params("collectionObjects")) { + mapFunc = function(c) { + return collectionRepresentation(c, false, false, false); + }; + } else { + mapFunc = function(c) { + return c.name(); + }; + } + setResponse(res, "collections", _.map(g._vertexCollections(), mapFunc).sort(), actions.HTTP_OK); }) .pathParam("graph", { type: graphName From fe09b081687ef31c077977a94b94e7fbe0accc4a Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 13:38:56 +0200 Subject: [PATCH 02/13] updated CHANGELOG --- CHANGELOG | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ea9d4e1392..d8d88fc0fe 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -77,9 +77,40 @@ v2.3.0 (XXXX-XX-XX) storing JavaScript date objects in the database in a sensible manner. -v2.2.3 (2014-XX-XX) +v2.2.4 (2014-XX-XX) ------------------- +* fixed issue #1016: AQL editor bug + +* fixed issue #1014: WITHIN function returns wrong distance + +* fixed AQL shortest path calculation in function `GRAPH_SHORTEST_PATH` to return + complete vertex objects instead of just vertex ids + +* allow changing of attributes of documents stored in server-side JavaScript variables + + Previously, the following did not work: + + var doc = db.collection.document(key); + doc._key = "abc"; // overwriting internal attributes not supported + doc.value = 123; // overwriting existing attributes not supported + + Now, modifying documents stored in server-side variables (e.g. `doc` in the above case) + is supported. Modifying the variables will not update the documents in the database, + but will modify the JavaScript object (which can be written back to the database using + `db.collection.update` or `db.collection.replace`) + +* fixed issue #997: arangoimp apparently doesn't support files >2gig on Windows + + large file support (requires using `_stat64` instead of `stat`) is now supported on + Windows + + +v2.2.3 (2014-09-02) +------------------- + +* added `around` for Foxx controller + * added `type` option for HTTP API `GET /_api/document?collection=...` This allows controlling the type of results to be returned. By default, paths to From 7d743475909ff44ec8a227999ee41b33a6e19b46 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 13:37:09 +0200 Subject: [PATCH 03/13] issue #1016: AQL editor bug --- js/apps/system/aardvark/frontend/js/views/queryView.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/apps/system/aardvark/frontend/js/views/queryView.js b/js/apps/system/aardvark/frontend/js/views/queryView.js index 8daee3e1f1..9c46614413 100644 --- a/js/apps/system/aardvark/frontend/js/views/queryView.js +++ b/js/apps/system/aardvark/frontend/js/views/queryView.js @@ -207,7 +207,6 @@ var inputEditor = ace.edit("aqlEditor"); inputEditor.getSession().setMode("ace/mode/aql"); inputEditor.setFontSize("16px"); - inputEditor.setOptions({fontFamily: "Courier New"}); inputEditor.commands.addCommand({ name: "togglecomment", bindKey: {win: "Ctrl-Shift-C", linux: "Ctrl-Shift-C", mac: "Command-Shift-C"}, From 1d2c21aec5a4e0f76d2251e9e6a712cbe4349299 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 14:15:37 +0200 Subject: [PATCH 04/13] create verbose plans --- arangod/Aql/ExecutionNode.cpp | 2 +- arangod/Aql/Query.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 8479b0fbc6..916b27ef0d 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -366,7 +366,7 @@ Variable* ExecutionNode::varFromJson (Ast* ast, } else { std::string msg; - msg += "Mandatory variable \"" + std::string(variableName) + "\"not found."; + msg += "Mandatory variable \"" + std::string(variableName) + "\" not found."; THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, msg.c_str()); } } diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index ab53ccf90b..6865389eeb 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -84,6 +84,7 @@ Query::Query (TRI_vocbase_t* vocbase, _queryJson(queryStruct), _type(Type), _bindParameters(nullptr), + _options(nullptr), _collections(vocbase), _strings() { @@ -99,6 +100,7 @@ Query::Query (TRI_vocbase_t* vocbase, Query::~Query () { if (_options != nullptr) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _options); + _options = nullptr; } if (_executor != nullptr) { @@ -398,7 +400,7 @@ QueryResult Query::explain (bool returnAllPlans) { for (auto it : plans) { TRI_ASSERT(it != nullptr); - out.add(it->toJson(TRI_UNKNOWN_MEM_ZONE, false)); + out.add(it->toJson(TRI_UNKNOWN_MEM_ZONE, true)); } result.json = out.steal(); From 7113480e985296e2039280c9b3dc68ea2f3bf611 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 14:30:45 +0200 Subject: [PATCH 05/13] changed return value of applicationContext.collectionName Now, a valid collection name is always returned, even if the application name prefix contains invalid characters --- CHANGELOG | 10 ++++++++++ js/server/modules/org/arangodb/foxx/manager.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d8d88fc0fe..97dc2c64b8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,16 @@ v2.3.0 (XXXX-XX-XX) ------------------- +* changed return value of Foxx.applicationContext#collectionName: + + Previously, the function could return invalid collection names because + invalid characters were not replaced in the application name prefix, only + in the collection name passed. + + Now, the function replaces invalid characters also in the application name + prefix, which might to slightly different results for application names that + contained any characters outside the ranges [a-z], [A-Z] and [0-9]. + * prevent XSS in AQL editor and logs view * integrated tutorial into ArangoShell and web interface diff --git a/js/server/modules/org/arangodb/foxx/manager.js b/js/server/modules/org/arangodb/foxx/manager.js index 79281080ac..58b8bb4eb0 100644 --- a/js/server/modules/org/arangodb/foxx/manager.js +++ b/js/server/modules/org/arangodb/foxx/manager.js @@ -198,7 +198,7 @@ function extendContext (context, app, root) { } context.collectionName = function (name) { - var replaced = (cp + name.replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '')).substr(0, 64); + var replaced = ((cp + name).replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '')).substr(0, 64); if (replaced.length === 0) { throw new Error("Cannot derive collection name from '" + name + "'"); From 1907d8bb05e2b1fbe305862ddeb9396fc3f4f201 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 14:35:18 +0200 Subject: [PATCH 06/13] added optimizer rule test --- ...-rule-interchange-adjacent-enumerations.js | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js diff --git a/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js b/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js new file mode 100644 index 0000000000..6ccdefddbe --- /dev/null +++ b/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js @@ -0,0 +1,216 @@ +/*global _, require, exports, assertTrue, assertEqual, AQL_EXECUTE, AQL_EXPLAIN */ +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests for optimizer rules +/// +/// @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 Jan Steemann +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var errors = require("internal").errors; +var helper = require("org/arangodb/aql-helper"); +var getQueryResults = helper.getQueryResults2; +var assertQueryError = helper.assertQueryError2; +var isEqual = helper.isEqual; +var db = require("org/arangodb").db; +var _ = require("underscore"); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function optimizerRuleTestSuite () { + var ruleName = "interchange-adjacent-enumerations"; + + // various choices to control the optimizer: + var paramNone = { optimizer: { rules: [ "-all" ] } }; + var paramEnabled = { optimizer: { rules: [ "-all", "+" + ruleName ] } }; + var paramDisabled = { optimizer: { rules: [ "+all", "-" + ruleName ] } }; + + var collection = null; + var collectionName = "UnitTestsAhuacatlOptimizer"; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + db._drop(collectionName); + collection = db._create(collectionName); + + for (var i = 0; i < 10; ++i) { + collection.save({ value: i }); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + db._drop(collectionName); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test that rule has no effect when explicitly disabled +//////////////////////////////////////////////////////////////////////////////// + + testRuleDisabled : function () { + var queries = [ + "FOR i IN " + collectionName + " FOR j IN " + collectionName + " RETURN 1", + "FOR j IN " + collectionName + " FILTER j.i == 1 FOR i IN " + collectionName + " RETURN j" + ]; + + var opts = _.clone(paramNone); + opts.allPlans = true; + + queries.forEach(function(query) { + var result = AQL_EXPLAIN(query, { }, opts); + result.plans.forEach(function(plan) { + assertEqual([ ], plan.rules); + }); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test that rule has no effect +//////////////////////////////////////////////////////////////////////////////// + + testRuleNoEffect : function () { + var queries = [ + "FOR i IN 1..10 RETURN i", + "FOR i IN " + collectionName + " RETURN i", + "FOR i IN " + collectionName + " FILTER i == 1 FOR j IN " + collectionName + " RETURN i", + "FOR i IN " + collectionName + " LIMIT 1 FOR j IN " + collectionName + " RETURN i", + "FOR i IN " + collectionName + " RETURN (FOR j IN " + collectionName + " RETURN j)" + ]; + + var opts = _.clone(paramEnabled); + opts.allPlans = true; + + queries.forEach(function(query) { + var result = AQL_EXPLAIN(query, { }, opts); + result.plans.forEach(function(plan) { + assertTrue(plan.rules.indexOf(ruleName) === -1, query); + }); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test that rule has an effect +//////////////////////////////////////////////////////////////////////////////// + + testRuleHasEffect : function () { + var queries = [ + [ "FOR i IN " + collectionName + " FOR j IN " + collectionName + " RETURN i", 1 ], + [ "FOR i IN 1..10 FOR j IN " + collectionName + " FOR k IN " + collectionName + " RETURN i", 1 ], + [ "FOR i IN " + collectionName + " FOR j IN " + collectionName + " FOR k IN " + collectionName + " RETURN i", 5 ], + [ "FOR i IN " + collectionName + " FOR j IN " + collectionName + " FOR k IN " + collectionName + " FOR l IN " + collectionName + " RETURN i", 23 ], + [ "FOR x IN (FOR i IN " + collectionName + " FOR j IN " + collectionName + " RETURN i) RETURN x", 1 ], + [ "FOR x IN (FOR i IN " + collectionName + " FOR j IN " + collectionName + " FOR k IN " + collectionName + " RETURN i) RETURN x", 5 ], + [ "FOR x IN (FOR i IN " + collectionName + " FOR j IN " + collectionName + " FOR k IN " + collectionName + " RETURN i) FOR y IN (FOR i IN " + collectionName + " FOR j IN " + collectionName + " RETURN i) RETURN x", 11 ] + ]; + + var opts = _.clone(paramEnabled); + opts.allPlans = true; + + queries.forEach(function(query) { + var withRule = 0; + var withoutRule = 0; + + var result = AQL_EXPLAIN(query[0], { }, opts); + result.plans.forEach(function(plan) { + if (plan.rules.indexOf(ruleName) === -1) { + withoutRule++; + } + else { + withRule++; + } + }); + + // there should still be the original plan + assertEqual(1, withoutRule, query[0]); + + // put there should also be permuted plans + assertEqual(query[1], withRule, query[0]); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test results +//////////////////////////////////////////////////////////////////////////////// + + testResults : function () { + var queries = [ + [ "FOR i IN " + collectionName + " FOR j IN " + collectionName + " SORT i.value, j.value FILTER i.value == j.value RETURN i.value", [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ], + [ "FOR j IN " + collectionName + " FOR i IN " + collectionName + " SORT i.value, j.value FILTER i.value == j.value RETURN i.value", [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ], + [ "FOR x IN (FOR i IN " + collectionName + " FOR j IN " + collectionName + " RETURN { i: i.value, j: j.value }) FILTER x.i == x.j SORT x.i RETURN x.i", [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ] + ]; + + var opts = _.clone(paramEnabled); + opts.allPlans = true; + + queries.forEach(function(query) { + var planDisabled = AQL_EXPLAIN(query[0], { }, paramDisabled); + var plansEnabled = AQL_EXPLAIN(query[0], { }, opts); + var resultDisabled = AQL_EXECUTE(query[0], { }, paramDisabled).json; + + assertTrue(planDisabled.plan.rules.indexOf(ruleName) === -1, query[0]); + assertEqual(resultDisabled, query[1]); + + + assertTrue(plansEnabled.plans.length > 1); + + // iterate over all plans + var withRule = 0; + plansEnabled.plans.forEach(function(plan) { + var resultEnabled = AQL_EXECUTEJSON(plan).json; + assertTrue(isEqual(resultDisabled, resultEnabled), query[0]); + if (plan.rules.indexOf(ruleName) !== -1) { + withRule++; + } + assertEqual(resultEnabled, query[1]); + }); + + assertTrue(withRule > 0); + + }); + } + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(optimizerRuleTestSuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: From 8c9436a92b1b3dc353411cc1b28e6630f60d5082 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 15:01:50 +0200 Subject: [PATCH 07/13] attempt to shield test from other influences --- js/common/tests/shell-database.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/common/tests/shell-database.js b/js/common/tests/shell-database.js index 72caa2ccea..f114af90a6 100644 --- a/js/common/tests/shell-database.js +++ b/js/common/tests/shell-database.js @@ -420,7 +420,7 @@ function DatabaseSuite () { internal.db._useDatabase("UnitTestsDatabase0"); assertEqual("UnitTestsDatabase0", internal.db._name()); var c1 = internal.db._create("test1"); - assertEqual([ "test1" ], getCollections()); + assertNotEqual(-1, getCollections().indexOf("test1")); c1.save({ "_key": "foo" }); assertEqual(1, internal.db._collection("test1").count()); assertEqual(1, c1.count()); @@ -429,7 +429,7 @@ function DatabaseSuite () { internal.db._useDatabase("UNITTESTSDATABASE0"); assertEqual("UNITTESTSDATABASE0", internal.db._name()); var c2 = internal.db._create("test1"); - assertEqual([ "test1" ], getCollections()); + assertNotEqual(-1, getCollections().indexOf("test1")); c2.save({ "_key": "foo" }); c2.save({ "_key": "bar" }); c2.save({ "_key": "baz" }); From 9b62c7367b1b9cb5f548800c60248d362bb64d43 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Wed, 10 Sep 2014 15:50:22 +0200 Subject: [PATCH 08/13] Deprecated _directedRelation and _undirectedRelation in general-graphs. Created _relation in general-graphs creating a _directedRelation. Handling undirected relations is now given to the user / api-implementor as arangodb itself does not offer any direction constrains which caused some confusion --- DEPRECATED.md | 4 + .../Users/General-Graphs/Management.mdpp | 17 +- .../js/modules/org/arangodb/general-graph.js | 7 +- .../modules/org/arangodb/general-graph.js | 101 +++++++--- .../arangodb/graph-examples/example-graph.js | 10 +- js/common/tests/shell-general-graph.js | 182 ++++++++---------- js/common/tests/shell-graph-traversal.js | 4 +- js/server/tests/ahuacatl-general-graph.js | 95 ++++----- 8 files changed, 246 insertions(+), 174 deletions(-) diff --git a/DEPRECATED.md b/DEPRECATED.md index fdb6e03b95..38730dc526 100644 --- a/DEPRECATED.md +++ b/DEPRECATED.md @@ -49,6 +49,7 @@ migrations will also be detailed here. * Http: In `POST _api/traversal` the usage of the body parameter `edgeCollection` is now deprecated, it will raise a warning if you use it. To suppress the warning, please start `arangod` with the option `--server.default-api-compatibility 20200`. Please use `graphName` instead. * Replication: the methods `logger.start`, `logger.stop` and `logger.properties` are now deprecated. Using them will raise a warning. * Replication: the HTTP methods `PUT /_api/replication/logger-start`, `PUT /_api/replication/logger-stop`, `GET /_api/replication/logger-config` and `PUT /_api/replication/logger-config` are now deprecated. Using them will raise a warning. +* General-Graph: In the module `org/arangodb/general-graph` the functions `_undirectedRelation` and `_directedRelation` is no longer suggested, they will be deprecated int the next version. Both functions have been unified to `_relation`. ## 2.4 @@ -68,6 +69,8 @@ migrations will also be detailed here. * Http: In `POST _api/traversal` the usage of the body parameter `edgeCollection` is no longer available by default. If you still want to use it, please start `arangod` with the option `--server.default-api-compatibility 20200`. Please use `graphName` instead. * Replication: the methods `logger.start`, `logger.stop` and `logger.properties` are no longer available. * Replication: the HTTP methods `PUT /_api/replication/logger-start`, `PUT /_api/replication/logger-stop`, `GET /_api/replication/logger-config` and `PUT /_api/replication/logger-config` are no longer available. +To suppress the warning, please start `arangod` with the option `--server.default-api-compatibility 20200`. +* General-Graph: In the module `org/arangodb/general-graph` the functions `_undirectedRelation` and `_directedRelation` are deprecated and will throw an error if you use them. Both functions have been unified to `_relation`. ## 2.5 @@ -85,3 +88,4 @@ migrations will also be detailed here. * Traversal: The usage of the traversal datasource `collectionDatasourceFactory` has been removed entirely. Please use `generalGraphDatasourceFactory` instead. * Http: The api `_api/graph` has been removed entirely. Please use the general graph api `_api/gharial` instead. * Http: In `POST _api/traversal` the usage of the body parameter `edgeCollection` has been removed entirely. Please use `graphName` instead. +* General-Graph: In the module `org/arangodb/general-graph` the functions `_undirectedRelation` and `_directedRelation` are no longer available by default, if you still want to use them start `arangod` with the option `--server.default-api-compatibility 20300`. Both functions have been unified to `_relation`. diff --git a/Documentation/Books/Users/General-Graphs/Management.mdpp b/Documentation/Books/Users/General-Graphs/Management.mdpp index 440c19cc00..66afd25813 100644 --- a/Documentation/Books/Users/General-Graphs/Management.mdpp +++ b/Documentation/Books/Users/General-Graphs/Management.mdpp @@ -8,7 +8,7 @@ In order to create a non empty graph the functionality to create edge definition !SECTION Edge Definitions -An edge definition is a directed or undirected relation of a graph. Each graph can have arbitrary many relations defined within the edge definitions array. +An edge definition is always a directed relation of a graph. Each graph can have arbitrary many relations defined within the edge definitions array. !SUBSECTION Initialize the list @@ -18,12 +18,27 @@ An edge definition is a directed or undirected relation of a graph. Each graph c @startDocuBlock JSF_general_graph_extend_edge_definitions +!SUBSUBSECTION Relation + +@startDocuBlock JSF_general_graph_relation + + !SUBSUBSECTION Undirected Relation +**Warning: Deprecated** + +This function is deprecated and will be removed soon. +Please use [Relation](../General-Graphs/Management.html#relation) instead. + @startDocuBlock JSF_general_graph_undirectedRelation !SUBSUBSECTION Directed Relation +**Warning: Deprecated** + +This function is deprecated and will be removed soon. +Please use [Relation](../General-Graphs/Management.html#relation) instead. + @startDocuBlock JSF_general_graph_directedRelation !SECTION Create a graph 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 234ebf5197..3c72ac9a51 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 @@ -1412,7 +1412,7 @@ var _undirectedRelation = function (relationName, vertexCollections) { //////////////////////////////////////////////////////////////////////////////// -var _directedRelation = function ( +var _relation = function ( relationName, fromVertexCollections, toVertexCollections) { var err; if (arguments.length < 3) { @@ -4423,7 +4423,10 @@ Graph.prototype._PRINT = function(context) { // ----------------------------------------------------------------------------- exports._undirectedRelation = _undirectedRelation; -exports._directedRelation = _directedRelation; +exports._directedRelation = function () { + return _relation.apply(this, arguments); +} ; +exports._relation = _relation; exports._graph = _graph; exports._edgeDefinitions = _edgeDefinitions; exports._extendEdgeDefinitions = _extendEdgeDefinitions; diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index 01f4519e08..03ff7a781f 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -1302,6 +1302,9 @@ AQLGenerator.prototype.next = function() { // --SECTION-- public functions // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// Deprecated block +//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_general_graph_undirectedRelation /// @brief Define an undirected relation. @@ -1343,6 +1346,9 @@ AQLGenerator.prototype.next = function() { //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// Deprecated block +//////////////////////////////////////////////////////////////////////////////// var _undirectedRelation = function (relationName, vertexCollections) { var err; if (arguments.length < 2) { @@ -1372,8 +1378,9 @@ var _undirectedRelation = function (relationName, vertexCollections) { to: stringToArray(vertexCollections) }; }; - - +//////////////////////////////////////////////////////////////////////////////// +/// Deprecated block +//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_general_graph_directedRelation /// @brief Define a directed relation. @@ -1410,8 +1417,49 @@ var _undirectedRelation = function (relationName, vertexCollections) { /// //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_relation +/// @brief Define a directed relation. +/// +/// `graph_module._relation(relationName, fromVertexCollections, toVertexCollections)` +/// +/// 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*. +/// +/// @PARAMS +/// +/// @PARAM{relationName, string, required} +/// The name of the edge collection where the edges should be stored. +/// Will be created if it does not yet exist. +/// +/// @PARAM{fromVertexCollections, array, required} +/// One or a list of collection names. Source vertices for the edges +/// have to be stored in these collections. Collections will be created if they do not exist. +/// +/// @PARAM{toVertexCollections, array, required} +/// One or a list of collection names. Target vertices for the edges +/// have to be stored in these collections. Collections will be created if they do not exist. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphRelationDefinition} +/// var graph_module = require("org/arangodb/general-graph"); +/// graph_module._relation("has_bought", ["Customer", "Company"], ["Groceries", "Electronics"]); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphRelationDefinitionSingle} +/// var graph_module = require("org/arangodb/general-graph"); +/// graph_module._relation("has_bought", "Customer", "Product"); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// -var _directedRelation = function ( +var _relation = function ( relationName, fromVertexCollections, toVertexCollections) { var err; if (arguments.length < 3) { @@ -1499,8 +1547,8 @@ var _listObjects = function() { /// /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeDefinitions} /// var graph_module = require("org/arangodb/general-graph"); -/// directed_relation = graph_module._directedRelation("lives_in", "user", "city"); -/// undirected_relation = graph_module._undirectedRelation("knows", "user"); +/// directed_relation = graph_module._relation("lives_in", "user", "city"); +/// undirected_relation = graph_module._relation("knows", "user", "user"); /// edgedefinitions = graph_module._edgeDefinitions(directed_relation, undirected_relation); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -1541,8 +1589,8 @@ var _edgeDefinitions = function () { /// /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeDefinitionsExtend} /// var graph_module = require("org/arangodb/general-graph"); -/// directed_relation = graph_module._directedRelation("lives_in", "user", "city"); -/// undirected_relation = graph_module._undirectedRelation("knows", "user"); +/// directed_relation = graph_module._relation("lives_in", "user", "city"); +/// undirected_relation = graph_module._relation("knows", "user", "user"); /// edgedefinitions = graph_module._edgeDefinitions(directed_relation); /// edgedefinitions = graph_module._extendEdgeDefinitions(undirected_relation); /// @END_EXAMPLE_ARANGOSH_OUTPUT @@ -1604,7 +1652,7 @@ var sortEdgeDefinition = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphCreateGraphHowTo3} /// ~ var graph_module = require("org/arangodb/general-graph"); /// ~ var graph = graph_module._create("myGraph"); -/// var rel = graph_module._directedRelation("isCustomer", ["shop"], ["customer"]); +/// var rel = graph_module._relation("isCustomer", ["shop"], ["customer"]); /// graph._extendEdgeDefinitions(rel); /// graph; /// ~ graph_module._drop("myGraph", true); @@ -1653,7 +1701,7 @@ var sortEdgeDefinition = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphCreateGraph2} /// var graph_module = require("org/arangodb/general-graph"); /// | graph = graph_module._create("myGraph", -/// [graph_module._undirectedRelation("myRelation", ["male", "female"])], ["sessions"]); +/// [graph_module._relation("myRelation", ["male", "female"])], ["sessions"]); /// ~ graph_module._drop("myGraph", true); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -3899,8 +3947,8 @@ Graph.prototype._diameter = function(options) { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__extendEdgeDefinitions} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var ed2 = graph_module._directedRelation("myEC2", ["myVC1"], ["myVC3"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph_module._relation("myEC2", ["myVC1"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._extendEdgeDefinitions(ed2); /// ~ var blub = graph_module._drop("myGraph", true); @@ -4080,8 +4128,8 @@ var changeEdgeDefinitionsForGraph = function(graph, edgeDefinition, newCollectio /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__editEdgeDefinition} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var original = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var modified = graph_module._directedRelation("myEC1", ["myVC2"], ["myVC3"]); +/// var original = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var modified = graph_module._relation("myEC1", ["myVC2"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [original]); /// graph._editEdgeDefinitions(modified); /// ~ var blub = graph_module._drop("myGraph", true); @@ -4159,8 +4207,8 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__deleteEdgeDefinition} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var ed2 = graph_module._directedRelation("myEC2", ["myVC1"], ["myVC3"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph_module._relation("myEC2", ["myVC1"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [ed1, ed2]); /// graph._deleteEdgeDefinition("myEC1"); /// db._collection("myEC1"); @@ -4172,8 +4220,8 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__deleteEdgeDefinitionWithDrop} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var ed2 = graph_module._directedRelation("myEC2", ["myVC1"], ["myVC3"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph_module._relation("myEC2", ["myVC1"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [ed1, ed2]); /// graph._deleteEdgeDefinition("myEC1", true); /// db._collection("myEC1"); @@ -4257,7 +4305,7 @@ Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollection) /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__addVertexCollection} /// var graph_module = require("org/arangodb/general-graph"); /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._addVertexCollection("myVC3", true); /// ~ var blub = graph_module._drop("myGraph", true); @@ -4317,7 +4365,7 @@ Graph.prototype._addVertexCollection = function(vertexCollectionName, createColl /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__orphanCollections} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._addVertexCollection("myVC3", true); /// graph._orphanCollections(); @@ -4356,7 +4404,7 @@ Graph.prototype._orphanCollections = function() { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__removeVertexCollections} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._addVertexCollection("myVC3", true); /// graph._addVertexCollection("myVC4", true); @@ -4421,8 +4469,13 @@ Graph.prototype._PRINT = function(context) { // --SECTION-- MODULE EXPORTS // ----------------------------------------------------------------------------- +/// Deprecated function (announced 2.3) exports._undirectedRelation = _undirectedRelation; -exports._directedRelation = _directedRelation; +/// Deprecated function (announced 2.3) +exports._directedRelation = function () { + return _relation.apply(this, arguments); +} ; +exports._relation = _relation; exports._graph = _graph; exports._edgeDefinitions = _edgeDefinitions; exports._extendEdgeDefinitions = _extendEdgeDefinitions; @@ -4451,9 +4504,9 @@ exports._listObjects = _listObjects; /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph_create_graph_example1} /// var graph_module = require("org/arangodb/general-graph"); /// var edgeDefinitions = graph_module._edgeDefinitions(); -/// graph_module._extendEdgeDefinitions(edgeDefinitions, graph_module._undirectedRelation("friend_of", ["Customer"])); +/// graph_module._extendEdgeDefinitions(edgeDefinitions, graph_module._relation("friend_of", "Customer", "Customer")); /// | graph_module._extendEdgeDefinitions( -/// | edgeDefinitions, graph_module._directedRelation( +/// | edgeDefinitions, graph_module._relation( /// "has_bought", ["Customer", "Company"], ["Groceries", "Electronics"])); /// graph_module._create("myStore", edgeDefinitions); /// ~ graph_module._drop("myStore"); @@ -4468,7 +4521,7 @@ exports._listObjects = _listObjects; /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph_create_graph_example2} /// var graph_module = require("org/arangodb/general-graph"); /// | var edgeDefinitions = graph_module._edgeDefinitions( -/// | graph_module._undirectedRelation("friend_of", ["Customer"]), graph_module._directedRelation( +/// | graph_module._relation("friend_of", ["Customer"]), graph_module._relation( /// "has_bought", ["Customer", "Company"], ["Groceries", "Electronics"])); /// graph_module._create("myStore", edgeDefinitions); /// ~ graph_module._drop("myStore"); diff --git a/js/common/modules/org/arangodb/graph-examples/example-graph.js b/js/common/modules/org/arangodb/graph-examples/example-graph.js index 303eb60322..127e9bc313 100644 --- a/js/common/modules/org/arangodb/graph-examples/example-graph.js +++ b/js/common/modules/org/arangodb/graph-examples/example-graph.js @@ -33,7 +33,7 @@ var createTraversalExample = function() { var g = Graph._create("knows_graph", - [Graph._undirectedRelation("knows", "persons")] + [Graph._relation("knows", "persons", "persons")] ); var a = g.persons.save({name: "Alice", _key: "alice"})._id; var b = g.persons.save({name: "Bob", _key: "bob"})._id; @@ -50,7 +50,7 @@ var createSocialGraph = function() { var edgeDefinition = []; - edgeDefinition.push(Graph._undirectedRelation("relation", ["female", "male"])); + edgeDefinition.push(Graph._relation("relation", ["female", "male"], ["female", "male"])); var g = Graph._create("social", edgeDefinition); var a = g.female.save({name: "Alice", _key: "alice"}); var b = g.male.save({name: "Bob", _key: "bob"}); @@ -65,13 +65,13 @@ var createRoutePlannerGraph = function() { var edgeDefinition = []; - edgeDefinition.push(Graph._directedRelation( + edgeDefinition.push(Graph._relation( "germanHighway", ["germanCity"], ["germanCity"]) ); edgeDefinition.push( - Graph._directedRelation("frenchHighway", ["frenchCity"], ["frenchCity"]) + Graph._relation("frenchHighway", ["frenchCity"], ["frenchCity"]) ); - edgeDefinition.push(Graph._directedRelation( + edgeDefinition.push(Graph._relation( "internationalHighway", ["frenchCity", "germanCity"], ["frenchCity", "germanCity"]) ); var g = Graph._create("routeplanner", edgeDefinition); diff --git a/js/common/tests/shell-general-graph.js b/js/common/tests/shell-general-graph.js index d3e6a356b3..0af7bdffec 100644 --- a/js/common/tests/shell-general-graph.js +++ b/js/common/tests/shell-general-graph.js @@ -53,8 +53,8 @@ function GeneralGraphCreationSuite() { var vn4 = "UnitTestVerticies4"; var gn = "UnitTestGraph"; var edgeDef = graph._edgeDefinitions( - graph._undirectedRelation(rn, vn1), - graph._directedRelation(rn1, + graph._relation(rn, [vn1], [vn1]), + graph._relation(rn1, [vn1, vn2], [vn3, vn4] ) ); @@ -110,7 +110,7 @@ function GeneralGraphCreationSuite() { //////////////////////////////////////////////////////////////////////////////// test_undirectedRelation : function () { - var r = graph._undirectedRelation(rn, [vn1, vn2]); + var r = graph._relation(rn, [vn1, vn2], [vn1, vn2]); assertEqual(r, { collection: rn, @@ -121,7 +121,7 @@ function GeneralGraphCreationSuite() { }, test_undirectedRelationWithSingleCollection : function () { - var r = graph._undirectedRelation(rn, vn1); + var r = graph._relation(rn, vn1, vn1); assertEqual(r, { collection: rn, @@ -133,33 +133,21 @@ function GeneralGraphCreationSuite() { test_undirectedRelationWithMissingName : function () { try { - graph._undirectedRelation("", [vn1, vn2]); + graph._relation("", [vn1, vn2], [vn1, vn2]); fail(); } catch (err) { - assertEqual(err.errorMessage, "Invalid parameter type. arg1 must not be empty"); + assertEqual(err.errorMessage, "Invalid parameter type. arg1 must be non empty string"); } }, - test_undirectedRelationWithTooFewArgs : function () { + test_relationWithTooFewArgs : function () { try { - graph._undirectedRelation([vn1, vn2]); + graph._relation([vn1, vn2]); fail(); } catch (err) { - assertEqual(err.errorMessage, "Invalid number of arguments. Expected: 2"); - } - }, - - test_undirectedRelationWithInvalidSecondArg : function () { - try { - var param = {}; - param[vn1] = vn2; - graph._undirectedRelation(rn, param); - fail(); - } - catch (err) { - assertEqual(err.errorMessage, "Invalid parameter type. arg2 must not be empty"); + assertEqual(err.errorMessage, "Invalid number of arguments. Expected: 3"); } }, @@ -167,7 +155,7 @@ function GeneralGraphCreationSuite() { var g = graph._create( gn, graph._edgeDefinitions( - graph._directedRelation(rn1, [vn2, vn1], [vn4, vn3]) + graph._relation(rn1, [vn2, vn1], [vn4, vn3]) ) ); @@ -178,7 +166,7 @@ function GeneralGraphCreationSuite() { }, test_directedRelation : function () { - var r = graph._directedRelation(rn, + var r = graph._relation(rn, [vn1, vn2], [vn3, vn4]); assertEqual(r, { @@ -191,7 +179,7 @@ function GeneralGraphCreationSuite() { test_directedRelationWithMissingName : function () { try { - graph._directedRelation("", + graph._relation("", [vn1, vn2], [vn3, vn4]); fail(); } @@ -202,7 +190,7 @@ function GeneralGraphCreationSuite() { test_directedRelationWithTooFewArgs : function () { try { - graph._directedRelation([vn1, vn2], [vn3, vn4]); + graph._relation([vn1, vn2], [vn3, vn4]); fail(); } catch (err) { @@ -210,11 +198,11 @@ function GeneralGraphCreationSuite() { } }, - test_directedRelationWithInvalidSecondArg : function () { + test_relationWithInvalidSecondArg : function () { try { var param = {}; param[vn1] = vn2; - graph._directedRelation(rn, param, vn3); + graph._relation(rn, param, vn3); fail(); } catch (err) { @@ -224,11 +212,11 @@ function GeneralGraphCreationSuite() { }, - test_directedRelationWithInvalidThirdArg : function () { + test_relationWithInvalidThirdArg : function () { try { var param = {}; param[vn1] = vn2; - graph._directedRelation(rn, vn3, param); + graph._relation(rn, vn3, param); fail(); } catch (err) { @@ -243,8 +231,8 @@ function GeneralGraphCreationSuite() { //with args assertEqual(graph._edgeDefinitions( - graph._undirectedRelation(rn, vn1), - graph._directedRelation(rn1, + graph._relation(rn, vn1, vn1), + graph._relation(rn1, [vn1, vn2], [vn3, vn4]) ), [ { @@ -268,12 +256,12 @@ function GeneralGraphCreationSuite() { //with args var ed =graph._edgeDefinitions( - graph._undirectedRelation("relationName", "vertexC1"), - graph._directedRelation("relationName", + graph._relation("relationName", "vertexC1", "vertexC1"), + graph._relation("relationName", ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"]) ); graph._extendEdgeDefinitions(ed, - graph._undirectedRelation("relationName", "vertexC1") + graph._relation("relationName", "vertexC1", "vertexC1") ); assertEqual(ed, [ { @@ -303,8 +291,8 @@ function GeneralGraphCreationSuite() { var a = graph._create( gn, graph._edgeDefinitions( - graph._undirectedRelation(rn, vn1), - graph._directedRelation(rn1, [vn1, vn2], [vn3, vn4]) + graph._relation(rn, vn1, vn1), + graph._relation(rn1, [vn1, vn2], [vn3, vn4]) ) ); assertTrue(a.__vertexCollections.hasOwnProperty(vn1)); @@ -356,8 +344,8 @@ function GeneralGraphCreationSuite() { graph._create( "", graph._edgeDefinitions( - graph._undirectedRelation("relationName", "vertexC1"), - graph._directedRelation("relationName2", + graph._relation("relationName", "vertexC1", "vertexC1"), + graph._relation("relationName2", ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"] ) ) @@ -433,7 +421,7 @@ function GeneralGraphCreationSuite() { if(graph._exists(gn)) { graph._drop(gn, true); } - var edgeDef2 = [graph._directedRelation(rn, vn1, vn2)]; + var edgeDef2 = [graph._relation(rn, vn1, vn2)]; var g = graph._create(gn, edgeDef2); var v1 = g[vn1].save({_key: "1"})._id; var v2 = g[vn2].save({_key: "2"})._id; @@ -471,7 +459,7 @@ function GeneralGraphCreationSuite() { test_deleteEdgeDefinitionFromExistingGraph1: function() { - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), g1 = graph._create(gN1, [dr1]); try { @@ -487,9 +475,9 @@ function GeneralGraphCreationSuite() { test_deleteEdgeDefinitionFromExistingGraph2: function() { - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec2, [vc3], [vc4, vc5]), - dr3 = graph._directedRelation(ec3, [vc4], [vc5]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec2, [vc3], [vc4, vc5]), + dr3 = graph._relation(ec3, [vc4], [vc5]), g1 = graph._create(gN1, [dr1, dr2, dr3]); assertEqual([dr1, dr2, dr3], g1.__edgeDefinitions); @@ -506,9 +494,9 @@ function GeneralGraphCreationSuite() { test_deleteEdgeDefinitionFromExistingGraphAndDropIt: function() { - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec2, [vc3], [vc4, vc5]), - dr3 = graph._directedRelation(ec3, [vc4], [vc5]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec2, [vc3], [vc4, vc5]), + dr3 = graph._relation(ec3, [vc4], [vc5]), g1 = graph._create(gN1, [dr1, dr2, dr3]); assertEqual([dr1, dr2, dr3], g1.__edgeDefinitions); @@ -530,8 +518,8 @@ function GeneralGraphCreationSuite() { } catch(ignore) { } - var dr1 = graph._directedRelation(ec1, [vc1], [vc2]), - dr2 = graph._directedRelation(ec1, [vc2], [vc3]), + var dr1 = graph._relation(ec1, [vc1], [vc2]), + dr2 = graph._relation(ec1, [vc2], [vc3]), g1 = graph._create(gN1, [dr1]); try { @@ -552,9 +540,9 @@ function GeneralGraphCreationSuite() { test_extendEdgeDefinitionFromExistingGraph2: function() { - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec2, [vc3], [vc4, vc5]), - dr2a = graph._directedRelation(ec2, [vc3], [vc4]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec2, [vc3], [vc4, vc5]), + dr2a = graph._relation(ec2, [vc3], [vc4]), g1 = graph._create(gN1, [dr1]), g2 = graph._create(gN2, [dr2]); @@ -588,9 +576,9 @@ function GeneralGraphCreationSuite() { } catch(ignore) { } - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec2, [vc3], [vc4, vc5]), - dr3 = graph._directedRelation(ec3, [vc3], [vc4]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec2, [vc3], [vc4, vc5]), + dr3 = graph._relation(ec3, [vc3], [vc4]), g1 = graph._create(gN1, [dr1]), g2 = graph._create(gN2, [dr2]); @@ -615,8 +603,8 @@ function GeneralGraphCreationSuite() { } catch(ignore) { } - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec2, [vc4, vc3, vc1, vc2], [vc4, vc3, vc1, vc2]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec2, [vc4, vc3, vc1, vc2], [vc4, vc3, vc1, vc2]), g1 = graph._create(gN1, [dr1]); g1._extendEdgeDefinitions(dr2); @@ -629,8 +617,8 @@ function GeneralGraphCreationSuite() { }, test_editEdgeDefinitionFromExistingGraph1: function() { - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec2, [vc3], [vc4, vc5]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec2, [vc3], [vc4, vc5]), g1 = graph._create(gN1, [dr1]); try { @@ -646,9 +634,9 @@ function GeneralGraphCreationSuite() { test_editEdgeDefinitionFromExistingGraph2: function() { - var dr1 = graph._directedRelation(ec1, [vc1, vc2], [vc3, vc4]), - dr2 = graph._directedRelation(ec2, [vc1], [vc4]), - dr3 = graph._directedRelation(ec1, [vc5], [vc5]), + var dr1 = graph._relation(ec1, [vc1, vc2], [vc3, vc4]), + dr2 = graph._relation(ec2, [vc1], [vc4]), + dr3 = graph._relation(ec1, [vc5], [vc5]), g1 = graph._create(gN1, [dr1, dr2]), g2 = graph._create(gN2, [dr1]); @@ -663,9 +651,9 @@ function GeneralGraphCreationSuite() { test_editEdgeDefinitionFromExistingGraph3: function() { - var dr1 = graph._directedRelation(ec1, [vc1], [vc1, vc2]), - dr2 = graph._directedRelation(ec1, [vc3], [vc4, vc5]), - dr3 = graph._directedRelation(ec2, [vc2], [vc2, vc3]), + var dr1 = graph._relation(ec1, [vc1], [vc1, vc2]), + dr2 = graph._relation(ec1, [vc3], [vc4, vc5]), + dr3 = graph._relation(ec2, [vc2], [vc2, vc3]), g1 = graph._create(gN1, [dr1, dr3]), g2 = graph._create(gN2, [dr1]); @@ -719,10 +707,10 @@ function GeneralGraphAQLQueriesSuite() { var createInclExcl = function() { dropInclExcl(); - var inc = graph._directedRelation( + var inc = graph._relation( included, [v1], [v1, v2] ); - var exc = graph._directedRelation( + var exc = graph._relation( excluded, [v1], [v3] ); var g = graph._create(graphName, [inc, exc]); @@ -998,8 +986,8 @@ function ChainedFluentAQLResultsSuite() { var g; var edgeDef = []; - edgeDef.push(graph._undirectedRelation(isFriend, user)); - edgeDef.push(graph._directedRelation(hasBought, user, product)); + edgeDef.push(graph._relation(isFriend, user, user)); + edgeDef.push(graph._relation(hasBought, user, product)); var findBoughts = function(result, list) { @@ -1475,7 +1463,7 @@ function ChainedFluentAQLResultsSuite() { } catch (ignore) { } var g2 = graph._create(emptyGN, [ - graph._undirectedRelation(emptyEdges, emptyVertices) + graph._relation(emptyEdges, emptyVertices, emptyVertices) ]); g2[emptyVertices].save({_key: "highlander"}); var res = g2._vertices(emptyVertices + "/highlander").edges().restrict(emptyEdges).toArray(); @@ -1894,8 +1882,8 @@ function EdgesAndVerticesSuite() { g = graph._create( unitTestGraphName, graph._edgeDefinitions( - graph._undirectedRelation(ec1, vc1), - graph._directedRelation(ec2, + graph._relation(ec1, vc1, vc1), + graph._relation(ec2, [vc1, vc2], [vc3, vc4] ) ) @@ -1913,7 +1901,7 @@ function EdgesAndVerticesSuite() { graph._create( myGraphName, graph._edgeDefinitions( - graph._undirectedRelation(myEdgeColName, myVertexColName) + graph._relation(myEdgeColName, myVertexColName, myVertexColName) ) ); graph._drop(myGraphName, true); @@ -1929,7 +1917,7 @@ function EdgesAndVerticesSuite() { graph._create( myGraphName, graph._edgeDefinitions( - graph._undirectedRelation(myEdgeColName, myVertexColName) + graph._relation(myEdgeColName, myVertexColName, myVertexColName) ) ); graph._drop(myGraphName); @@ -1943,7 +1931,7 @@ function EdgesAndVerticesSuite() { graph._create( myGraphName, graph._edgeDefinitions( - graph._undirectedRelation(ec1, vc1) + graph._relation(ec1, vc1, vc1) ) ); assertTrue(graph._exists(myGraphName)); @@ -1959,7 +1947,7 @@ function EdgesAndVerticesSuite() { graph._create( myGraphName, graph._edgeDefinitions( - graph._undirectedRelation(ec1, vc2) + graph._relation(ec1, vc2, vc2) ) ); } catch (e) { @@ -1984,8 +1972,8 @@ function EdgesAndVerticesSuite() { graph._create( myGraphName, graph._edgeDefinitions( - graph._undirectedRelation(myED, myVD1), - graph._undirectedRelation(myED, myVD2) + graph._relation(myED, myVD1, myVD1), + graph._relation(myED, myVD2, myVD2) ) ); } catch (e) { @@ -2185,7 +2173,7 @@ function EdgesAndVerticesSuite() { var g2 = graph._create( myGraphName, graph._edgeDefinitions( - graph._directedRelation(myEC02, + graph._relation(myEC02, [ec1], [myVC01] ) ) @@ -2236,18 +2224,18 @@ function EdgesAndVerticesSuite() { } catch (ignore) { } - db._createEdgeCollection(eC1) - db._createEdgeCollection(eC2) - db._createEdgeCollection(eC3) - db._createEdgeCollection(eC4) - db._create(vC1) - db._create(vC2) - db._create(vC3) - db._create(vC4) + db._createEdgeCollection(eC1); + db._createEdgeCollection(eC2); + db._createEdgeCollection(eC3); + db._createEdgeCollection(eC4); + db._create(vC1); + db._create(vC2); + db._create(vC3); + db._create(vC4); var vertex1 = db[vC1].save({}); - var vertexId1 = vertex1._id; + vertexId1 = vertex1._id; var vertex2 = db[vC1].save({}); - var vertexId2 = vertex2._id; + vertexId2 = vertex2._id; var vertex3 = db[vC1].save({}); var vertexId3 = vertex3._id; var vertex4 = db[vC1].save({}); @@ -2260,25 +2248,25 @@ function EdgesAndVerticesSuite() { var g1 = graph._create( gN1, graph._edgeDefinitions( - graph._directedRelation(eC1, [eC4], [vC1]) + graph._relation(eC1, [eC4], [vC1]) ) ); var g2 = graph._create( gN2, graph._edgeDefinitions( - graph._directedRelation(eC2, [eC1], [vC2]) + graph._relation(eC2, [eC1], [vC2]) ) ); var g3 = graph._create( gN3, graph._edgeDefinitions( - graph._directedRelation(eC3, [eC2], [vC3]) + graph._relation(eC3, [eC2], [vC3]) ) ); var g4 = graph._create( gN4, graph._edgeDefinitions( - graph._directedRelation(eC4, [eC3], [vC4]) + graph._relation(eC4, [eC3], [vC4]) ) ); @@ -2390,7 +2378,7 @@ function GeneralGraphCommonNeighborsSuite() { testGraph = graph._create( "bla3", graph._edgeDefinitions( - graph._directedRelation(eColName, + graph._relation(eColName, [v1ColName, v2ColName], [v1ColName, v2ColName] ) @@ -2576,7 +2564,7 @@ function OrphanCollectionSuite() { g1 = graph._create( gN1, graph._edgeDefinitions( - graph._directedRelation( + graph._relation( eC1, [vC1], [vC1, vC2] ) ) @@ -2584,7 +2572,7 @@ function OrphanCollectionSuite() { g2 = graph._create( gN2, graph._edgeDefinitions( - graph._directedRelation( + graph._relation( eC2, [vC3], [vC1] ) ) @@ -2791,8 +2779,8 @@ function MeasurementsSuite() { g = graph._create( unitTestGraphName, graph._edgeDefinitions( - graph._undirectedRelation(ec1, vc1), - graph._directedRelation(ec2, + graph._relation(ec1, vc1, vc1), + graph._relation(ec2, [vc1, vc2], [vc3, vc4] ) ) diff --git a/js/common/tests/shell-graph-traversal.js b/js/common/tests/shell-graph-traversal.js index 45492c1e7b..f20d393cad 100644 --- a/js/common/tests/shell-graph-traversal.js +++ b/js/common/tests/shell-graph-traversal.js @@ -1923,8 +1923,8 @@ function GeneralGraphTraversalSuite () { cleanUp(); var edgeDef = []; - edgeDef.push(generalGraph._directedRelation(enDir, vnA, vnBDH)); - edgeDef.push(generalGraph._undirectedRelation(enUndir, [vnBDH, vnCEFGI])); + edgeDef.push(generalGraph._relation(enDir, vnA, vnBDH)); + edgeDef.push(generalGraph._relation(enUndir, [vnBDH, vnCEFGI], [vnBDH, vnCEFGI])); g = generalGraph._create(gn, edgeDef); saveVertex(vnA, "A"); diff --git a/js/server/tests/ahuacatl-general-graph.js b/js/server/tests/ahuacatl-general-graph.js index bc63fa5c44..3ed8efff8e 100644 --- a/js/server/tests/ahuacatl-general-graph.js +++ b/js/server/tests/ahuacatl-general-graph.js @@ -40,6 +40,15 @@ var assertQueryError = helper.assertQueryError; //////////////////////////////////////////////////////////////////////////////// function ahuacatlQueryGeneralEdgesTestSuite() { + + var v1 = "UnitTestsAhuacatlVertex1"; + var v2 = "UnitTestsAhuacatlVertex2"; + var v3 = "UnitTestsAhuacatlVertex3"; + var v4 = "UnitTestsAhuacatlVertex4"; + var e1 = "UnitTestsAhuacatlEdge1"; + var e2 = "UnitTestsAhuacatlEdge2"; + var or = "UnitTestsAhuacatlOrphan"; + return { //////////////////////////////////////////////////////////////////////////////// @@ -47,21 +56,21 @@ function ahuacatlQueryGeneralEdgesTestSuite() { //////////////////////////////////////////////////////////////////////////////// setUp: function () { - db._drop("UnitTestsAhuacatlVertex1"); - db._drop("UnitTestsAhuacatlVertex2"); - db._drop("UnitTestsAhuacatlVertex3"); - db._drop("UnitTestsAhuacatlVertex4"); - db._drop("UnitTestsAhuacatlEdge1"); - db._drop("UnitTestsAhuacatlEdge2"); - db._drop("UnitTestsAhuacatlOrphan"); + db._drop(v1); + db._drop(v2); + db._drop(v3); + db._drop(v4); + db._drop(e1); + db._drop(e2); + db._drop(or); - var vertex1 = db._create("UnitTestsAhuacatlVertex1"); - var vertex2 = db._create("UnitTestsAhuacatlVertex2"); - var vertex3 = db._create("UnitTestsAhuacatlVertex3"); - var vertex4 = db._create("UnitTestsAhuacatlVertex4"); - var edge1 = db._createEdgeCollection("UnitTestsAhuacatlEdge1"); - var edge2 = db._createEdgeCollection("UnitTestsAhuacatlEdge2"); - var oprhan = db._create("UnitTestsAhuacatlOrphan"); + var vertex1 = db._create(v1); + var vertex2 = db._create(v2); + var vertex3 = db._create(v3); + var vertex4 = db._create(v4); + var edge1 = db._createEdgeCollection(e1); + var edge2 = db._createEdgeCollection(e2); + var oprhan = db._create(or); vertex1.save({ _key: "v1", hugo: true}); vertex1.save({ _key: "v2", hugo: true}); @@ -77,14 +86,14 @@ function ahuacatlQueryGeneralEdgesTestSuite() { collection.save(from, to, { what: from.split("/")[1] + "->" + to.split("/")[1] }); } - makeEdge("UnitTestsAhuacatlVertex1/v1", "UnitTestsAhuacatlVertex1/v2", edge1); - makeEdge("UnitTestsAhuacatlVertex1/v2", "UnitTestsAhuacatlVertex1/v1", edge1); - makeEdge("UnitTestsAhuacatlVertex1/v1", "UnitTestsAhuacatlVertex3/v5", edge2); - makeEdge("UnitTestsAhuacatlVertex1/v2", "UnitTestsAhuacatlVertex3/v5", edge2); - makeEdge("UnitTestsAhuacatlVertex2/v3", "UnitTestsAhuacatlVertex3/v6", edge2); - makeEdge("UnitTestsAhuacatlVertex2/v4", "UnitTestsAhuacatlVertex4/v7", edge2); - makeEdge("UnitTestsAhuacatlVertex2/v3", "UnitTestsAhuacatlVertex3/v5", edge2); - makeEdge("UnitTestsAhuacatlVertex2/v3", "UnitTestsAhuacatlVertex4/v8", edge2); + makeEdge(v1 + "/v1", v1 + "/v2", edge1); + makeEdge(v1 + "/v2", v1 + "/v1", edge1); + makeEdge(v1 + "/v1", v3 + "/v5", edge2); + makeEdge(v1 + "/v2", v3 + "/v5", edge2); + makeEdge(v2 + "/v3", v3 + "/v6", edge2); + makeEdge(v2 + "/v4", v4 + "/v7", edge2); + makeEdge(v2 + "/v3", v3 + "/v5", edge2); + makeEdge(v2 + "/v3", v4 + "/v8", edge2); try { db._collection("_graphs").remove("_graphs/bla3"); @@ -93,13 +102,13 @@ function ahuacatlQueryGeneralEdgesTestSuite() { graph._create( "bla3", graph._edgeDefinitions( - graph._undirectedRelation("UnitTestsAhuacatlEdge1", "UnitTestsAhuacatlVertex1"), - graph._directedRelation("UnitTestsAhuacatlEdge2", - ["UnitTestsAhuacatlVertex1", "UnitTestsAhuacatlVertex2"], - ["UnitTestsAhuacatlVertex3", "UnitTestsAhuacatlVertex4"] + graph._relation(e1, v1, v1), + graph._relation(e2, + [v1, v2], + [v3, v4] ) ), - ["UnitTestsAhuacatlOrphan"] + [or] ); }, @@ -108,13 +117,13 @@ function ahuacatlQueryGeneralEdgesTestSuite() { //////////////////////////////////////////////////////////////////////////////// tearDown: function () { - db._drop("UnitTestsAhuacatlVertex1"); - db._drop("UnitTestsAhuacatlVertex2"); - db._drop("UnitTestsAhuacatlVertex3"); - db._drop("UnitTestsAhuacatlVertex4"); - db._drop("UnitTestsAhuacatlEdge1"); - db._drop("UnitTestsAhuacatlEdge2"); - db._drop("UnitTestsAhuacatlOrphan"); + db._drop(v1); + db._drop(v2); + db._drop(v3); + db._drop(v4); + db._drop(e1); + db._drop(e2); + db._drop(or); db._collection("_graphs").remove("_graphs/bla3"); }, @@ -125,8 +134,8 @@ function ahuacatlQueryGeneralEdgesTestSuite() { testEdgesAny: function () { var actual; - actual = getRawQueryResults("FOR e IN GRAPH_VERTICES('bla3', 'UnitTestsAhuacatlVertex1/v1', {direction : 'any'}) RETURN e"); - assertEqual(actual[0]._id, 'UnitTestsAhuacatlVertex1/v1'); + actual = getRawQueryResults("FOR e IN GRAPH_VERTICES('bla3', '" + v1 + "/v1', {direction : 'any'}) RETURN e"); + assertEqual(actual[0]._id, v1 + '/v1'); actual = getRawQueryResults("FOR e IN GRAPH_VERTICES('bla3', {}, {direction : 'any', vertexCollectionRestriction : 'UnitTestsAhuacatlOrphan'}) RETURN e"); assertEqual(actual[0]._id, 'UnitTestsAhuacatlOrphan/orphan'); @@ -309,7 +318,7 @@ function ahuacatlQueryGeneralCommonTestSuite() { graph._create( "bla3", graph._edgeDefinitions( - graph._directedRelation("UnitTestsAhuacatlEdge1", + graph._relation("UnitTestsAhuacatlEdge1", ["UnitTestsAhuacatlVertex1", "UnitTestsAhuacatlVertex2"], ["UnitTestsAhuacatlVertex1", "UnitTestsAhuacatlVertex2"] ) @@ -496,8 +505,8 @@ function ahuacatlQueryGeneralPathsTestSuite() { var g = graph._create( "bla3", graph._edgeDefinitions( - graph._undirectedRelation("UnitTestsAhuacatlEdge1", "UnitTestsAhuacatlVertex1"), - graph._directedRelation("UnitTestsAhuacatlEdge2", + graph._relation("UnitTestsAhuacatlEdge1", "UnitTestsAhuacatlVertex1", "UnitTestsAhuacatlVertex1"), + graph._relation("UnitTestsAhuacatlEdge2", ["UnitTestsAhuacatlVertex1", "UnitTestsAhuacatlVertex2"], ["UnitTestsAhuacatlVertex3", "UnitTestsAhuacatlVertex4"] ) @@ -725,8 +734,8 @@ function ahuacatlQueryGeneralTraversalTestSuite() { var g = graph._create( "werKenntWen", graph._edgeDefinitions( - graph._undirectedRelation(KenntAnderenBerliner, "UnitTests_Berliner"), - graph._directedRelation(KenntAnderen, + graph._relation(KenntAnderenBerliner, "UnitTests_Berliner", "UnitTests_Berliner"), + graph._relation(KenntAnderen, ["UnitTests_Hamburger", "UnitTests_Frankfurter", "UnitTests_Berliner", "UnitTests_Leipziger"], ["UnitTests_Hamburger", "UnitTests_Frankfurter", "UnitTests_Berliner", "UnitTests_Leipziger"] ) @@ -1690,8 +1699,8 @@ function ahuacatlQueryGeneralCyclesSuite() { var g = graph._create( "werKenntWen", graph._edgeDefinitions( - graph._undirectedRelation(KenntAnderenBerliner, "UnitTests_Berliner"), - graph._directedRelation(KenntAnderen, + graph._relation(KenntAnderenBerliner, "UnitTests_Berliner", "UnitTests_Berliner"), + graph._relation(KenntAnderen, ["UnitTests_Hamburger", "UnitTests_Frankfurter", "UnitTests_Berliner", "UnitTests_Leipziger"], ["UnitTests_Hamburger", "UnitTests_Frankfurter", "UnitTests_Berliner", "UnitTests_Leipziger"] ) From a6d57d2c8ac83c87b5b174383434733df80b0e0b Mon Sep 17 00:00:00 2001 From: Max Neunhoeffer Date: Wed, 27 Aug 2014 10:21:24 +0200 Subject: [PATCH 09/13] Fix DBserver bug with CollectionNameResolver and local collections. --- arangod/Cluster/ClusterInfo.cpp | 6 ++++-- arangod/Cluster/ClusterInfo.h | 3 ++- arangod/Utils/CollectionNameResolver.h | 24 ++++++++++++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/arangod/Cluster/ClusterInfo.cpp b/arangod/Cluster/ClusterInfo.cpp index 5698e5cf83..4cd6a943e5 100644 --- a/arangod/Cluster/ClusterInfo.cpp +++ b/arangod/Cluster/ClusterInfo.cpp @@ -650,7 +650,9 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) { _shards.insert( make_pair(collection,shared_ptr >(shards))); - // insert the collection into the existing map + // insert the collection into the existing map, insert it under its + // ID as well as under its name, so that a lookup can be done with + // either of the two. (*it2).second.insert(std::make_pair(collection, collectionData)); (*it2).second.insert(std::make_pair(collectionData->name(), @@ -687,7 +689,7 @@ shared_ptr ClusterInfo::getCollection AllCollections::const_iterator it = _collections.find(databaseID); if (it != _collections.end()) { - // look up collection by id + // look up collection by id (or by name) DatabaseCollections::const_iterator it2 = (*it).second.find(collectionID); if (it2 != (*it).second.end()) { diff --git a/arangod/Cluster/ClusterInfo.h b/arangod/Cluster/ClusterInfo.h index 86bd07b8ae..c52af68b4b 100644 --- a/arangod/Cluster/ClusterInfo.h +++ b/arangod/Cluster/ClusterInfo.h @@ -810,7 +810,8 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// /// @brief ask about a collection -/// If it is not found in the cache, the cache is reloaded once. +/// If it is not found in the cache, the cache is reloaded once. The second +/// argument can be a collection ID or a collection name (both cluster-wide). //////////////////////////////////////////////////////////////////////////////// shared_ptr getCollection (DatabaseID const&, diff --git a/arangod/Utils/CollectionNameResolver.h b/arangod/Utils/CollectionNameResolver.h index 68657d3dad..daf52b3a9d 100644 --- a/arangod/Utils/CollectionNameResolver.h +++ b/arangod/Utils/CollectionNameResolver.h @@ -169,20 +169,28 @@ namespace triagens { (&_vocbase->_collectionsById, &cid)); if (nullptr != found) { - name = triagens::basics::StringUtils::itoa(found->_planId); + if (found->_planId == 0) { + // DBserver local case + char* n = TRI_GetCollectionNameByIdVocBase(_vocbase, cid); + if (0 != n) { + name = n; + TRI_Free(TRI_UNKNOWN_MEM_ZONE, n); + } + } + else { + // DBserver case of a shard: + name = triagens::basics::StringUtils::itoa(found->_planId); + shared_ptr ci + = ClusterInfo::instance()->getCollection(found->_dbName,name); + name = ci->name(); // can be empty, if collection unknown + } } TRI_READ_UNLOCK_COLLECTIONS_VOCBASE(_vocbase); - - if (! name.empty()) { - shared_ptr ci - = ClusterInfo::instance()->getCollection(found->_dbName, name); - name = ci->name(); - } } else { // exactly as in the non-cluster case - char *n = TRI_GetCollectionNameByIdVocBase(_vocbase, cid); + char* n = TRI_GetCollectionNameByIdVocBase(_vocbase, cid); if (0 != n) { name = n; TRI_Free(TRI_UNKNOWN_MEM_ZONE, n); From 3487d7c9bae43337874e93b00d46bb1f2b7504a9 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 10 Sep 2014 15:56:02 +0200 Subject: [PATCH 10/13] added derived file --- .../js/modules/org/arangodb/general-graph.js | 94 ++++++++++++++----- 1 file changed, 72 insertions(+), 22 deletions(-) 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 3c72ac9a51..f58a2bc2e9 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 @@ -1303,6 +1303,9 @@ AQLGenerator.prototype.next = function() { // --SECTION-- public functions // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// Deprecated block +//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_general_graph_undirectedRelation /// @brief Define an undirected relation. @@ -1344,6 +1347,9 @@ AQLGenerator.prototype.next = function() { //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// Deprecated block +//////////////////////////////////////////////////////////////////////////////// var _undirectedRelation = function (relationName, vertexCollections) { var err; if (arguments.length < 2) { @@ -1373,8 +1379,9 @@ var _undirectedRelation = function (relationName, vertexCollections) { to: stringToArray(vertexCollections) }; }; - - +//////////////////////////////////////////////////////////////////////////////// +/// Deprecated block +//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_general_graph_directedRelation /// @brief Define a directed relation. @@ -1411,6 +1418,47 @@ var _undirectedRelation = function (relationName, vertexCollections) { /// //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_relation +/// @brief Define a directed relation. +/// +/// `graph_module._relation(relationName, fromVertexCollections, toVertexCollections)` +/// +/// 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*. +/// +/// @PARAMS +/// +/// @PARAM{relationName, string, required} +/// The name of the edge collection where the edges should be stored. +/// Will be created if it does not yet exist. +/// +/// @PARAM{fromVertexCollections, array, required} +/// One or a list of collection names. Source vertices for the edges +/// have to be stored in these collections. Collections will be created if they do not exist. +/// +/// @PARAM{toVertexCollections, array, required} +/// One or a list of collection names. Target vertices for the edges +/// have to be stored in these collections. Collections will be created if they do not exist. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphRelationDefinition} +/// var graph_module = require("org/arangodb/general-graph"); +/// graph_module._relation("has_bought", ["Customer", "Company"], ["Groceries", "Electronics"]); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphRelationDefinitionSingle} +/// var graph_module = require("org/arangodb/general-graph"); +/// graph_module._relation("has_bought", "Customer", "Product"); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// @endDocuBlock +/// +//////////////////////////////////////////////////////////////////////////////// var _relation = function ( relationName, fromVertexCollections, toVertexCollections) { @@ -1500,8 +1548,8 @@ var _listObjects = function() { /// /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeDefinitions} /// var graph_module = require("org/arangodb/general-graph"); -/// directed_relation = graph_module._directedRelation("lives_in", "user", "city"); -/// undirected_relation = graph_module._undirectedRelation("knows", "user"); +/// directed_relation = graph_module._relation("lives_in", "user", "city"); +/// undirected_relation = graph_module._relation("knows", "user", "user"); /// edgedefinitions = graph_module._edgeDefinitions(directed_relation, undirected_relation); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -1542,8 +1590,8 @@ var _edgeDefinitions = function () { /// /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphEdgeDefinitionsExtend} /// var graph_module = require("org/arangodb/general-graph"); -/// directed_relation = graph_module._directedRelation("lives_in", "user", "city"); -/// undirected_relation = graph_module._undirectedRelation("knows", "user"); +/// directed_relation = graph_module._relation("lives_in", "user", "city"); +/// undirected_relation = graph_module._relation("knows", "user", "user"); /// edgedefinitions = graph_module._edgeDefinitions(directed_relation); /// edgedefinitions = graph_module._extendEdgeDefinitions(undirected_relation); /// @END_EXAMPLE_ARANGOSH_OUTPUT @@ -1605,7 +1653,7 @@ var sortEdgeDefinition = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphCreateGraphHowTo3} /// ~ var graph_module = require("org/arangodb/general-graph"); /// ~ var graph = graph_module._create("myGraph"); -/// var rel = graph_module._directedRelation("isCustomer", ["shop"], ["customer"]); +/// var rel = graph_module._relation("isCustomer", ["shop"], ["customer"]); /// graph._extendEdgeDefinitions(rel); /// graph; /// ~ graph_module._drop("myGraph", true); @@ -1654,7 +1702,7 @@ var sortEdgeDefinition = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphCreateGraph2} /// var graph_module = require("org/arangodb/general-graph"); /// | graph = graph_module._create("myGraph", -/// [graph_module._undirectedRelation("myRelation", ["male", "female"])], ["sessions"]); +/// [graph_module._relation("myRelation", ["male", "female"])], ["sessions"]); /// ~ graph_module._drop("myGraph", true); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -3900,8 +3948,8 @@ Graph.prototype._diameter = function(options) { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__extendEdgeDefinitions} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var ed2 = graph_module._directedRelation("myEC2", ["myVC1"], ["myVC3"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph_module._relation("myEC2", ["myVC1"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._extendEdgeDefinitions(ed2); /// ~ var blub = graph_module._drop("myGraph", true); @@ -4081,8 +4129,8 @@ var changeEdgeDefinitionsForGraph = function(graph, edgeDefinition, newCollectio /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__editEdgeDefinition} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var original = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var modified = graph_module._directedRelation("myEC1", ["myVC2"], ["myVC3"]); +/// var original = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var modified = graph_module._relation("myEC1", ["myVC2"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [original]); /// graph._editEdgeDefinitions(modified); /// ~ var blub = graph_module._drop("myGraph", true); @@ -4160,8 +4208,8 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__deleteEdgeDefinition} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var ed2 = graph_module._directedRelation("myEC2", ["myVC1"], ["myVC3"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph_module._relation("myEC2", ["myVC1"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [ed1, ed2]); /// graph._deleteEdgeDefinition("myEC1"); /// db._collection("myEC1"); @@ -4173,8 +4221,8 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition) { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__deleteEdgeDefinitionWithDrop} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); -/// var ed2 = graph_module._directedRelation("myEC2", ["myVC1"], ["myVC3"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed2 = graph_module._relation("myEC2", ["myVC1"], ["myVC3"]); /// var graph = graph_module._create("myGraph", [ed1, ed2]); /// graph._deleteEdgeDefinition("myEC1", true); /// db._collection("myEC1"); @@ -4258,7 +4306,7 @@ Graph.prototype._deleteEdgeDefinition = function(edgeCollection, dropCollection) /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__addVertexCollection} /// var graph_module = require("org/arangodb/general-graph"); /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._addVertexCollection("myVC3", true); /// ~ var blub = graph_module._drop("myGraph", true); @@ -4318,7 +4366,7 @@ Graph.prototype._addVertexCollection = function(vertexCollectionName, createColl /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__orphanCollections} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._addVertexCollection("myVC3", true); /// graph._orphanCollections(); @@ -4357,7 +4405,7 @@ Graph.prototype._orphanCollections = function() { /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__removeVertexCollections} /// var graph_module = require("org/arangodb/general-graph") /// ~ if (graph_module._exists("myGraph")){var blub = graph_module._drop("myGraph", true);} -/// var ed1 = graph_module._directedRelation("myEC1", ["myVC1"], ["myVC2"]); +/// var ed1 = graph_module._relation("myEC1", ["myVC1"], ["myVC2"]); /// var graph = graph_module._create("myGraph", [ed1]); /// graph._addVertexCollection("myVC3", true); /// graph._addVertexCollection("myVC4", true); @@ -4422,7 +4470,9 @@ Graph.prototype._PRINT = function(context) { // --SECTION-- MODULE EXPORTS // ----------------------------------------------------------------------------- +/// Deprecated function (announced 2.3) exports._undirectedRelation = _undirectedRelation; +/// Deprecated function (announced 2.3) exports._directedRelation = function () { return _relation.apply(this, arguments); } ; @@ -4455,9 +4505,9 @@ exports._listObjects = _listObjects; /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph_create_graph_example1} /// var graph_module = require("org/arangodb/general-graph"); /// var edgeDefinitions = graph_module._edgeDefinitions(); -/// graph_module._extendEdgeDefinitions(edgeDefinitions, graph_module._undirectedRelation("friend_of", ["Customer"])); +/// graph_module._extendEdgeDefinitions(edgeDefinitions, graph_module._relation("friend_of", "Customer", "Customer")); /// | graph_module._extendEdgeDefinitions( -/// | edgeDefinitions, graph_module._directedRelation( +/// | edgeDefinitions, graph_module._relation( /// "has_bought", ["Customer", "Company"], ["Groceries", "Electronics"])); /// graph_module._create("myStore", edgeDefinitions); /// ~ graph_module._drop("myStore"); @@ -4472,7 +4522,7 @@ exports._listObjects = _listObjects; /// @EXAMPLE_ARANGOSH_OUTPUT{general_graph_create_graph_example2} /// var graph_module = require("org/arangodb/general-graph"); /// | var edgeDefinitions = graph_module._edgeDefinitions( -/// | graph_module._undirectedRelation("friend_of", ["Customer"]), graph_module._directedRelation( +/// | graph_module._relation("friend_of", ["Customer"]), graph_module._relation( /// "has_bought", ["Customer", "Company"], ["Groceries", "Electronics"])); /// graph_module._create("myStore", edgeDefinitions); /// ~ graph_module._drop("myStore"); From cb4c60a36f15be1c8c4102788df30bf7b9c6f29b Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 11 Sep 2014 09:54:59 +0200 Subject: [PATCH 11/13] explain tests --- arangod/Aql/ExecutionNode.cpp | 4 +- arangod/Aql/Query.cpp | 31 +- arangod/Aql/Query.h | 27 +- arangod/V8Server/v8-vocbase.cpp | 10 +- js/server/tests/aql-explain.js | 294 ++++++++++++++++++ ...-rule-interchange-adjacent-enumerations.js | 4 + 6 files changed, 349 insertions(+), 21 deletions(-) create mode 100644 js/server/tests/aql-explain.js diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 916b27ef0d..3a752a618d 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -406,10 +406,8 @@ Json ExecutionNode::toJsonHelperGeneric (triagens::basics::Json& nodes, json("parents", parents); } json("id", Json(static_cast(id()))); + json("estimatedCost", Json(_estimatedCost)); - if (_estimatedCost != 0.0) { - json("estimatedCost", Json(_estimatedCost)); - } return json; } diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 6865389eeb..48a820733e 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -353,7 +353,7 @@ QueryResult Query::parse () { /// @brief explain an AQL query //////////////////////////////////////////////////////////////////////////////// -QueryResult Query::explain (bool returnAllPlans) { +QueryResult Query::explain () { try { ExecutionPlan* plan; Parser parser(this); @@ -393,14 +393,14 @@ QueryResult Query::explain (bool returnAllPlans) { QueryResult result(TRI_ERROR_NO_ERROR); - if (returnAllPlans) { + if (allPlans()) { triagens::basics::Json out(triagens::basics::Json::List); auto plans = opt.getPlans(); for (auto it : plans) { TRI_ASSERT(it != nullptr); - out.add(it->toJson(TRI_UNKNOWN_MEM_ZONE, true)); + out.add(it->toJson(TRI_UNKNOWN_MEM_ZONE, verbosePlans())); } result.json = out.steal(); @@ -410,7 +410,7 @@ QueryResult Query::explain (bool returnAllPlans) { plan = opt.stealBest(); // Now we own the best one again TRI_ASSERT(plan != nullptr); - result.json = plan->toJson(TRI_UNKNOWN_MEM_ZONE, false).steal(); + result.json = plan->toJson(TRI_UNKNOWN_MEM_ZONE, verbosePlans()).steal(); delete plan; } @@ -502,14 +502,30 @@ char* Query::registerString (std::string const& p, // --SECTION-- private methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief fetch a boolean value from the options +//////////////////////////////////////////////////////////////////////////////// + +bool Query::getBooleanOption (char const* option, bool defaultValue) const { + if (! TRI_IsArrayJson(_options)) { + return defaultValue; + } + + TRI_json_t const* valueJson = TRI_LookupArrayJson(_options, option); + if (! TRI_IsBooleanJson(valueJson)) { + return defaultValue; + } + + return valueJson->_value._boolean; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief neatly format transaction errors to the user. //////////////////////////////////////////////////////////////////////////////// -QueryResult Query::transactionError (int errorCode, AQL_TRANSACTION_V8 const& trx) +QueryResult Query::transactionError (int errorCode, AQL_TRANSACTION_V8 const& trx) const { - std::string err; - err += std::string(TRI_errno_string(errorCode)); + std::string err(TRI_errno_string(errorCode)); auto detail = trx.getErrorData(); if (detail.size() > 0) { @@ -521,7 +537,6 @@ QueryResult Query::transactionError (int errorCode, AQL_TRANSACTION_V8 const& tr return QueryResult(errorCode, err); } - //////////////////////////////////////////////////////////////////////////////// /// @brief read the "optimizer.rules" section from the options //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Query.h b/arangod/Aql/Query.h index e687a9a9cc..bcff98011d 100644 --- a/arangod/Aql/Query.h +++ b/arangod/Aql/Query.h @@ -155,6 +155,22 @@ namespace triagens { return _queryLength; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief should we return verbose plans? +//////////////////////////////////////////////////////////////////////////////// + + bool verbosePlans () const { + return getBooleanOption("verbosePlans", false); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief should we return all plans? +//////////////////////////////////////////////////////////////////////////////// + + bool allPlans () const { + return getBooleanOption("allPlans", false); + } + //////////////////////////////////////////////////////////////////////////////// /// @brief extract a region from the query //////////////////////////////////////////////////////////////////////////////// @@ -185,7 +201,7 @@ namespace triagens { /// @brief explain an AQL query //////////////////////////////////////////////////////////////////////////////// - QueryResult explain (bool); + QueryResult explain (); //////////////////////////////////////////////////////////////////////////////// /// @brief get v8 executor @@ -214,6 +230,13 @@ namespace triagens { // --SECTION-- private methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief fetch a boolean value from the options +//////////////////////////////////////////////////////////////////////////////// + + bool getBooleanOption (char const*, + bool) const; + //////////////////////////////////////////////////////////////////////////////// /// @brief read the "optimizer.rules" section from the options //////////////////////////////////////////////////////////////////////////////// @@ -224,7 +247,7 @@ namespace triagens { /// @brief neatly format transaction errors to the user. //////////////////////////////////////////////////////////////////////////////// - QueryResult transactionError (int errorCode, AQL_TRANSACTION_V8 const& trx); + QueryResult transactionError (int errorCode, AQL_TRANSACTION_V8 const& trx) const; // ----------------------------------------------------------------------------- // --SECTION-- private variables diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 4ffc4afaf6..289cadfe88 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -889,7 +889,6 @@ static v8::Handle JS_ExplainAql (v8::Arguments const& argv) { } } - bool returnAllPlans = false; TRI_json_t* options = nullptr; if (argv.Length() > 2) { @@ -901,18 +900,13 @@ static v8::Handle JS_ExplainAql (v8::Arguments const& argv) { TRI_V8_TYPE_ERROR(scope, "expecting object for "); } - if (argv[2]->ToObject()->Has(TRI_V8_STRING("allPlans"))) { - // should we return all plans? - returnAllPlans = TRI_ObjectToBoolean(argv[2]->ToObject()->Get(TRI_V8_STRING("allPlans"))); - } - options = TRI_ObjectToJson(argv[2]); } // bind parameters will be freed by the query later triagens::aql::Query query(vocbase, queryString.c_str(), queryString.size(), parameters, options); - auto queryResult = query.explain(returnAllPlans); + auto queryResult = query.explain(); if (queryResult.code != TRI_ERROR_NO_ERROR) { TRI_V8_EXCEPTION_FULL(scope, queryResult.code, queryResult.details); @@ -920,7 +914,7 @@ static v8::Handle JS_ExplainAql (v8::Arguments const& argv) { v8::Handle result = v8::Object::New(); if (queryResult.json != nullptr) { - if (returnAllPlans) { + if (query.allPlans()) { result->Set(TRI_V8_STRING("plans"), TRI_ObjectJson(queryResult.json)); } else { diff --git a/js/server/tests/aql-explain.js b/js/server/tests/aql-explain.js new file mode 100644 index 0000000000..ac62af786e --- /dev/null +++ b/js/server/tests/aql-explain.js @@ -0,0 +1,294 @@ +/*global require, exports, assertTrue, assertEqual, AQL_EXECUTE, AQL_EXPLAIN */ +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests for optimizer rules +/// +/// @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 Jan Steemann +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var errors = require("internal").errors; +var internal = require("internal"); +var errors = internal.errors; +var db = require("org/arangodb").db; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function explainSuite () { + var cn = "UnitTestsAhuacatlExplain"; + var c; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + db._drop(cn); + c = db._create(cn); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + db._drop(cn); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test bind parameters +//////////////////////////////////////////////////////////////////////////////// + + testExplainBindMissing : function () { + var actual; + var query = "RETURN @foo"; + + try { + actual = AQL_EXPLAIN(query); + fail(); + } + catch (err) { + assertEqual(err.errorNum, errors.ERROR_QUERY_BIND_PARAMETER_MISSING.code); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test bind parameters +//////////////////////////////////////////////////////////////////////////////// + + testExplainBindPresent : function () { + var actual; + var query = "RETURN @foo"; + + actual = AQL_EXPLAIN(query, { foo: "bar" }); + assertEqual(3, actual.plan.nodes.length); + assertEqual("SingletonNode", actual.plan.nodes[0].type); + assertEqual("CalculationNode", actual.plan.nodes[1].type); + assertEqual("ReturnNode", actual.plan.nodes[2].type); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test verbosity w/ single plan +//////////////////////////////////////////////////////////////////////////////// + + testExplainVerbosity : function () { + var actual; + var query = "FOR i IN " + cn + " FOR j IN " + cn + " RETURN i"; + + // single plan, no options + actual = AQL_EXPLAIN(query); + assertTrue(actual.hasOwnProperty("plan")); + assertFalse(Array.isArray(actual.plan)); + assertTrue(actual.plan.hasOwnProperty("nodes")); + assertTrue(Array.isArray(actual.plan.nodes)); + assertTrue(actual.plan.hasOwnProperty("rules")); + assertTrue(Array.isArray(actual.plan.rules)); + assertFalse(actual.plan.hasOwnProperty("estimatedCost")); + + actual.plan.nodes.forEach(function(node) { + assertTrue(node.hasOwnProperty("type")); + assertFalse(node.hasOwnProperty("typeID")); // deactivated if not verbose + assertTrue(node.hasOwnProperty("dependencies")); + assertTrue(Array.isArray(node.dependencies)); + assertFalse(node.hasOwnProperty("parents")); // deactivated if not verbose + assertTrue(node.hasOwnProperty("id")); + assertTrue(node.hasOwnProperty("estimatedCost")); + }); + + // single plan, verbose options + actual = AQL_EXPLAIN(query, { }, { verbosePlans: true }); + assertTrue(actual.hasOwnProperty("plan")); + assertFalse(Array.isArray(actual.plan)); + assertTrue(actual.plan.hasOwnProperty("nodes")); + assertTrue(Array.isArray(actual.plan.nodes)); + assertTrue(actual.plan.hasOwnProperty("rules")); + assertTrue(Array.isArray(actual.plan.rules)); + + actual.plan.nodes.forEach(function(node) { + assertTrue(node.hasOwnProperty("type")); + assertTrue(node.hasOwnProperty("typeID")); + assertTrue(node.hasOwnProperty("dependencies")); + assertTrue(Array.isArray(node.dependencies)); + assertTrue(node.hasOwnProperty("parents")); + assertTrue(Array.isArray(node.parents)); + assertTrue(node.hasOwnProperty("id")); + assertTrue(node.hasOwnProperty("estimatedCost")); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test explain w/ a signle plan vs. all plans +//////////////////////////////////////////////////////////////////////////////// + + testExplainAllPlansVsSingle : function () { + var actual; + var query = "FOR i IN " + cn + " FOR j IN " + cn + " RETURN i"; + + // single plan + actual = AQL_EXPLAIN(query, { }, { verbosePlans: true }); + assertTrue(actual.hasOwnProperty("plan")); + assertFalse(actual.hasOwnProperty("plans")); + assertFalse(Array.isArray(actual.plan)); + + assertTrue(actual.plan.hasOwnProperty("nodes")); + assertTrue(Array.isArray(actual.plan.nodes)); + + actual.plan.nodes.forEach(function(node) { + assertTrue(node.hasOwnProperty("type")); + assertTrue(node.hasOwnProperty("typeID")); + assertTrue(node.hasOwnProperty("dependencies")); + assertTrue(Array.isArray(node.dependencies)); + assertTrue(node.hasOwnProperty("parents")); + assertTrue(node.hasOwnProperty("id")); + assertTrue(node.hasOwnProperty("estimatedCost")); + }); + + assertTrue(actual.plan.hasOwnProperty("rules")); + assertTrue(Array.isArray(actual.plan.rules)); + + + // multiple plans + actual = AQL_EXPLAIN(query, { }, { allPlans: true, verbosePlans: true }); + assertFalse(actual.hasOwnProperty("plan")); + assertTrue(actual.hasOwnProperty("plans")); + assertTrue(Array.isArray(actual.plans)); + + actual.plans.forEach(function (plan) { + assertTrue(plan.hasOwnProperty("nodes")); + assertTrue(Array.isArray(plan.nodes)); + + plan.nodes.forEach(function(node) { + assertTrue(node.hasOwnProperty("type")); + assertTrue(node.hasOwnProperty("typeID")); + assertTrue(node.hasOwnProperty("dependencies")); + assertTrue(Array.isArray(node.dependencies)); + assertTrue(node.hasOwnProperty("parents")); + assertTrue(node.hasOwnProperty("id")); + assertTrue(node.hasOwnProperty("estimatedCost")); + }); + + assertTrue(plan.hasOwnProperty("rules")); + assertTrue(Array.isArray(plan.rules)); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test nodes in plan +//////////////////////////////////////////////////////////////////////////////// + + testNodes : function () { + var actual; + var query = "FOR i IN " + cn + " FILTER i.value > 1 LET a = i.value / 2 SORT a DESC COLLECT x = a INTO g RETURN x"; + + actual = AQL_EXPLAIN(query, null, { optimizer: { rules: [ "-all" ] } }); + var nodes = actual.plan.nodes, node; + + node = nodes[0]; + assertEqual("SingletonNode", node.type); + assertEqual([ ], node.dependencies); + assertEqual(1, node.id); + assertEqual(1, node.estimatedCost); + + node = nodes[1]; + assertEqual("EnumerateCollectionNode", node.type); + assertEqual([ 1 ], node.dependencies); + assertEqual(2, node.id); + assertEqual(0, node.estimatedCost); + assertEqual("_system", node.database); + assertEqual(cn, node.collection); + assertEqual("i", node.outVariable.name); + + node = nodes[2]; + assertEqual("CalculationNode", node.type); + assertEqual([ 2 ], node.dependencies); + assertEqual(3, node.id); + assertTrue(node.hasOwnProperty("expression")); + assertFalse(node.canThrow); + var out = node.outVariable.name; + + node = nodes[3]; + assertEqual("FilterNode", node.type); + assertEqual([ 3 ], node.dependencies); + assertEqual(4, node.id); + assertEqual(0, node.estimatedCost); + assertEqual(out, node.inVariable.name); + + node = nodes[4]; + assertEqual("CalculationNode", node.type); + assertEqual([ 4 ], node.dependencies); + assertEqual(5, node.id); + assertTrue(node.hasOwnProperty("expression")); + assertEqual("a", node.outVariable.name); + assertTrue(node.canThrow); + + node = nodes[5]; + assertEqual("SortNode", node.type); + assertEqual([ 5 ], node.dependencies); + assertEqual(6, node.id); + assertEqual(1, node.elements.length); + assertEqual("a", node.elements[0].inVariable.name); + assertFalse(node.stable); + + node = nodes[6]; + assertEqual("SortNode", node.type); + assertEqual([ 6 ], node.dependencies); + assertEqual(7, node.id); + assertEqual(1, node.elements.length); + assertEqual("a", node.elements[0].inVariable.name); + assertTrue(node.stable); + + node = nodes[7]; + assertEqual("AggregateNode", node.type); + assertEqual([ 7 ], node.dependencies); + assertEqual(8, node.id); + assertEqual(1, node.aggregates.length); + assertEqual("a", node.aggregates[0].inVariable.name); + assertEqual("x", node.aggregates[0].outVariable.name); + assertEqual("g", node.outVariable.name); + + node = nodes[8]; + assertEqual("ReturnNode", node.type); + assertEqual([ 8 ], node.dependencies); + assertEqual(9, node.id); + assertEqual("x", node.inVariable.name); + } + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(explainSuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: diff --git a/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js b/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js index 6ccdefddbe..8ad2a1a9f7 100644 --- a/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js +++ b/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations.js @@ -85,6 +85,7 @@ function optimizerRuleTestSuite () { var opts = _.clone(paramNone); opts.allPlans = true; + opts.verbosePlans = true; queries.forEach(function(query) { var result = AQL_EXPLAIN(query, { }, opts); @@ -109,6 +110,7 @@ function optimizerRuleTestSuite () { var opts = _.clone(paramEnabled); opts.allPlans = true; + opts.verbosePlans = true; queries.forEach(function(query) { var result = AQL_EXPLAIN(query, { }, opts); @@ -135,6 +137,7 @@ function optimizerRuleTestSuite () { var opts = _.clone(paramEnabled); opts.allPlans = true; + opts.verbosePlans = true; queries.forEach(function(query) { var withRule = 0; @@ -171,6 +174,7 @@ function optimizerRuleTestSuite () { var opts = _.clone(paramEnabled); opts.allPlans = true; + opts.verbosePlans = true; queries.forEach(function(query) { var planDisabled = AQL_EXPLAIN(query[0], { }, paramDisabled); From 71600f89b72f1a995e0e8c7d33e554d0a50b61d5 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 11 Sep 2014 11:56:40 +0200 Subject: [PATCH 12/13] use RANGE AqlValue type --- arangod/Aql/AqlValue.cpp | 33 +++++++++++++++++++++++++++++---- arangod/Aql/AqlValue.h | 32 ++++++++++++++++++++++++++++++++ arangod/Aql/AstNode.cpp | 6 ++++++ arangod/Aql/ExecutionBlock.cpp | 9 ++++----- arangod/Aql/Expression.cpp | 18 +++++++++++++++++- arangod/Aql/Types.h | 2 ++ 6 files changed, 90 insertions(+), 10 deletions(-) diff --git a/arangod/Aql/AqlValue.cpp b/arangod/Aql/AqlValue.cpp index 4d7bdece5b..684854e39e 100644 --- a/arangod/Aql/AqlValue.cpp +++ b/arangod/Aql/AqlValue.cpp @@ -99,6 +99,27 @@ void AqlValue::destroy () { _type = EMPTY; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the name of an AqlValue type +//////////////////////////////////////////////////////////////////////////////// + +std::string AqlValue::getTypeString () const { + switch (_type) { + case JSON: + return "json"; + case SHAPED: + return "shaped"; + case DOCVEC: + return "docvec"; + case RANGE: + return "range"; + case EMPTY: + return "empty"; + } + + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief clone for recursive copying //////////////////////////////////////////////////////////////////////////////// @@ -242,7 +263,6 @@ bool AqlValue::isArray () const { THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } - //////////////////////////////////////////////////////////////////////////////// /// @brief get a string representation of the AqlValue //////////////////////////////////////////////////////////////////////////////// @@ -578,10 +598,15 @@ Json AqlValue::extractListMember (AQL_TRANSACTION_V8* trx, case RANGE: { TRI_ASSERT(_range != nullptr); size_t const n = _range->size(); - size_t const p = static_cast(position); - if (p < n) { - return Json(static_cast(_range->at(p))); + if (position < 0) { + // a negative position is allowed + position = static_cast(n) + position; + } + + if (position >= 0 && position < static_cast(n)) { + // only look up the value if it is within list bounds + return Json(static_cast(_range->at(static_cast(position)))); } break; // fall-through to returning null } diff --git a/arangod/Aql/AqlValue.h b/arangod/Aql/AqlValue.h index 1437d6035f..df1cc371f7 100644 --- a/arangod/Aql/AqlValue.h +++ b/arangod/Aql/AqlValue.h @@ -144,6 +144,12 @@ namespace triagens { void destroy (); +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the name of an AqlValue type +//////////////////////////////////////////////////////////////////////////////// + + std::string getTypeString () const; + //////////////////////////////////////////////////////////////////////////////// /// @brief clone for recursive copying //////////////////////////////////////////////////////////////////////////////// @@ -181,6 +187,32 @@ namespace triagens { std::string toString () const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the numeric value of an AqlValue +/// this will fail if the value is not a number +//////////////////////////////////////////////////////////////////////////////// + + template + T toNumber () const { + switch (_type) { + case JSON: { + TRI_json_t const* json = _json->json(); + TRI_ASSERT(TRI_IsNumberJson(json)); + return static_cast(json->_value._number); + } + + case SHAPED: + case DOCVEC: + case RANGE: + case EMPTY: { + // cannot convert these types + return 0; + } + } + + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } + //////////////////////////////////////////////////////////////////////////////// /// @brief get a string representation of the AqlValue /// this will fail if the value is not a string diff --git a/arangod/Aql/AstNode.cpp b/arangod/Aql/AstNode.cpp index 9fe1670dd0..4c3460cd09 100644 --- a/arangod/Aql/AstNode.cpp +++ b/arangod/Aql/AstNode.cpp @@ -571,11 +571,17 @@ bool AstNode::isSimple () const { if (type == NODE_TYPE_FCALL) { // some functions have C++ handlers + // check if the called function is one of them auto func = static_cast(getData()); TRI_ASSERT(func != nullptr); return (func->implementation != nullptr && getMember(0)->isSimple()); } + + if (type == NODE_TYPE_RANGE) { + // a range is simple if both bounds are simple + return (getMember(0)->isSimple() && getMember(1)->isSimple()); + } return false; } diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index b50f4ce73a..0db2192bef 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -1023,7 +1023,7 @@ void IndexRangeBlock::readSkiplistIndex () { skiplistOperator = TRI_CreateIndexOperator(TRI_EQ_INDEX_OPERATOR, nullptr, nullptr, parameters.copy().steal(), shaper, nullptr, i, nullptr); } - if (!range._low._undefined) { + if (! range._low._undefined) { auto op = range._low.toIndexOperator(false, parameters.copy(), shaper); if (skiplistOperator != nullptr) { skiplistOperator = TRI_CreateIndexOperator(TRI_AND_INDEX_OPERATOR, @@ -1033,7 +1033,7 @@ void IndexRangeBlock::readSkiplistIndex () { skiplistOperator = op; } } - if (!range._high._undefined) { + if (! range._high._undefined) { auto op = range._high.toIndexOperator(true, parameters.copy(), shaper); if (skiplistOperator != nullptr) { skiplistOperator = TRI_CreateIndexOperator(TRI_AND_INDEX_OPERATOR, @@ -1133,7 +1133,6 @@ void IndexRangeBlock::readHashIndex () { } } - //std::cout << "PID: " << pid << ", NAME: " << name << "\n"; } }; @@ -1360,7 +1359,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) { break; } case AqlValue::RANGE: { - sizeInVar = inVarReg._range->_high - inVarReg._range->_low + 1; + sizeInVar = inVarReg._range->size(); break; } case AqlValue::DOCVEC: { @@ -1412,7 +1411,7 @@ AqlValue EnumerateListBlock::getAqlValue (AqlValue inVarReg) { return AqlValue(new Json(inVarReg._json->at(_index++).copy())); } case AqlValue::RANGE: { - return AqlValue(new Json(static_cast(inVarReg._range->_low + _index++))); + return AqlValue(new Json(static_cast(inVarReg._range->at(_index++)))); } case AqlValue::DOCVEC: { // incoming doc vec has a single column AqlValue out = inVarReg._vector->at(_thisblock)->getValue(_index - diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index 31be5fb456..d2caef8b83 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -156,7 +156,7 @@ AqlValue Expression::execute (AQL_TRANSACTION_V8* trx, void Expression::analyzeExpression () { TRI_ASSERT(_type == UNPROCESSED); - + if (_node->isConstant()) { // generate a constant value _data = _node->toJsonValue(TRI_UNKNOWN_MEM_ZONE); @@ -346,6 +346,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, else if (node->type == NODE_TYPE_FCALL) { // some functions have C++ handlers + // check if the called function has one auto func = static_cast(node->getData()); TRI_ASSERT(func->implementation != nullptr); @@ -357,6 +358,21 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, result.destroy(); return res2; } + + else if (node->type == NODE_TYPE_RANGE) { + TRI_document_collection_t const* myCollection = nullptr; + + auto low = node->getMember(0); + auto high = node->getMember(1); + AqlValue resultLow = executeSimpleExpression(low, &myCollection, trx, docColls, argv, startPos, vars, regs); + AqlValue resultHigh = executeSimpleExpression(high, &myCollection, trx, docColls, argv, startPos, vars, regs); + + if (! resultLow.isNumber() || ! resultHigh.isNumber()) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "invalid data type for range"); + } + + return AqlValue(resultLow.toNumber(), resultHigh.toNumber()); + } THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unhandled type in simple expression"); } diff --git a/arangod/Aql/Types.h b/arangod/Aql/Types.h index 156ec1229d..5d9f1a0a82 100644 --- a/arangod/Aql/Types.h +++ b/arangod/Aql/Types.h @@ -44,6 +44,8 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// struct Range { + Range () = delete; + Range (int64_t low, int64_t high) : _low(low), From a30a4b2261d855c2f4eeb8b2238d53f6f0715965 Mon Sep 17 00:00:00 2001 From: Willi Goesgens Date: Thu, 11 Sep 2014 13:23:03 +0200 Subject: [PATCH 13/13] Add code coverage files to cleanup process --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index baa582c4cb..b8a63f6ccc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -393,6 +393,8 @@ distclean-local: clean-local: rm -rf $(CLEANUP) + rm -rf `find -name \*.gcno` + rm -rf `find -name \*.gcda` superclean: clean rm -rf arangod/*/.deps lib/*/.deps Makefile