diff --git a/Documentation/DbaManual/Authentication.md b/Documentation/DbaManual/Authentication.md index 12656c151a..db781a3509 100644 --- a/Documentation/DbaManual/Authentication.md +++ b/Documentation/DbaManual/Authentication.md @@ -17,9 +17,9 @@ fashion. The collection `_users` contains all users and a salted SHA256 hash of their passwords. A user can be active or inactive. A typical document of this collection is -@EXAMPLE_ARANGOSH_OUTPUT{AuthenticationExample1} - db._users.firstExample("user", "root") -@END_EXAMPLE_ARANGOSH_OUTPUT +@EXAMPLE_ARANGOSH_RUN{AuthenticationExample1} + log(db._users.firstExample("user", "root")); +@END_EXAMPLE_ARANGOSH_RUN Command-Line Options for the Authentication and Authorisation{#DbaManualAuthenticationCommandLine} -------------------------------------------------------------------------------------------------- diff --git a/Documentation/Scripts/generateExamples.py b/Documentation/Scripts/generateExamples.py index 6a8b07f534..609bc0b8b2 100644 --- a/Documentation/Scripts/generateExamples.py +++ b/Documentation/Scripts/generateExamples.py @@ -60,12 +60,18 @@ OutputDir = "/tmp/" ################################################################################ ### @brief arangosh output +### +### A list of commands that are executed in order to produce the output. The +### commands and there output is logged. ################################################################################ ArangoshOutput = {} ################################################################################ ### @brief arangosh run +### +### A list of commands that are executed in order to produce the output. This +### is mostly used for HTTP request examples. ################################################################################ ArangoshRun = {} @@ -150,7 +156,7 @@ for filename in argv: print >> sys.stderr, "%s\nduplicate file name '%s'\n%s\n" % ('#' * 80, name, '#' * 80) ArangoshFiles[name] = True - ArangoshOutput[name] = "" + ArangoshOutput[name] = [] state = STATE_ARANGOSH_OUTPUT continue @@ -179,7 +185,7 @@ for filename in argv: line = line[len(strip):] - ArangoshOutput[name] += line + "\n" + ArangoshOutput[name].append(line) elif state == STATE_ARANGOSH_RUN: m = r4.match(line) @@ -221,6 +227,8 @@ for filename in argv: ### @brief generate arangosh example ################################################################################ +gr1 = re.compile(r'^[ \n]*var ') + def generateArangoshOutput(): print "var internal = require('internal');" print "var fs = require('fs');" @@ -242,7 +250,21 @@ def generateArangoshOutput(): print "(function() {" print "internal.startCaptureMode();"; - print "try { var value = %s; print(value); } catch (err) { print(err); }" % value + print "try {" + print " var XXX;" + + for l in value: + m = gr1.match(l) + + print "print('arangosh> %s');" % l.replace("\\", "\\\\").replace("'", "\\'") + + if m: + print "%s" % l + else: + print "XXX = %s" % l + print "print(XXX);" + + print "} catch (err) { print(err); }" print "var output = internal.stopCaptureMode();"; print "ArangoshOutput['%s'] = output;" % key if JS_DEBUG: @@ -279,12 +301,13 @@ def generateArangoshRun(): print "internal.output('RUN STARTING: %s\\n');" % key print "var output = '';" print "var appender = function(text) { output += text; };" - print "var logCurlRequestRaw = require('internal').appendCurlRequest(appender);" + print "var log = function (a) { internal.startCaptureMode(); print(a); appender(internal.stopCaptureMode()); };" + print "var logCurlRequestRaw = internal.appendCurlRequest(appender);" print "var logCurlRequest = function () { var r = logCurlRequestRaw.apply(logCurlRequestRaw, arguments); db._collections(); return r; };" - print "var curlRequestRaw = require('internal').appendCurlRequest(function (text) { });" + print "var curlRequestRaw = internal.appendCurlRequest(function (text) {});" print "var curlRequest = function () { return curlRequestRaw.apply(curlRequestRaw, arguments); };" - print "var logJsonResponse = require('internal').appendJsonResponse(appender);" - print "var logRawResponse = require('internal').appendRawResponse(appender);" + print "var logJsonResponse = internal.appendJsonResponse(appender);" + print "var logRawResponse = internal.appendRawResponse(appender);" print "var assert = function(a) { if (! a) { internal.output('%s\\nASSERTION FAILED: %s\\n%s\\n'); throw new Error('assertion failed'); } };" % ('#' * 80, key, '#' * 80) print "try { %s internal.output('RUN SUCCEEDED: %s\\n'); } catch (err) { print('%s\\nRUN FAILED: %s, ', err, '\\n%s\\n'); }" % (value, key, '#' * 80, key, '#' * 80) print "ArangoshRun['%s'] = output;" % key diff --git a/Documentation/UserManual/HandlingDocuments.md b/Documentation/UserManual/HandlingDocuments.md index 19e07efd23..0d1ffb109f 100644 --- a/Documentation/UserManual/HandlingDocuments.md +++ b/Documentation/UserManual/HandlingDocuments.md @@ -15,9 +15,9 @@ corresponding language API. For example: -@EXAMPLE_ARANGOSH_OUTPUT{HandlingDocumentsExample1} - db.demo.document("demo/schlonz") -@END_EXAMPLE_ARANGOSH_OUTPUT +@EXAMPLE_ARANGOSH_RUN{HandlingDocumentsExample1} + log(db.demo.document("demo/schlonz")); +@END_EXAMPLE_ARANGOSH_RUN All documents contain special attributes: the document handle in `_id`, the document's unique key in `_key` and and the ETag aka document revision in diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 6c3905e7ed..6e19844281 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -344,6 +344,7 @@ SHELL_COMMON = \ @top_srcdir@/js/common/tests/shell-edge.js \ @top_srcdir@/js/common/tests/shell-errors.js \ @top_srcdir@/js/common/tests/shell-fs.js \ + @top_srcdir@/js/common/tests/shell-general-graph.js \ @top_srcdir@/js/common/tests/shell-graph-traversal.js \ @top_srcdir@/js/common/tests/shell-graph-algorithms.js \ @top_srcdir@/js/common/tests/shell-graph-measurement.js \ @@ -454,6 +455,7 @@ SHELL_SERVER_AHUACATL = @top_srcdir@/js/server/tests/ahuacatl-ranges.js \ @top_srcdir@/js/server/tests/ahuacatl-hash.js \ @top_srcdir@/js/server/tests/ahuacatl-skiplist.js \ @top_srcdir@/js/server/tests/ahuacatl-cross.js \ + @top_srcdir@/js/server/tests/ahuacatl-general-graph.js \ @top_srcdir@/js/server/tests/ahuacatl-graph.js \ @top_srcdir@/js/server/tests/ahuacatl-edges.js \ @top_srcdir@/js/server/tests/ahuacatl-refaccess-variable.js \ diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 1886e41b26..8854ebe30d 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -438,6 +438,19 @@ static TRI_doc_update_policy_e ExtractUpdatePolicy (v8::Arguments const& argv, return policy; } +TRI_doc_update_policy_e ExtractUpdatePolicy (bool overwrite) { + TRI_doc_update_policy_e policy ; + + if (overwrite) { + // overwrite! + policy = TRI_DOC_UPDATE_LAST_WRITE; + } + else { + policy = TRI_DOC_UPDATE_CONFLICT; + } + + return policy; +} //////////////////////////////////////////////////////////////////////////////// /// @brief wraps a C++ into a v8::Object //////////////////////////////////////////////////////////////////////////////// @@ -2508,6 +2521,17 @@ static v8::Handle SaveEdgeCol ( return scope.Close(result); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal struct which is used for reading the different option +/// parameters for the update function +//////////////////////////////////////////////////////////////////////////////// + +struct UpdateOptions { + bool overwrite = true; + bool keepNull = true; + bool waitForSync = false; +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief updates (patches) a document //////////////////////////////////////////////////////////////////////////////// @@ -2515,18 +2539,43 @@ static v8::Handle SaveEdgeCol ( static v8::Handle UpdateVocbaseCol (bool useCollection, v8::Arguments const& argv) { v8::HandleScope scope; + UpdateOptions options; + TRI_doc_update_policy_e policy = TRI_DOC_UPDATE_ERROR; // check the arguments if (argv.Length() < 2 || argv.Length() > 5) { - TRI_V8_EXCEPTION_USAGE(scope, "update(, , , , )"); + TRI_V8_EXCEPTION_USAGE(scope, "update(, , , , )"); } - const TRI_doc_update_policy_e policy = ExtractUpdatePolicy(argv, 3); + if (argv.Length() > 2) { + if (argv[2]->IsObject()) { + v8::Handle optionsObject = argv[2].As(); + if (optionsObject->Has(v8::String::New("overwrite"))) { + options.overwrite = TRI_ObjectToBoolean(optionsObject->Get(v8::String::New("overwrite"))); + policy = ExtractUpdatePolicy(options.overwrite); + } + if (optionsObject->Has(v8::String::New("keepNull"))) { + options.keepNull = TRI_ObjectToBoolean(optionsObject->Get(v8::String::New("keepNull"))); + } + if (optionsObject->Has(v8::String::New("waitForSync"))) { + options.waitForSync = TRI_ObjectToBoolean(optionsObject->Get(v8::String::New("waitForSync"))); + } + } else { // old variant update(, , , , ) + if (argv.Length() > 2 ) { + options.overwrite = TRI_ObjectToBoolean(argv[2]); + policy = ExtractUpdatePolicy(options.overwrite); + } + if (argv.Length() > 3 ) { + options.keepNull = TRI_ObjectToBoolean(argv[3]); + } + if (argv.Length() > 4 ) { + options.waitForSync = TRI_ObjectToBoolean(argv[4]); + } + } + } +// LOG_ERROR ( "overwrite %d keepNull %d waitForSync %d" , options.overwrite , options.keepNull , options.waitForSync); // delete null attributes // default value: null values are saved as Null - const bool nullMeansRemove = (argv.Length() >= 4 && ! TRI_ObjectToBoolean(argv[3])); - const bool forceSync = ExtractForceSync(argv, 5); - TRI_voc_key_t key = 0; TRI_voc_rid_t rid; @@ -2573,9 +2622,9 @@ static v8::Handle UpdateVocbaseCol (bool useCollection, if (ServerState::instance()->isCoordinator()) { return scope.Close(ModifyVocbaseColCoordinator(col, policy, - forceSync, + options.waitForSync, true, // isPatch - ! nullMeansRemove, // keepNull + ! options.keepNull, argv)); } @@ -2641,7 +2690,7 @@ static v8::Handle UpdateVocbaseCol (bool useCollection, } } - TRI_json_t* patchedJson = TRI_MergeJson(TRI_UNKNOWN_MEM_ZONE, old, json, nullMeansRemove); + TRI_json_t* patchedJson = TRI_MergeJson(TRI_UNKNOWN_MEM_ZONE, old, json, ! options.keepNull); TRI_FreeJson(zone, old); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); @@ -2650,7 +2699,7 @@ static v8::Handle UpdateVocbaseCol (bool useCollection, TRI_V8_EXCEPTION_MEMORY(scope); } - res = trx.updateDocument(key, &document, patchedJson, policy, forceSync, rid, &actualRevision); + res = trx.updateDocument(key, &document, patchedJson, policy, options.waitForSync, rid, &actualRevision); res = trx.finish(res); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, patchedJson); diff --git a/js/actions/api-graph.js b/js/actions/api-graph.js index 2a738da71c..7c82525415 100644 --- a/js/actions/api-graph.js +++ b/js/actions/api-graph.js @@ -512,13 +512,16 @@ function delete_graph_graph (req, res) { //////////////////////////////////////////////////////////////////////////////// /// @brief creates a graph vertex /// -/// @RESTHEADER{POST /_api/graph/`graph-name`/vertex,create vertex} +/// @RESTHEADER{POST /_api/graph/`graph-name`/`vertex-name`,create vertex} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{vertex-name,string,required} +/// The name of the vertex +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -600,7 +603,7 @@ function post_graph_vertex (req, res, g) { //////////////////////////////////////////////////////////////////////////////// /// @brief gets the vertex properties /// -/// @RESTHEADER{GET /_api/graph/`graph-name`/vertex,get vertex} +/// @RESTHEADER{GET /_api/graph/`graph-name`/`vertex-name`,get vertex} /// /// @RESTURLPARAMETERS /// @@ -694,13 +697,16 @@ function get_graph_vertex (req, res, g) { //////////////////////////////////////////////////////////////////////////////// /// @brief delete vertex /// -/// @RESTHEADER{DELETE /_api/graph/`graph-name`/vertex,delete vertex} +/// @RESTHEADER{DELETE /_api/graph/`graph-name`/`vertex-name`,delete vertex} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{vertex-name,string,required} +/// The name of the vertex +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -858,13 +864,16 @@ function update_graph_vertex (req, res, g, isPatch) { //////////////////////////////////////////////////////////////////////////////// /// @brief updates a vertex /// -/// @RESTHEADER{PUT /_api/graph/`graph-name`/vertex,update vertex} +/// @RESTHEADER{PUT /_api/graph/`graph-name`/`vertex-name`,update vertex} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{vertex-name,string,required} +/// The name of the vertex +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -933,13 +942,16 @@ function put_graph_vertex (req, res, g) { //////////////////////////////////////////////////////////////////////////////// /// @brief updates a vertex /// -/// @RESTHEADER{PATCH /_api/graph/`graph-name`/vertex,update vertex} +/// @RESTHEADER{PATCH /_api/graph/`graph-name`/`vertex-name`,update vertex} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{vertex-name,string,required} +/// The name of the vertex +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -1389,13 +1401,16 @@ function post_graph_vertex_vertices (req, res, g) { //////////////////////////////////////////////////////////////////////////////// /// @brief creates an edge /// -/// @RESTHEADER{POST /_api/graph/`graph-name`/edge,create edge} +/// @RESTHEADER{POST /_api/graph/`graph-name`/`edge-name`,create edge} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{edge-name,string,required} +/// The name of the edge +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -1587,13 +1602,16 @@ function get_graph_edge (req, res, g) { //////////////////////////////////////////////////////////////////////////////// /// @brief deletes an edge /// -/// @RESTHEADER{DELETE /_api/graph/`graph-name`/edge,delete edge} +/// @RESTHEADER{DELETE /_api/graph/`graph-name`/`edge-name`,delete edge} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{edge-name,string,required} +/// The name of the edge +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -1754,13 +1772,16 @@ function update_graph_edge (req, res, g, isPatch) { //////////////////////////////////////////////////////////////////////////////// /// @brief updates an edge /// -/// @RESTHEADER{PUT /_api/graph/`graph-name`/edge,update edge} +/// @RESTHEADER{PUT /_api/graph/`graph-name`/`edge-name`,update edge} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{edge-name,string,required} +/// The name of the edge +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -1833,13 +1854,16 @@ function put_graph_edge (req, res, g) { //////////////////////////////////////////////////////////////////////////////// /// @brief updates an edge /// -/// @RESTHEADER{PATCH /_api/graph/`graph-name`/edge,update edge} +/// @RESTHEADER{PATCH /_api/graph/`graph-name`/`edge-name`,update edge} /// /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{graph-name,string,required} /// The name of the graph /// +/// @RESTURLPARAM{edge-name,string,required} +/// The name of the edge +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection.js index 276cf77278..257d6a02e1 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-collection.js @@ -1200,12 +1200,29 @@ ArangoCollection.prototype.update = function (id, data, overwrite, keepNull, wai id = id._id; } - // set default value for keepNull - var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); - var params = "?keepNull=" + (keepNullValue ? "true" : "false"); + var params = ""; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (! options.hasOwnProperty("keepNull")) { + options.keepNull = true; + } + params = "?keepNull=" + options.keepNull; + + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "&policy=last"; + } + + } else { + + // set default value for keepNull + var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); + params = "?keepNull=" + (keepNullValue ? "true" : "false"); + + if (overwrite) { + params += "&policy=last"; + } - if (overwrite) { - params += "&policy=last"; } var url = this._documenturl(id) + params; diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-database.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-database.js index 2c5bdfaeb3..f5973f0746 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-database.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/arango-database.js @@ -747,14 +747,27 @@ ArangoDatabase.prototype._update = function (id, data, overwrite, keepNull, wait id = id._id; } - // set default value for keepNull - var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); - var params = "?keepNull=" + (keepNullValue ? "true" : "false"); + var params = ""; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (! options.hasOwnProperty("keepNull")) { + options.keepNull = true; + } + params = "?keepNull=" + options.keepNull; - if (overwrite) { - params += "&policy=last"; + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "&policy=last"; + } + } else { + // set default value for keepNull + var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); + params = "?keepNull=" + (keepNullValue ? "true" : "false"); + + if (overwrite) { + params += "&policy=last"; + } } - var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); diff --git a/js/apps/system/aardvark/frontend/js/templates/modalWarning.ejs b/js/apps/system/aardvark/frontend/js/templates/modalWarning.ejs deleted file mode 100644 index 910165b64a..0000000000 --- a/js/apps/system/aardvark/frontend/js/templates/modalWarning.ejs +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/js/apps/system/aardvark/frontend/js/views/dashboardView.js b/js/apps/system/aardvark/frontend/js/views/dashboardView.js index 4d2ae0ea41..c31b644c86 100644 --- a/js/apps/system/aardvark/frontend/js/views/dashboardView.js +++ b/js/apps/system/aardvark/frontend/js/views/dashboardView.js @@ -97,7 +97,8 @@ undefined, this.events ); - window.modalView.hideFooter = false; + + window.modalView.hideFooter = false; $('#modal-dialog').on('hidden', function () { self.hidden(); @@ -411,13 +412,7 @@ if (d.times.length > 0) { self.isUpdating = true; self.mergeHistory(d); - } else if (self.isUpdating !== true) { - window.modalView.show( - "modalWarning.ejs", - "WARNING !" - ); - self.isUpdating = false; - } + } } ); }, diff --git a/js/apps/system/aardvark/manifest.json b/js/apps/system/aardvark/manifest.json index a70dc603ff..7284407d12 100644 --- a/js/apps/system/aardvark/manifest.json +++ b/js/apps/system/aardvark/manifest.json @@ -47,7 +47,6 @@ "frontend/js/templates/modalCollectionInfo.ejs", "frontend/js/templates/modalGraph.ejs", "frontend/js/templates/modalNewVersion.ejs", - "frontend/js/templates/modalWarning.ejs", "frontend/js/templates/modalTable.ejs", "frontend/js/templates/modalHotkeys.ejs", "frontend/js/templates/navigationView.ejs", diff --git a/js/apps/system/aardvark/test/specs/views/dashboardViewSpec.js b/js/apps/system/aardvark/test/specs/views/dashboardViewSpec.js index f1082df0c4..e9bb686285 100644 --- a/js/apps/system/aardvark/test/specs/views/dashboardViewSpec.js +++ b/js/apps/system/aardvark/test/specs/views/dashboardViewSpec.js @@ -712,32 +712,6 @@ }); - it("getStatistics without nextStart and no data yet", function () { - view.server = { - endpoint: "abcde", - target: "xyz" - }; - spyOn(view, "mergeHistory"); - spyOn(modalDummy, "show"); - spyOn($, "ajax").andCallFake(function (url, opt) { - expect(opt.async).toEqual(false); - return { - done: function (y) { - y({ - times: [] - }); - } - }; - }); - - view.getStatistics(); - expect(view.mergeHistory).not.toHaveBeenCalled(); - expect(modalDummy.show).toHaveBeenCalledWith("modalWarning.ejs", - "WARNING !"); - expect(view.isUpdating).toEqual(false); - }); - - it("prepare D3Charts", function () { spyOn(nv, "addGraph").andCallFake(function (a, b) { a(); diff --git a/js/client/modules/org/arangodb/arango-collection.js b/js/client/modules/org/arangodb/arango-collection.js index c0228ddd96..5ca8d2bb58 100644 --- a/js/client/modules/org/arangodb/arango-collection.js +++ b/js/client/modules/org/arangodb/arango-collection.js @@ -1199,12 +1199,29 @@ ArangoCollection.prototype.update = function (id, data, overwrite, keepNull, wai id = id._id; } - // set default value for keepNull - var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); - var params = "?keepNull=" + (keepNullValue ? "true" : "false"); + var params = ""; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (! options.hasOwnProperty("keepNull")) { + options.keepNull = true; + } + params = "?keepNull=" + options.keepNull; + + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "&policy=last"; + } + + } else { + + // set default value for keepNull + var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); + params = "?keepNull=" + (keepNullValue ? "true" : "false"); + + if (overwrite) { + params += "&policy=last"; + } - if (overwrite) { - params += "&policy=last"; } var url = this._documenturl(id) + params; diff --git a/js/client/modules/org/arangodb/arango-database.js b/js/client/modules/org/arangodb/arango-database.js index 0c2d557338..45cdf83059 100644 --- a/js/client/modules/org/arangodb/arango-database.js +++ b/js/client/modules/org/arangodb/arango-database.js @@ -746,14 +746,27 @@ ArangoDatabase.prototype._update = function (id, data, overwrite, keepNull, wait id = id._id; } - // set default value for keepNull - var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); - var params = "?keepNull=" + (keepNullValue ? "true" : "false"); + var params = ""; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (! options.hasOwnProperty("keepNull")) { + options.keepNull = true; + } + params = "?keepNull=" + options.keepNull; - if (overwrite) { - params += "&policy=last"; + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "&policy=last"; + } + } else { + // set default value for keepNull + var keepNullValue = ((typeof keepNull === "undefined") ? true : keepNull); + params = "?keepNull=" + (keepNullValue ? "true" : "false"); + + if (overwrite) { + params += "&policy=last"; + } } - var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index b5daa0eefc..3967199ced 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -24,7 +24,7 @@ /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// -/// @author Florian Bartels +/// @author Florian Bartels, Michael Hackstein, Guido Schwab /// @author Copyright 2011-2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -123,7 +123,7 @@ var findOrCreateCollectionsByEdgeDefinitions = function (edgeDefinitions, noCrea /// @brief internal function to get graphs collection //////////////////////////////////////////////////////////////////////////////// -var _getGraphCollection = function() { +var getGraphCollection = function() { var gCol = db._graphs; if (gCol === null || gCol === undefined) { throw "_graphs collection does not exist."; @@ -131,6 +131,20 @@ var _getGraphCollection = function() { return gCol; }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal function to wrap arango collections for overwrite +//////////////////////////////////////////////////////////////////////////////// + +var wrapCollection = function(col) { + var wrapper = {}; + _.each(_.functions(col), function(func) { + wrapper[func] = function() { + return col[func].apply(col, arguments); + }; + }); + return wrapper; +}; + // ----------------------------------------------------------------------------- @@ -164,10 +178,25 @@ var AQLGenerator = function(graph) { "graphName": graph.__name }; this.graph = graph; + this.cursor = null; this.lastEdgeVar = ""; }; +AQLGenerator.prototype._clearCursor = function() { + if (this.cursor) { + this.cursor.dispose(); + this.cursor = null; + } +}; + +AQLGenerator.prototype._createCursor = function() { + if (!this.cursor) { + this.cursor = this.execute(); + } +}; + AQLGenerator.prototype.edges = function(startVertex, direction) { + this._clearCursor(); var edgeName = "edges_" + this.stack.length; var query = "FOR " + edgeName + " IN GRAPH_EDGES(@graphName,@startVertex_" @@ -188,6 +217,7 @@ AQLGenerator.prototype.getLastEdgeVar = function() { }; AQLGenerator.prototype.restrict = function(restrictions) { + this._clearCursor(); var rest = stringToArray(restrictions); var unknown = []; var g = this.graph; @@ -215,6 +245,7 @@ AQLGenerator.prototype.restrict = function(restrictions) { }; AQLGenerator.prototype.filter = function(example) { + this._clearCursor(); var ex = []; if (Object.prototype.toString.call(example) !== "[object Array]") { if (Object.prototype.toString.call(example) !== "[object Object]") { @@ -254,14 +285,31 @@ AQLGenerator.prototype.printQuery = function() { }; AQLGenerator.prototype.execute = function() { + this._clearCursor(); var query = this.printQuery(); var bindVars = this.bindVars; query += " RETURN " + this.getLastEdgeVar(); - return db._query(query, bindVars); + return db._query(query, bindVars, {count: true}); }; AQLGenerator.prototype.toArray = function() { - return this.execute().toArray(); + this._createCursor(); + return this.cursor.toArray(); +}; + +AQLGenerator.prototype.count = function() { + this._createCursor(); + return this.cursor.count(); +}; + +AQLGenerator.prototype.hasNext = function() { + this._createCursor(); + return this.cursor.hasNext(); +}; + +AQLGenerator.prototype.next = function() { + this._createCursor(); + return this.cursor.next(); }; // ----------------------------------------------------------------------------- @@ -269,7 +317,32 @@ AQLGenerator.prototype.toArray = function() { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// +/// @fn JSF_general_graph_undirectedRelationDefinition /// @brief define an undirected relation. +/// +/// @FUN{general-graph._undirectedRelationDefinition(@FA{relationName}, @FA{vertexCollections})} +/// +/// Defines an undirected relation with the name @FA{relationName} using the +/// list of @FA{vertexCollections}. This relation allows the user to store +/// edges in any direction between any pair of vertices within the +/// @FA{vertexCollections}. +/// +/// @EXAMPLES +/// +/// To define simple relation with only one vertex collection: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphUndirectedRelationDefinition1} +/// var graph = require("org/arangodb/general-graph"); +/// graph._undirectedRelationDefinition("friend", "user"); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// +/// To define a relation between several vertex collections: +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphUndirectedRelationDefinition2} +/// var graph = require("org/arangodb/general-graph"); +/// graph._undirectedRelationDefinition("marriage", ["female", "male"]); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// //////////////////////////////////////////////////////////////////////////////// @@ -331,7 +404,7 @@ var _directedRelationDefinition = function ( //////////////////////////////////////////////////////////////////////////////// -var edgeDefinitions = function () { +var _edgeDefinitions = function () { var res = [], args = arguments; Object.keys(args).forEach(function (x) { @@ -342,6 +415,20 @@ var edgeDefinitions = function () { }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief extend an edge definitions +//////////////////////////////////////////////////////////////////////////////// + + +var _extendEdgeDefinitions = function () { + + var args = arguments; + var res = args[0]; + args = args.slice(1); + res.concat(args); + +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief create a new graph //////////////////////////////////////////////////////////////////////////////// @@ -349,7 +436,7 @@ var edgeDefinitions = function () { var _create = function (graphName, edgeDefinitions) { - var gdb = _getGraphCollection(), + var gdb = getGraphCollection(), g, graphAlreadyExists = true, collections; @@ -396,11 +483,10 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti this.__edgeCollections = edgeCollections; this.__edgeDefinitions = edgeDefinitions; - _.each(vertexCollections, function(obj, key) { - self[key] = obj; - var old_remove = obj.remove.bind(obj); - obj.remove = function(vertexId, options) { + var wrap = wrapCollection(obj); + var old_remove = wrap.remove; + wrap.remove = function(vertexId, options) { var myEdges = self._EDGES(vertexId); myEdges.forEach( function(edgeObj) { @@ -417,12 +503,13 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti } return old_remove(vertexId, options); }; + self[key] = wrap; }); _.each(edgeCollections, function(obj, key) { - self[key] = obj; - var old_save = obj.save.bind(obj); - obj.save = function(from, to, data) { + var wrap = wrapCollection(obj); + var old_save = wrap.save; + wrap.save = function(from, to, data) { edgeDefinitions.forEach( function(edgeDefinition) { if (edgeDefinition.collection === key) { @@ -437,6 +524,7 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti ); return old_save(from, to, data); }; + self[key] = wrap; }); }; @@ -448,7 +536,7 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti var _graph = function(graphName) { - var gdb = _getGraphCollection(), + var gdb = getGraphCollection(), g, collections; try { @@ -471,17 +559,46 @@ var _graph = function(graphName) { //////////////////////////////////////////////////////////////////////////////// var _exists = function(graphId) { - var gCol = _getGraphCollection(); + var gCol = getGraphCollection(); return gCol.exists(graphId); }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief Helper for dropping collections of a graph. +//////////////////////////////////////////////////////////////////////////////// + +var checkIfMayBeDropped = function(colName, graphName, graphs) { + var result = true; + graphs.forEach( + function(graph) { + if (graph._key === graphName) { + return; + } + var edgeDefinitions = graph.edgeDefinitions; + if (graph.edgeDefinitions) { + edgeDefinitions.forEach( + function(edgeDefinition) { + var from = edgeDefinition.from; + var to = edgeDefinition.to; + var collection = edgeDefinition.collection; + if (collection === colName || from.indexOf(colName) !== -1 || to.indexOf(colName) !== -1) { + result = false; + } + } + ); + } + } + ); + return result; +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief drop a graph. //////////////////////////////////////////////////////////////////////////////// var _drop = function(graphId, dropCollections) { - var gdb = _getGraphCollection(); + var gdb = getGraphCollection(); if (!gdb.exists(graphId)) { throw "Graph " + graphId + " does not exist."; @@ -494,16 +611,23 @@ var _drop = function(graphId, dropCollections) { function(edgeDefinition) { var from = edgeDefinition.from; var to = edgeDefinition.to; - var edge = edgeDefinition.collection; - db._drop(edge); + var collection = edgeDefinition.collection; + var graphs = getGraphCollection().toArray(); + if (checkIfMayBeDropped(collection, graph._key, graphs)) { + db._drop(collection); + } from.forEach( function(col) { - db._drop(col); + if (checkIfMayBeDropped(col, graph._key, graphs)) { + db._drop(col); + } } ); to.forEach( function(col) { - db._drop(col); + if (checkIfMayBeDropped(col, graph._key, graphs)) { + db._drop(col); + } } ); } @@ -669,7 +793,8 @@ Graph.prototype._getVertexCollectionByName = function(name) { exports._undirectedRelationDefinition = _undirectedRelationDefinition; exports._directedRelationDefinition = _directedRelationDefinition; exports._graph = _graph; -exports.edgeDefinitions = edgeDefinitions; +exports._edgeDefinitions = _edgeDefinitions; +exports._extendEdgeDefinitions = _extendEdgeDefinitions; exports._create = _create; exports._drop = _drop; exports._exists = _exists; diff --git a/js/common/tests/shell-document.js b/js/common/tests/shell-document.js index 81aa9f0652..93c57b49d0 100644 --- a/js/common/tests/shell-document.js +++ b/js/common/tests/shell-document.js @@ -855,6 +855,123 @@ function CollectionDocumentSuite () { assertEqual(a1._id, a10._id); assertNotEqual(a9._rev, a10._rev); + var doc10 = collection.document(a1._id); + assertEqual(a1._id, doc10._id); + assertEqual(a10._rev, doc10._rev); + assertEqual({"one": -1, "three": 3, "five": 5}, doc10.d); + assertEqual({"e1": 1, "e3": 3}, doc10.e); + assertEqual({"one": 1, "three": 3}, doc10.f); + }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief update a document +//////////////////////////////////////////////////////////////////////////////// + + testNewSignatureUpdateDocument : function () { + var a1 = collection.save({ a : 1}); + + assertTypeOf("string", a1._id); + assertTypeOf("string", a1._rev); + + var a2 = collection.update(a1, { a : 2 }); + + assertEqual(a1._id, a2._id); + assertNotEqual(a1._rev, a2._rev); + + try { + collection.update(a1, { a : 3 }); + fail(); + } + catch (err) { + assertEqual(ERRORS.ERROR_ARANGO_CONFLICT.code, err.errorNum); + } + + var doc2 = collection.document(a1._id); + + assertEqual(a1._id, doc2._id); + assertEqual(a2._rev, doc2._rev); + assertEqual(2, doc2.a); + + var a4 = collection.update(a1, { a : 4 }, {"overwrite": true}); + + assertEqual(a1._id, a4._id); + assertNotEqual(a1._rev, a4._rev); + assertNotEqual(a2._rev, a4._rev); + + var doc4 = collection.document(a1._id); + + assertEqual(a1._id, doc4._id); + assertEqual(a4._rev, doc4._rev); + assertEqual(4, doc4.a); + + var a5 = collection.update(a4, { b : 1, c : 2, d : "foo", e : null }); + assertEqual(a1._id, a5._id); + assertNotEqual(a4._rev, a5._rev); + + var doc5 = collection.document(a1._id); + assertEqual(a1._id, doc5._id); + assertEqual(a5._rev, doc5._rev); + assertEqual(4, doc5.a); + assertEqual(1, doc5.b); + assertEqual(2, doc5.c); + assertEqual("foo", doc5.d); + assertEqual(null, doc5.e); + + var a6 = collection.update(a5, { f : null, b : null, a : null, g : 2, c : 4 }); + assertEqual(a1._id, a6._id); + assertNotEqual(a5._rev, a6._rev); + + var doc6 = collection.document(a1._id); + assertEqual(a1._id, doc6._id); + assertEqual(a6._rev, doc6._rev); + assertEqual(null, doc6.a); + assertEqual(null, doc6.b); + assertEqual(4, doc6.c); + assertEqual("foo", doc6.d); + assertEqual(null, doc6.e); + assertEqual(null, doc6.f); + assertEqual(2, doc6.g); + + var a7 = collection.update(a6, { a : null, b : null, c : null, g : null }, {"overwrite": true, "keepNull": false}); + assertEqual(a1._id, a7._id); + assertNotEqual(a6._rev, a7._rev); + + var doc7 = collection.document(a1._id); + assertEqual(a1._id, doc7._id); + assertEqual(a7._rev, doc7._rev); + assertEqual(undefined, doc7.a); + assertEqual(undefined, doc7.b); + assertEqual(undefined, doc7.c); + assertEqual("foo", doc7.d); + assertEqual(null, doc7.e); + assertEqual(null, doc7.f); + assertEqual(undefined, doc7.g); + + var a8 = collection.update(a7, { d : { "one" : 1, "two" : 2, "three" : 3 }, e : { }, f : { "one" : 1 }} ); + assertEqual(a1._id, a8._id); + assertNotEqual(a7._rev, a8._rev); + + var doc8 = collection.document(a1._id); + assertEqual(a1._id, doc8._id); + assertEqual(a8._rev, doc8._rev); + assertEqual({"one": 1, "two": 2, "three": 3}, doc8.d); + assertEqual({}, doc8.e); + assertEqual({"one": 1}, doc8.f); + + var a9 = collection.update(a8, { d : { "four" : 4 }, "e" : { "e1" : [ 1, 2 ], "e2" : 2 }, "f" : { "three" : 3 }} ); + assertEqual(a1._id, a9._id); + assertNotEqual(a8._rev, a9._rev); + + var doc9 = collection.document(a1._id); + assertEqual(a1._id, doc9._id); + assertEqual(a9._rev, doc9._rev); + assertEqual({"one": 1, "two": 2, "three": 3, "four": 4}, doc9.d); + assertEqual({"e2": 2, "e1": [ 1, 2 ]}, doc9.e); + assertEqual({"one": 1, "three": 3}, doc9.f); + + var a10 = collection.update(a9, { d : { "one" : -1, "two": null, "four" : null, "five" : 5 }, "e" : { "e1" : 1, "e2" : null, "e3" : 3 }}, {"overwrite": true, "keepNull": false}); + assertEqual(a1._id, a10._id); + assertNotEqual(a9._rev, a10._rev); + var doc10 = collection.document(a1._id); assertEqual(a1._id, doc10._id); assertEqual(a10._rev, doc10._rev); @@ -1405,6 +1522,123 @@ function DatabaseDocumentSuite () { assertEqual(a1._id, a10._id); assertNotEqual(a9._rev, a10._rev); + var doc10 = db._document(a1._id); + assertEqual(a1._id, doc10._id); + assertEqual(a10._rev, doc10._rev); + assertEqual({"one": -1, "three": 3, "five": 5}, doc10.d); + assertEqual({"e1": 1, "e3": 3}, doc10.e); + assertEqual({"one": 1, "three": 3}, doc10.f); + }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests update function with new signature +//////////////////////////////////////////////////////////////////////////////// + + testNewsignatureOf_UpdateDocument : function () { + var a1 = collection.save({ a : 1}); + + assertTypeOf("string", a1._id); + assertTypeOf("string", a1._rev); + + var a2 = db._update(a1, { a : 2 }); + + assertEqual(a1._id, a2._id); + assertNotEqual(a1._rev, a2._rev); + + try { + db._update(a1, { a : 3 }); + fail(); + } + catch (err) { + assertEqual(ERRORS.ERROR_ARANGO_CONFLICT.code, err.errorNum); + } + + var doc2 = db._document(a1._id); + + assertEqual(a1._id, doc2._id); + assertEqual(a2._rev, doc2._rev); + assertEqual(2, doc2.a); + + var a4 = db._update(a1, { a : 4 }, {"overwrite": true}); + + assertEqual(a1._id, a4._id); + assertNotEqual(a1._rev, a4._rev); + assertNotEqual(a2._rev, a4._rev); + + var doc4 = db._document(a1._id); + + assertEqual(a1._id, doc4._id); + assertEqual(a4._rev, doc4._rev); + assertEqual(4, doc4.a); + + var a5 = db._update(a4, { b : 1, c : 2, d : "foo", e : null }); + assertEqual(a1._id, a5._id); + assertNotEqual(a4._rev, a5._rev); + + var doc5 = db._document(a1._id); + assertEqual(a1._id, doc5._id); + assertEqual(a5._rev, doc5._rev); + assertEqual(4, doc5.a); + assertEqual(1, doc5.b); + assertEqual(2, doc5.c); + assertEqual("foo", doc5.d); + assertEqual(null, doc5.e); + + var a6 = db._update(a5, { f : null, b : null, a : null, g : 2, c : 4 }); + assertEqual(a1._id, a6._id); + assertNotEqual(a5._rev, a6._rev); + + var doc6 = db._document(a1._id); + assertEqual(a1._id, doc6._id); + assertEqual(a6._rev, doc6._rev); + assertEqual(null, doc6.a); + assertEqual(null, doc6.b); + assertEqual(4, doc6.c); + assertEqual("foo", doc6.d); + assertEqual(null, doc6.e); + assertEqual(null, doc6.f); + assertEqual(2, doc6.g); + + var a7 = db._update(a6, { a : null, b : null, c : null, g : null }, {"overwrite": true, "keepNull": false}); + assertEqual(a1._id, a7._id); + assertNotEqual(a6._rev, a7._rev); + + var doc7 = db._document(a1._id); + assertEqual(a1._id, doc7._id); + assertEqual(a7._rev, doc7._rev); + assertEqual(undefined, doc7.a); + assertEqual(undefined, doc7.b); + assertEqual(undefined, doc7.c); + assertEqual("foo", doc7.d); + assertEqual(null, doc7.e); + assertEqual(null, doc7.f); + assertEqual(undefined, doc7.g); + + var a8 = db._update(a7, { d : { "one" : 1, "two" : 2, "three" : 3 }, e : { }, f : { "one" : 1 }} ); + assertEqual(a1._id, a8._id); + assertNotEqual(a7._rev, a8._rev); + + var doc8 = db._document(a1._id); + assertEqual(a1._id, doc8._id); + assertEqual(a8._rev, doc8._rev); + assertEqual({"one": 1, "two": 2, "three": 3}, doc8.d); + assertEqual({}, doc8.e); + assertEqual({"one": 1}, doc8.f); + + var a9 = db._update(a8, { d : { "four" : 4 }, "e" : { "e1" : [ 1, 2 ], "e2" : 2 }, "f" : { "three" : 3 }} ); + assertEqual(a1._id, a9._id); + assertNotEqual(a8._rev, a9._rev); + + var doc9 = db._document(a1._id); + assertEqual(a1._id, doc9._id); + assertEqual(a9._rev, doc9._rev); + assertEqual({"one": 1, "two": 2, "three": 3, "four": 4}, doc9.d); + assertEqual({"e2": 2, "e1": [ 1, 2 ]}, doc9.e); + assertEqual({"one": 1, "three": 3}, doc9.f); + + var a10 = db._update(a9, { d : { "one" : -1, "two": null, "four" : null, "five" : 5 }, "e" : { "e1" : 1, "e2" : null, "e3" : 3 }}, {"overwrite": true, "keepNull": false}); + assertEqual(a1._id, a10._id); + assertNotEqual(a9._rev, a10._rev); + var doc10 = db._document(a1._id); assertEqual(a1._id, doc10._id); assertEqual(a10._rev, doc10._rev); diff --git a/js/common/tests/shell-general-graph.js b/js/common/tests/shell-general-graph.js index 3fb5e6ce46..1a4d8b005c 100644 --- a/js/common/tests/shell-general-graph.js +++ b/js/common/tests/shell-general-graph.js @@ -1,4 +1,4 @@ -/*jslint indent: 2, nomen: true, maxlen: 80, sloppy: true */ +/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true */ /*global require, assertEqual, assertTrue, assertFalse, fail */ //////////////////////////////////////////////////////////////////////////////// @@ -46,6 +46,20 @@ var _ = require("underscore"); function GeneralGraphCreationSuite() { + var rn = "UnitTestRelationName"; + var rn1 = "UnitTestRelationName1"; + var vn1 = "UnitTestVerticies1"; + var vn2 = "UnitTestVerticies2"; + var vn3 = "UnitTestVerticies3"; + var vn4 = "UnitTestVerticies4"; + var gn = "UnitTestGraph"; + var edgeDef = graph._edgeDefinitions( + graph._undirectedRelationDefinition(rn, vn1), + graph._directedRelationDefinition(rn1, + [vn1, vn2], [vn3, vn4] + ) + ); + return { //////////////////////////////////////////////////////////////////////////////// @@ -53,215 +67,178 @@ function GeneralGraphCreationSuite() { //////////////////////////////////////////////////////////////////////////////// test_undirectedRelationDefinition : function () { - var r; - - try { - r = graph._undirectedRelationDefinition("relationName", ["vertexC1", "vertexC2"]); - } - catch (err) { - } + var r = graph._undirectedRelationDefinition(rn, [vn1, vn2]); assertEqual(r, { - collection: "relationName", - from: ["vertexC1", "vertexC2"], - to: ["vertexC1", "vertexC2"] + collection: rn, + from: [vn1, vn2], + to: [vn1, vn2] }); }, test_undirectedRelationDefinitionWithSingleCollection : function () { - var r; - - try { - r = graph._undirectedRelationDefinition("relationName", "vertexC1"); - } - catch (err) { - } + var r = graph._undirectedRelationDefinition(rn, vn1); assertEqual(r, { - collection: "relationName", - from: ["vertexC1"], - to: ["vertexC1"] + collection: rn, + from: [vn1], + to: [vn1] }); }, test_undirectedRelationDefinitionWithMissingName : function () { - var r, exception; try { - r = graph._undirectedRelationDefinition("", ["vertexC1", "vertexC2"]); + graph._undirectedRelationDefinition("", [vn1, vn2]); + fail(); } catch (err) { - exception = err; + assertEqual(err, " must be a not empty string"); } - - assertEqual(exception, " must be a not empty string"); - }, test_undirectedRelationDefinitionWithTooFewArgs : function () { - var r, exception; try { - r = graph._undirectedRelationDefinition(["vertexC1", "vertexC2"]); + graph._undirectedRelationDefinition([vn1, vn2]); + fail(); } catch (err) { - exception = err; + assertEqual(err, "method _undirectedRelationDefinition expects 2 arguments"); } - - assertEqual(exception, "method _undirectedRelationDefinition expects 2 arguments"); - }, test_undirectedRelationDefinitionWithInvalidSecondArg : function () { - var r, exception; try { - r = graph._undirectedRelationDefinition("name", {"vertexC1" : "vertexC2"}); + var param = {}; + param[vn1] = vn2; + graph._undirectedRelationDefinition(rn, param); + fail(); } catch (err) { - exception = err; + assertEqual(err, " must be a not empty string or array"); } - - assertEqual(exception, " must be a not empty string or array"); - }, test_directedRelationDefinition : function () { - var r; - - try { - r = graph._directedRelationDefinition("relationName", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"]); - } - catch (err) { - } + var r = graph._directedRelationDefinition(rn, + [vn1, vn2], [vn3, vn4]); assertEqual(r, { - collection: "relationName", - from: ["vertexC1", "vertexC2"], - to: ["vertexC3", "vertexC4"] + collection: rn, + from: [vn1, vn2], + to: [vn3, vn4] }); }, test_directedRelationDefinitionWithMissingName : function () { - var r, exception; try { - r = graph._directedRelationDefinition("", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"]); + graph._directedRelationDefinition("", + [vn1, vn2], [vn3, vn4]); + fail(); } catch (err) { - exception = err; + assertEqual(err, " must be a not empty string"); } - - assertEqual(exception, " must be a not empty string"); - }, test_directedRelationDefinitionWithTooFewArgs : function () { - var r, exception; try { - r = graph._directedRelationDefinition(["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"]); + graph._directedRelationDefinition([vn1, vn2], [vn3, vn4]); + fail(); } catch (err) { - exception = err; + assertEqual(err, "method _directedRelationDefinition expects 3 arguments"); } - - assertEqual(exception, "method _directedRelationDefinition expects 3 arguments"); - }, test_directedRelationDefinitionWithInvalidSecondArg : function () { - var r, exception; try { - r = graph._directedRelationDefinition("name", {"vertexC1" : "vertexC2"}, ""); + var param = {}; + param[vn1] = vn2; + graph._directedRelationDefinition(rn, param, vn3); + fail(); } catch (err) { - exception = err; + assertEqual(err, " must be a not empty string or array"); } - assertEqual(exception, " must be a not empty string or array"); }, test_directedRelationDefinitionWithInvalidThirdArg : function () { - var r, exception; try { - r = graph._directedRelationDefinition("name", ["vertexC1", "vertexC2"], []); + var param = {}; + param[vn1] = vn2; + graph._directedRelationDefinition(rn, vn3, param); + fail(); } catch (err) { - exception = err; + assertEqual(err, " must be a not empty string or array"); } - - assertEqual(exception, " must be a not empty string or array"); - }, testEdgeDefinitions : function () { - //with empty args - assertEqual(graph.edgeDefinitions(), []); + assertEqual(graph._edgeDefinitions(), []); //with args - assertEqual(graph.edgeDefinitions( - graph._undirectedRelationDefinition("relationName", "vertexC1"), - graph._directedRelationDefinition("relationName", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"]) + assertEqual(graph._edgeDefinitions( + graph._undirectedRelationDefinition(rn, vn1), + graph._directedRelationDefinition(rn1, + [vn1, vn2], [vn3, vn4]) ), [ { - collection: "relationName", - from: ["vertexC1"], - to: ["vertexC1"] + collection: rn, + from: [vn1], + to: [vn1] }, { - collection: "relationName", - from: ["vertexC1", "vertexC2"], - to: ["vertexC3", "vertexC4"] + collection: rn1, + from: [vn1, vn2], + to: [vn3, vn4] } ]); - }, - test_create : function () { - try { - arangodb.db._collection("_graphs").remove("_graphs/bla3") - } catch (err) { + if (db._collection("_graphs").exists(gn)) { + db._collection("_graphs").remove(gn); } var a = graph._create( - "bla3", - graph.edgeDefinitions( - graph._undirectedRelationDefinition("relationName", "vertexC1"), - graph._directedRelationDefinition("relationName2", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"] - ) + gn, + graph._edgeDefinitions( + graph._undirectedRelationDefinition(rn, vn1), + graph._directedRelationDefinition(rn1, [vn1, vn2], [vn3, vn4]) ) ); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC1')); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC2')); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC3')); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC4')); - assertTrue(a.__edgeCollections.hasOwnProperty('relationName')); - assertTrue(a.__edgeCollections.hasOwnProperty('relationName2')); + assertTrue(a.__vertexCollections.hasOwnProperty(vn1)); + assertTrue(a.__vertexCollections.hasOwnProperty(vn2)); + assertTrue(a.__vertexCollections.hasOwnProperty(vn3)); + assertTrue(a.__vertexCollections.hasOwnProperty(vn4)); + assertTrue(a.__edgeCollections.hasOwnProperty(rn)); + assertTrue(a.__edgeCollections.hasOwnProperty(rn1)); assertEqual(a.__edgeDefinitions, [ { - "collection" : "relationName", + "collection" : rn, "from" : [ - "vertexC1" + vn1 ], "to" : [ - "vertexC1" + vn1 ] }, { - "collection" : "relationName2", + "collection" : rn1, "from" : [ - "vertexC1", - "vertexC2" + vn1, + vn2 ], "to" : [ - "vertexC3", - "vertexC4" + vn3, + vn4 ] } ] @@ -269,126 +246,79 @@ function GeneralGraphCreationSuite() { }, test_create_WithOut_EdgeDefiniton : function () { - var msg; - try { - arangodb.db._collection("_graphs").remove("_graphs/bla3") - } catch (err) { + if (db._collection("_graphs").exists(gn)) { + db._collection("_graphs").remove(gn); } - try { - var a = graph._create( - "bla3", + graph._create( + gn, [] ); + fail(); } catch (err) { - msg = err; + assertEqual(err, "at least one edge definition is required to create a graph."); } - - assertEqual(msg, "at least one edge definition is required to create a graph."); - }, test_create_WithOut_Name : function () { - var msg; - try { - arangodb.db._collection("_graphs").remove("_graphs/bla3") - } catch (err) { + if (db._collection("_graphs").exists(gn)) { + db._collection("_graphs").remove(gn); } - try { - var a = graph._create( + graph._create( "", - graph.edgeDefinitions( - graph._undirectedRelationDefinition("relationName", "vertexC1"), - graph._directedRelationDefinition("relationName2", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"] - ) - ) + edgeDef ); + fail(); } catch (err) { - msg = err; + assertEqual(err, "a graph name is required to create a graph."); } - - assertEqual(msg, "a graph name is required to create a graph."); - }, test_create_With_Already_Existing_Graph : function () { - try { - arangodb.db._collection("_graphs").remove("_graphs/bla3") - } catch (err) { + if (db._collection("_graphs").exists(gn)) { + db._collection("_graphs").remove(gn); } - graph._create( - "bla3", - graph.edgeDefinitions( - graph._undirectedRelationDefinition("relationName", "vertexC1"), - graph._directedRelationDefinition("relationName2", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"] - ) - ) - ); - var msg; + graph._create(gn, edgeDef); try { - var a = graph._create( - "bla3", - graph.edgeDefinitions( - graph._undirectedRelationDefinition("relationName", "vertexC1"), - graph._directedRelationDefinition("relationName2", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"] - ) - ) - ); + graph._create(gn, edgeDef); } catch (err) { - msg = err; + assertEqual(err, "graph " + gn + " already exists."); } - - assertEqual(msg, "graph bla3 already exists."); - }, test_get_graph : function () { - - try { - arangodb.db._collection("_graphs").remove("_graphs/bla3") - } catch (err) { + if (db._collection("_graphs").exists(gn)) { + db._collection("_graphs").remove(gn); } - graph._create( - "bla3", - graph.edgeDefinitions( - graph._undirectedRelationDefinition("relationName", "vertexC1"), - graph._directedRelationDefinition("relationName2", - ["vertexC1", "vertexC2"], ["vertexC3", "vertexC4"] - ) - ) - ); + graph._create(gn, edgeDef); + var a = graph._graph(gn); - var a = graph._graph("bla3"); - - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC1')); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC2')); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC3')); - assertTrue(a.__vertexCollections.hasOwnProperty('vertexC4')); - assertTrue(a.__edgeCollections.hasOwnProperty('relationName')); - assertTrue(a.__edgeCollections.hasOwnProperty('relationName2')); + assertTrue(a.__vertexCollections.hasOwnProperty(vn1)); + assertTrue(a.__vertexCollections.hasOwnProperty(vn2)); + assertTrue(a.__vertexCollections.hasOwnProperty(vn3)); + assertTrue(a.__vertexCollections.hasOwnProperty(vn4)); + assertTrue(a.__edgeCollections.hasOwnProperty(rn)); + assertTrue(a.__edgeCollections.hasOwnProperty(rn1)); assertEqual(a.__edgeDefinitions, [ { - "collection" : "relationName", + "collection" : rn, "from" : [ - "vertexC1" + vn1 ], "to" : [ - "vertexC1" + vn1 ] }, { - "collection" : "relationName2", + "collection" : rn1, "from" : [ - "vertexC1", - "vertexC2" + vn1, + vn2 ], "to" : [ - "vertexC3", - "vertexC4" + vn3, + vn4 ] } ] @@ -396,13 +326,52 @@ function GeneralGraphCreationSuite() { }, test_get_graph_without_hit : function () { - var msg; try { - var a = graph._graph("bla4"); + graph._graph(gn + "UnknownExtension"); + fail(); } catch (e) { - msg = e; + assertEqual(e, "graph " + gn + "UnknownExtension" + " does not exists."); } - assertEqual(msg, "graph bla4 does not exists."); + }, + + test_creationOfGraphShouldNotAffectCollections: function() { + if(graph._exists(gn)) { + graph._drop(gn); + } + var edgeDef2 = [graph._directedRelationDefinition(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; + var v3 = g[vn1].save({_key: "3"})._id; + + g[rn].save(v1, v2, {}); + assertEqual(g[vn1].count(), 2); + assertEqual(g[vn2].count(), 1); + assertEqual(g[rn].count(), 1); + try { + g[rn].save(v2, v3, {}); + fail(); + } catch (e) { + // This should create an error + assertEqual(g[rn].count(), 1); + } + + try { + db[rn].save(v2, v3, {}); + } catch (e) { + // This should not create an error + fail(); + } + assertEqual(g[rn].count(), 2); + + db[vn2].remove(v2); + // This should not remove edges + assertEqual(g[rn].count(), 2); + + g[vn1].remove(v1); + // This should remove edges + assertEqual(g[rn].count(), 1); + graph._drop(gn, true); } }; @@ -679,7 +648,6 @@ function GeneralGraphAQLQueriesSuite() { test_filterOnOutEdges: function() { var query = g._outEdges(v1 + "/1").filter({val: true}); - // var query = g._outEdges("v1/1").filter("e.val = true"); assertEqual(query.printQuery(), "FOR edges_0 IN GRAPH_EDGES(" + '@graphName,@startVertex_0,"outbound") ' + 'FILTER MATCHES(edges_0,[{"val":true}])'); @@ -691,7 +659,96 @@ function GeneralGraphAQLQueriesSuite() { assertTrue(findIdInResult(result, e1), "Did not include e1"); assertFalse(findIdInResult(result, e2), "e2 is not excluded"); assertFalse(findIdInResult(result, e3), "e3 is not excluded"); - } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: counting of query results +//////////////////////////////////////////////////////////////////////////////// + test_queryCount: function() { + var query = g._edges(v1 + "/1"); + assertEqual(query.count(), 3); + query = g._inEdges(v1 + "/1").filter({val: true}); + assertEqual(query.count(), 0); + query = g._outEdges(v1 + "/1").filter({val: true}); + assertEqual(query.count(), 1); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: Cursor iteration +//////////////////////////////////////////////////////////////////////////////// + test_cursorIteration: function() { + var query = g._edges(v1 + "/1"); + var list = [e1, e2, e3]; + var next; + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 2); + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 1); + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 0); + assertFalse(query.hasNext()); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: Cursor recreation after iteration +//////////////////////////////////////////////////////////////////////////////// + test_cursorIterationAndRecreation: function() { + var query = g._edges(v1 + "/1"); + var list = [e1, e2, e3]; + var next; + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 2); + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 1); + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 0); + assertFalse(query.hasNext()); + query = query.filter({val: true}); + list = [e1]; + assertTrue(query.hasNext()); + next = query.next(); + list = _.without(list, next._id); + assertEqual(list.length, 0); + assertFalse(query.hasNext()); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: Is cursor recreated after counting of query results and appending filter +//////////////////////////////////////////////////////////////////////////////// + test_cursorRecreationAfterCount: function() { + var query = g._edges(v1 + "/1"); + assertEqual(query.count(), 3); + query = query.filter({val: true}); + assertEqual(query.count(), 1); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: Is cursor recreated after to array of query results and appending filter +//////////////////////////////////////////////////////////////////////////////// + test_cursorRecreationAfterToArray: function() { + var query = g._edges(v1 + "/1"); + var result = query.toArray(); + assertTrue(findIdInResult(result, e1), "Did not include e1"); + assertTrue(findIdInResult(result, e2), "Did not include e2"); + assertTrue(findIdInResult(result, e3), "Did not include e3"); + query = query.filter({val: true}); + result = query.toArray(); + assertTrue(findIdInResult(result, e1), "Did not include e1"); + assertFalse(findIdInResult(result, e2), "e2 is not excluded"); + assertFalse(findIdInResult(result, e3), "e3 is not excluded"); + } //////////////////////////////////////////////////////////////////////////////// /// @brief test: let construct on edges @@ -727,6 +784,7 @@ function EdgesAndVerticesSuite() { var vertexIds = []; var vertexId1, vertexId2; var edgeId1, edgeId2; + var unitTestGraphName = "unitTestGraph"; fillCollections = function() { var ids = {}; @@ -784,12 +842,12 @@ function EdgesAndVerticesSuite() { setUp : function() { try { - arangodb.db._collection("_graphs").remove("_graphs/unitTestGraph") + arangodb.db._collection("_graphs").remove("_graphs/" + unitTestGraphName) } catch (err) { } g = graph._create( - "unitTestGraph", - graph.edgeDefinitions( + unitTestGraphName, + graph._edgeDefinitions( graph._undirectedRelationDefinition("unitTestEdgeCollection1", "unitTestVertexCollection1"), graph._directedRelationDefinition("unitTestEdgeCollection2", ["unitTestVertexCollection1", "unitTestVertexCollection2"], ["unitTestVertexCollection3", "unitTestVertexCollection4"] @@ -799,12 +857,23 @@ function EdgesAndVerticesSuite() { }, tearDown : function() { - db.unitTestVertexCollection1.drop(); - db.unitTestVertexCollection2.drop(); - db.unitTestVertexCollection3.drop(); - db.unitTestVertexCollection4.drop(); - db.unitTestEdgeCollection1.drop(); - db.unitTestEdgeCollection2.drop(); + graph._drop(unitTestGraphName); + }, + + test_dropGraph : function () { + var myGraphName = unitTestGraphName + "2"; + var myEdgeColName = "unitTestEdgeCollection1"; + var myVertexColName = "unitTestVertexCollection4711"; + graph._create( + myGraphName, + graph._edgeDefinitions( + graph._undirectedRelationDefinition(myEdgeColName, myVertexColName) + ) + ); + graph._drop(myGraphName); + assertFalse(graph._exists(myGraphName)); + assertTrue(db._collection(myVertexColName) === null); + assertTrue(db._collection(myEdgeColName) !== null); }, test_edgeCollections : function () { @@ -853,8 +922,8 @@ function EdgesAndVerticesSuite() { test_vC_remove : function () { var vertex = g.unitTestVertexCollection1.save({first_name: "Tim"}); var vertexId = vertex._id; - var vertex = g.unitTestVertexCollection1.remove(vertexId); - assertTrue(vertex); + var result = g.unitTestVertexCollection1.remove(vertexId); + assertTrue(result); }, test_vC_removeWithEdge : function () { diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index b977499f72..d165cd0263 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -3971,13 +3971,11 @@ function DATE_MILLISECOND (value) { // --SECTION-- graph functions // ----------------------------------------------------------------------------- - //////////////////////////////////////////////////////////////////////////////// /// @brief find all paths through a graph, INTERNAL part called recursively //////////////////////////////////////////////////////////////////////////////// function GET_SUB_EDGES (edgeCollections, direction, vertexId) { - if (!Array.isArray(edgeCollections)) { edgeCollections = [edgeCollections]; } @@ -3994,11 +3992,10 @@ function GET_SUB_EDGES (edgeCollections, direction, vertexId) { result = result.concat(edgeCollection.edges(vertexId)); } }); + return result; - } - //////////////////////////////////////////////////////////////////////////////// /// @brief find all paths through a graph, INTERNAL part called recursively //////////////////////////////////////////////////////////////////////////////// @@ -4136,7 +4133,6 @@ function GRAPH_PATHS (vertices, edgeCollection, direction, followCycles, minLeng return result; } - //////////////////////////////////////////////////////////////////////////////// /// @brief find all paths through a graph //////////////////////////////////////////////////////////////////////////////// @@ -4150,7 +4146,6 @@ function GENERAL_GRAPH_PATHS (graphname, direction, followCycles, minLength, max minLength = minLength || 0; maxLength = maxLength !== undefined ? maxLength : 10; - // check graph exists and load edgeDefintions var graph = DOCUMENT_HANDLE("_graphs/" + graphname); if (!graph) { @@ -4232,8 +4227,6 @@ function GENERAL_GRAPH_PATHS (graphname, direction, followCycles, minLength, max return result; } - - //////////////////////////////////////////////////////////////////////////////// /// @brief visitor callback function for traversal //////////////////////////////////////////////////////////////////////////////// @@ -4329,7 +4322,6 @@ function TRAVERSAL_CHECK_EXAMPLES_TYPEWEIGHTS (examples, func) { }); } - //////////////////////////////////////////////////////////////////////////////// /// @brief tranform key to id //////////////////////////////////////////////////////////////////////////////// @@ -4337,17 +4329,17 @@ function TRAVERSAL_CHECK_EXAMPLES_TYPEWEIGHTS (examples, func) { function TO_ID (vertex, collection) { "use strict"; - if (vertex === 'object' && vertex.hasOwnProperty('_id')) { - return vertex._id; + if (typeof vertex === 'object' && vertex.hasOwnProperty('_id')) { + return vertex._id; } - if (vertex.indexOf('/') === -1 && collection) { - return collection + '/' + vertex; + if (typeof vertex === 'string' && vertex.indexOf('/') === -1 && collection) { + return collection + '/' + vertex; } + return vertex; } - //////////////////////////////////////////////////////////////////////////////// /// @brief traverse a graph //////////////////////////////////////////////////////////////////////////////// @@ -4456,19 +4448,15 @@ function TRAVERSAL_FUNC (func, return result; } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief shortest path algorithm -//////////////////////////////////////////////////////////////////////////////// - -function GRAPH_SHORTEST_PATH (vertexCollection, - edgeCollection, - startVertex, - endVertex, - direction, - params) { - "use strict"; +//////////////////////////////////////////////////////////////////////////////// +/// @brief helper function to determine parameters for SHORTEST_PATH and +/// GRAPH_SHORTEST_PATH +//////////////////////////////////////////////////////////////////////////////// + +function SHORTEST_PATH_PARAMS (params) { + "use strict"; + if (params === undefined) { params = { }; } @@ -4488,6 +4476,23 @@ function GRAPH_SHORTEST_PATH (vertexCollection, params.distance = undefined; } + return params; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shortest path algorithm +//////////////////////////////////////////////////////////////////////////////// + +function GRAPH_SHORTEST_PATH (vertexCollection, + edgeCollection, + startVertex, + endVertex, + direction, + params) { + "use strict"; + + params = SHORTEST_PATH_PARAMS(params); + return TRAVERSAL_FUNC("SHORTEST_PATH", TRAVERSAL.collectionDatasourceFactory(COLLECTION(edgeCollection)), TO_ID(startVertex, vertexCollection), @@ -4501,32 +4506,15 @@ function GRAPH_SHORTEST_PATH (vertexCollection, //////////////////////////////////////////////////////////////////////////////// function GENERAL_GRAPH_SHORTEST_PATH (graphName, - startVertex, - endVertex, - direction, - params) { + startVertex, + endVertex, + direction, + params) { "use strict"; - if (params === undefined) { - params = { }; - } + params = SHORTEST_PATH_PARAMS(params); - params.strategy = "dijkstra"; - params.itemorder = "forward"; - params.visitor = TRAVERSAL_VISITOR; - - if (typeof params.distance === "string") { - var name = params.distance.toUpperCase(); - - params.distance = function (config, vertex1, vertex2, edge) { - return FCALL_USER(name, [ config, vertex1, vertex2, edge ]); - }; - } - else { - params.distance = undefined; - } - - return TRAVERSAL_FUNC("SHORTEST_PATH", + return TRAVERSAL_FUNC("GRAPH_SHORTEST_PATH", TRAVERSAL.generalGraphDatasourceFactory(graphName), TO_ID(startVertex), TO_ID(endVertex), @@ -4534,6 +4522,21 @@ function GENERAL_GRAPH_SHORTEST_PATH (graphName, params); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief helper function to determine parameters for TRAVERSAL and +/// GRAPH_TRAVERSAL +//////////////////////////////////////////////////////////////////////////////// + +function TRAVERSAL_PARAMS (params) { + "use strict"; + + if (params === undefined) { + params = { }; + } + + params.visitor = TRAVERSAL_VISITOR; + return params; +} //////////////////////////////////////////////////////////////////////////////// /// @brief traverse a graph @@ -4545,12 +4548,8 @@ function GRAPH_TRAVERSAL (vertexCollection, direction, params) { "use strict"; - - if (params === undefined) { - params = { }; - } - params.visitor = TRAVERSAL_VISITOR; + params = TRAVERSAL_PARAMS(params); return TRAVERSAL_FUNC("TRAVERSAL", TRAVERSAL.collectionDatasourceFactory(COLLECTION(edgeCollection)), @@ -4560,7 +4559,6 @@ function GRAPH_TRAVERSAL (vertexCollection, params); } - //////////////////////////////////////////////////////////////////////////////// /// @brief traverse a graph //////////////////////////////////////////////////////////////////////////////// @@ -4571,13 +4569,9 @@ function GENERAL_GRAPH_TRAVERSAL (graphName, params) { "use strict"; - if (params === undefined) { - params = { }; - } + params = TRAVERSAL_PARAMS(params); - params.visitor = TRAVERSAL_VISITOR; - - return TRAVERSAL_FUNC("TRAVERSAL", + return TRAVERSAL_FUNC("GRAPH_TRAVERSAL", TRAVERSAL.generalGraphDatasourceFactory(graphName), TO_ID(startVertex), undefined, @@ -4585,6 +4579,27 @@ function GENERAL_GRAPH_TRAVERSAL (graphName, params); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief helper function to determine parameters for TRAVERSAL_TREE and +/// GRAPH_TRAVERSAL_TREE +//////////////////////////////////////////////////////////////////////////////// + +function TRAVERSAL_TREE_PARAMS (params, connectName, funcName) { + "use strict"; + + if (params === undefined) { + params = { }; + } + + params.visitor = TRAVERSAL_TREE_VISITOR; + params.connect = connectName; + + if (params.connect === "") { + THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, funcName); + } + + return params; +} //////////////////////////////////////////////////////////////////////////////// /// @brief traverse a graph and create a hierarchical result @@ -4600,16 +4615,7 @@ function GRAPH_TRAVERSAL_TREE (vertexCollection, params) { "use strict"; - if (connectName === "") { - THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "TRAVERSAL_TREE"); - } - - if (params === undefined) { - params = { }; - } - - params.visitor = TRAVERSAL_TREE_VISITOR; - params.connect = connectName; + params = TRAVERSAL_TREE_PARAMS(params, connectName, "TRAVERSAL_TREE"); var result = TRAVERSAL_FUNC("TRAVERSAL_TREE", TRAVERSAL.collectionDatasourceFactory(COLLECTION(edgeCollection)), @@ -4631,24 +4637,15 @@ function GRAPH_TRAVERSAL_TREE (vertexCollection, //////////////////////////////////////////////////////////////////////////////// function GENERAL_GRAPH_TRAVERSAL_TREE (graphName, - startVertex, - direction, - connectName, - params) { + startVertex, + direction, + connectName, + params) { "use strict"; - if (connectName === "") { - THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "TRAVERSAL_TREE"); - } + params = TRAVERSAL_TREE_PARAMS(params, connectName, "GRAPH_TRAVERSAL_TREE"); - if (params === undefined) { - params = { }; - } - - params.visitor = TRAVERSAL_TREE_VISITOR; - params.connect = connectName; - - var result = TRAVERSAL_FUNC("TRAVERSAL_TREE", + var result = TRAVERSAL_FUNC("GRAPH_TRAVERSAL_TREE", TRAVERSAL.generalGraphDatasourceFactory(graphName), TO_ID(startVertex), undefined, @@ -4661,7 +4658,6 @@ function GENERAL_GRAPH_TRAVERSAL_TREE (graphName, return [ result[0][params.connect] ]; } - //////////////////////////////////////////////////////////////////////////////// /// @brief return connected edges //////////////////////////////////////////////////////////////////////////////// @@ -4772,73 +4768,12 @@ function GENERAL_GRAPH_EDGES ( } //////////////////////////////////////////////////////////////////////////////// -/// @brief return connected neighbors +/// @brief helper function to filter edges based on examples //////////////////////////////////////////////////////////////////////////////// -function GRAPH_NEIGHBORS (vertexCollection, - edgeCollection, - vertex, - direction, - examples) { +function FILTERED_EDGES (edges, vertex, direction, examples) { "use strict"; - var c = COLLECTION(vertexCollection); - - if (typeof vertex === 'object' && vertex.hasOwnProperty('_id')) { - vertex = vertex._id; - } - - if (vertex.indexOf('/') === -1) { - vertex = vertexCollection + '/' + vertex; - } - - var edges = GRAPH_EDGES(edgeCollection, vertex, direction); - var result = [ ]; - - FILTER(edges, examples).forEach (function (e) { - var key; - - if (direction === "inbound") { - key = e._from; - } - else if (direction === "outbound") { - key = e._to; - } - else if (direction === "any") { - key = e._from; - if (key === vertex) { - key = e._to; - } - } - - if (key === vertex) { - // do not return the start vertex itself - return; - } - - try { - result.push({ edge: CLONE(e), vertex: c.document(key) }); - } - catch (err) { - } - }); - - return result; -} - - -//////////////////////////////////////////////////////////////////////////////// -/// @brief return connected neighbors -//////////////////////////////////////////////////////////////////////////////// - -function GENERAL_GRAPH_NEIGHBORS (graphName, - vertex, - direction, - examples) { - "use strict"; - - - var edges = GENERAL_GRAPH_EDGES(graphName, TO_ID(vertex), direction); var result = [ ]; FILTER(edges, examples).forEach (function (e) { @@ -4872,6 +4807,36 @@ function GENERAL_GRAPH_NEIGHBORS (graphName, return result; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief return connected neighbors +//////////////////////////////////////////////////////////////////////////////// + +function GRAPH_NEIGHBORS (vertexCollection, + edgeCollection, + vertex, + direction, + examples) { + "use strict"; + + vertex = TO_ID(vertex, vertexCollection); + var edges = GRAPH_EDGES(edgeCollection, vertex, direction); + return FILTERED_EDGES(edges, vertex, direction, examples); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return connected neighbors +//////////////////////////////////////////////////////////////////////////////// + +function GENERAL_GRAPH_NEIGHBORS (graphName, + vertex, + direction, + examples) { + "use strict"; + + vertex = TO_ID(vertex); + var edges = GENERAL_GRAPH_EDGES(graphName, vertex, direction); + return FILTERED_EDGES(edges, vertex, direction, examples); +} // ----------------------------------------------------------------------------- // --SECTION-- MODULE EXPORTS