From 108a2cf6190d2ffa8dd13895f9b44e5670f80119 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 26 Jun 2014 15:23:10 +0200 Subject: [PATCH 01/11] clean up deleted collections without leaks --- arangod/VocBase/barrier.h | 2 +- arangod/VocBase/cleanup.cpp | 35 ++++++++++++++++++++------------- arangod/Wal/CollectorThread.cpp | 9 +-------- arangod/Wal/CollectorThread.h | 2 ++ 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/arangod/VocBase/barrier.h b/arangod/VocBase/barrier.h index 253603f381..215e0dccf2 100644 --- a/arangod/VocBase/barrier.h +++ b/arangod/VocBase/barrier.h @@ -51,7 +51,7 @@ struct TRI_datafile_s; //////////////////////////////////////////////////////////////////////////////// typedef enum { - TRI_BARRIER_ELEMENT, + TRI_BARRIER_ELEMENT = 1, TRI_BARRIER_DATAFILE_DROP_CALLBACK, TRI_BARRIER_DATAFILE_RENAME_CALLBACK, TRI_BARRIER_COLLECTION_UNLOAD_CALLBACK, diff --git a/arangod/VocBase/cleanup.cpp b/arangod/VocBase/cleanup.cpp index 61ee5f20a2..5281b3b432 100644 --- a/arangod/VocBase/cleanup.cpp +++ b/arangod/VocBase/cleanup.cpp @@ -67,7 +67,8 @@ static int const CLEANUP_INDEX_ITERATIONS = 5; /// @brief checks all datafiles of a collection //////////////////////////////////////////////////////////////////////////////// -static void CleanupDocumentCollection (TRI_document_collection_t* document) { +static void CleanupDocumentCollection (TRI_vocbase_col_t* collection, + TRI_document_collection_t* document) { bool unloadChecked = false; // loop until done @@ -121,10 +122,21 @@ static void CleanupDocumentCollection (TRI_document_collection_t* document) { // we must release the lock temporarily to check if the collection is fully collected TRI_UnlockSpin(&container->_lock); + bool isDeleted = false; + // must not hold the spin lock while querying the collection if (! TRI_IsFullyCollectedDocumentCollection(document)) { - // collection is not fully collected - postpone the unload - return; + // if there is still some collection to perform, check if the collection was deleted already + if (TRI_TRY_READ_LOCK_STATUS_VOCBASE_COL(collection)) { + isDeleted = (collection->_status == TRI_VOC_COL_STATUS_DELETED); + TRI_READ_UNLOCK_STATUS_VOCBASE_COL(collection); + } + + if (! isDeleted) { + // collection is not fully collected and still undeleted - postpone the unload + return; + } + // if deleted, then we may unload / delete } unloadChecked = true; @@ -248,26 +260,21 @@ void TRI_CleanupVocBase (void* data) { // check if we can get the compactor lock exclusively if (TRI_CheckAndLockCompactorVocBase(vocbase)) { - size_t i, n; - // copy all collections TRI_READ_LOCK_COLLECTIONS_VOCBASE(vocbase); TRI_CopyDataVectorPointer(&collections, &vocbase->_collections); TRI_READ_UNLOCK_COLLECTIONS_VOCBASE(vocbase); - n = collections._length; + size_t const n = collections._length; - for (i = 0; i < n; ++i) { - TRI_vocbase_col_t* collection; - TRI_document_collection_t* document; - - collection = (TRI_vocbase_col_t*) collections._buffer[i]; + for (size_t i = 0; i < n; ++i) { + TRI_vocbase_col_t* collection = static_cast(collections._buffer[i]); TRI_READ_LOCK_STATUS_VOCBASE_COL(collection); - document = collection->_collection; + TRI_document_collection_t* document = collection->_collection; - if (document == NULL) { + if (document == nullptr) { TRI_READ_UNLOCK_STATUS_VOCBASE_COL(collection); continue; } @@ -283,7 +290,7 @@ void TRI_CleanupVocBase (void* data) { document->cleanupIndexes(document); } - CleanupDocumentCollection(document); + CleanupDocumentCollection(collection, document); } TRI_UnlockCompactorVocBase(vocbase); diff --git a/arangod/Wal/CollectorThread.cpp b/arangod/Wal/CollectorThread.cpp index df8bdc53a8..c0ac3faacc 100644 --- a/arangod/Wal/CollectorThread.cpp +++ b/arangod/Wal/CollectorThread.cpp @@ -478,7 +478,6 @@ bool CollectorThread::processQueuedOperations () { _numPendingOperations -= numOperations; - // delete the object delete (*it2); @@ -652,17 +651,12 @@ int CollectorThread::processCollectionOperations (CollectorCache* cache) { // finally update all datafile statistics LOG_TRACE("updating datafile statistics for collection '%s'", document->_info._name); updateDatafileStatistics(document, cache); - - // TODO: the following assertion is only true in a running system - // if we just started the server, we don't know how many uncollected operations we have!! - // TRI_ASSERT(document->_uncollectedLogfileEntries >= cache->totalOperationsCount); + document->_uncollectedLogfileEntries -= cache->totalOperationsCount; if (document->_uncollectedLogfileEntries < 0) { document->_uncollectedLogfileEntries = 0; } - cache->freeBarriers(); - res = TRI_ERROR_NO_ERROR; } catch (triagens::arango::Exception const& ex) { @@ -866,7 +860,6 @@ int CollectorThread::transferMarkers (Logfile* logfile, if (cache != nullptr) { // prevent memleak - cache->freeBarriers(); delete cache; } diff --git a/arangod/Wal/CollectorThread.h b/arangod/Wal/CollectorThread.h index 837944b095..9942788091 100644 --- a/arangod/Wal/CollectorThread.h +++ b/arangod/Wal/CollectorThread.h @@ -106,6 +106,7 @@ namespace triagens { if (operations != nullptr) { delete operations; } + freeBarriers(); } //////////////////////////////////////////////////////////////////////////////// @@ -125,6 +126,7 @@ namespace triagens { for (auto it = barriers.begin(); it != barriers.end(); ++it) { TRI_FreeBarrier((*it)); } + barriers.clear(); } From f93897de3943254f3f70e46982d37e8f3e8de6f6 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 26 Jun 2014 15:28:40 +0200 Subject: [PATCH 02/11] changed filename --- UnitTests/Makefile.unittests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 4e964426b9..1bbbf9e373 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -566,7 +566,7 @@ unittests-shell-server-ahuacatl: ### @brief SHELL CLIENT TESTS ################################################################################ -UNITTESTS_READONLY = $(addprefix --javascript.unit-tests ,@top_srcdir@/js/client/tests/shell-changeMode.js) +UNITTESTS_READONLY = $(addprefix --javascript.unit-tests ,@top_srcdir@/js/client/tests/shell-changeMode-noncluster.js) UNITTESTS_NO_READONLY = $(addprefix --javascript.unit-tests ,@top_srcdir@/js/client/tests/shell-noChangeMode.js) .PHONY: unittests-shell-client-readonly From 4569420fda6928743149eb08081c059888b3b344 Mon Sep 17 00:00:00 2001 From: Esteban Lombeyda Date: Thu, 26 Jun 2014 15:42:17 +0200 Subject: [PATCH 03/11] all file for tesing the changeMode werde renamed --- UnitTests/Makefile.unittests | 2 +- .../{shell-noChangeMode.js => shell-noChangeMode-noncluster.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename js/client/tests/{shell-noChangeMode.js => shell-noChangeMode-noncluster.js} (100%) diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 1bbbf9e373..363a4797cd 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -567,7 +567,7 @@ unittests-shell-server-ahuacatl: ################################################################################ UNITTESTS_READONLY = $(addprefix --javascript.unit-tests ,@top_srcdir@/js/client/tests/shell-changeMode-noncluster.js) -UNITTESTS_NO_READONLY = $(addprefix --javascript.unit-tests ,@top_srcdir@/js/client/tests/shell-noChangeMode.js) +UNITTESTS_NO_READONLY = $(addprefix --javascript.unit-tests ,@top_srcdir@/js/client/tests/shell-noChangeMode-noncluster.js) .PHONY: unittests-shell-client-readonly unittests-shell-client-readonly: diff --git a/js/client/tests/shell-noChangeMode.js b/js/client/tests/shell-noChangeMode-noncluster.js similarity index 100% rename from js/client/tests/shell-noChangeMode.js rename to js/client/tests/shell-noChangeMode-noncluster.js From 9ca37522a79cfb1ad6749d539702d32c043e446b Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 26 Jun 2014 15:44:08 +0200 Subject: [PATCH 04/11] fixed undefined behavior in task shutdown --- arangod/V8Server/V8Job.cpp | 27 +++++++++++++++++++++------ arangod/V8Server/V8Job.h | 10 ++++++++-- arangod/V8Server/V8TimerTask.cpp | 4 ++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/arangod/V8Server/V8Job.cpp b/arangod/V8Server/V8Job.cpp index 0aaab1b38d..cd408f87c0 100644 --- a/arangod/V8Server/V8Job.cpp +++ b/arangod/V8Server/V8Job.cpp @@ -56,8 +56,24 @@ V8Job::V8Job (TRI_vocbase_t* vocbase, _vocbase(vocbase), _v8Dealer(v8Dealer), _command(command), - _parameters(parameters), + _parameters(nullptr), _canceled(0) { + + if (parameters != nullptr) { + // create our own copy of the parameters + _parameters = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, parameters); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a V8 job +//////////////////////////////////////////////////////////////////////////////// + +V8Job::~V8Job () { + if (_parameters != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _parameters); + _parameters = nullptr; + } } // ----------------------------------------------------------------------------- @@ -76,7 +92,7 @@ Job::JobType V8Job::type () { /// {@inheritDoc} //////////////////////////////////////////////////////////////////////////////// -const string& V8Job::queue () { +string const& V8Job::queue () { static const string queue = "STANDARD"; return queue; } @@ -90,11 +106,10 @@ Job::status_t V8Job::work () { return status_t(JOB_DONE); } - ApplicationV8::V8Context* context - = _v8Dealer->enterContext(_vocbase, 0, true, false); + ApplicationV8::V8Context* context = _v8Dealer->enterContext(_vocbase, nullptr, true, false); // note: the context might be 0 in case of shut-down - if (context == 0) { + if (context == nullptr) { return status_t(JOB_DONE); } @@ -119,7 +134,7 @@ Job::status_t V8Job::work () { } v8::Handle fArgs; - if (_parameters != 0) { + if (_parameters != nullptr) { fArgs = TRI_ObjectJson(_parameters); } else { diff --git a/arangod/V8Server/V8Job.h b/arangod/V8Server/V8Job.h index 15a991bcc3..ac7cf26333 100644 --- a/arangod/V8Server/V8Job.h +++ b/arangod/V8Server/V8Job.h @@ -64,6 +64,12 @@ namespace triagens { std::string const&, TRI_json_t const*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a V8 job +//////////////////////////////////////////////////////////////////////////////// + + ~V8Job (); + // ----------------------------------------------------------------------------- // --SECTION-- Job methods // ----------------------------------------------------------------------------- @@ -80,7 +86,7 @@ namespace triagens { /// {@inheritDoc} //////////////////////////////////////////////////////////////////////////////// - const std::string& queue (); + std::string const& queue (); //////////////////////////////////////////////////////////////////////////////// /// {@inheritDoc} @@ -140,7 +146,7 @@ namespace triagens { /// @brief paramaters //////////////////////////////////////////////////////////////////////////////// - TRI_json_t const* _parameters; + TRI_json_t* _parameters; //////////////////////////////////////////////////////////////////////////////// /// @brief cancel flag diff --git a/arangod/V8Server/V8TimerTask.cpp b/arangod/V8Server/V8TimerTask.cpp index ffd8f6c43c..d1e3481702 100644 --- a/arangod/V8Server/V8TimerTask.cpp +++ b/arangod/V8Server/V8TimerTask.cpp @@ -65,7 +65,7 @@ V8TimerTask::V8TimerTask (string const& id, _parameters(parameters), _created(TRI_microtime()) { - TRI_ASSERT(vocbase != 0); + TRI_ASSERT(vocbase != nullptr); // increase reference counter for the database used TRI_UseVocBase(_vocbase); @@ -79,7 +79,7 @@ V8TimerTask::~V8TimerTask () { // decrease reference counter for the database used TRI_ReleaseVocBase(_vocbase); - if (_parameters != 0) { + if (_parameters != nullptr) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _parameters); } } From d827eaea2d520785f5adc03a717b7ecc195b5a04 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 26 Jun 2014 15:44:34 +0200 Subject: [PATCH 05/11] simplified --- lib/BasicsC/json.c | 3 ++- lib/BasicsC/json.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/BasicsC/json.c b/lib/BasicsC/json.c index faeced6213..c12daa3f32 100644 --- a/lib/BasicsC/json.c +++ b/lib/BasicsC/json.c @@ -1186,7 +1186,8 @@ int TRI_CopyToJson (TRI_memory_zone_t* zone, /// @brief copies a json object //////////////////////////////////////////////////////////////////////////////// -TRI_json_t* TRI_CopyJson (TRI_memory_zone_t* zone, const TRI_json_t* const src) { +TRI_json_t* TRI_CopyJson (TRI_memory_zone_t* zone, + TRI_json_t const* src) { TRI_json_t* dst; int res; diff --git a/lib/BasicsC/json.h b/lib/BasicsC/json.h index 9953ca28bb..b455f94433 100644 --- a/lib/BasicsC/json.h +++ b/lib/BasicsC/json.h @@ -382,7 +382,8 @@ int TRI_CopyToJson (TRI_memory_zone_t*, TRI_json_t* dst, TRI_json_t const* src); /// @brief copies a json object //////////////////////////////////////////////////////////////////////////////// -TRI_json_t* TRI_CopyJson (TRI_memory_zone_t*, const TRI_json_t* const); +TRI_json_t* TRI_CopyJson (TRI_memory_zone_t*, + TRI_json_t const*); //////////////////////////////////////////////////////////////////////////////// /// @brief parses a json string From 0b68523c75fddbabb4ef0f7a062ced8c68a30670 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 26 Jun 2014 15:45:31 +0200 Subject: [PATCH 06/11] allow creation of secondary indexes in recovery --- arangod/VocBase/document-collection.cpp | 15 ++++----------- arangod/Wal/LogfileManager.cpp | 4 ++++ arangod/Wal/LogfileManager.h | 8 -------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/arangod/VocBase/document-collection.cpp b/arangod/VocBase/document-collection.cpp index e592adc69e..a5b3fdec65 100644 --- a/arangod/VocBase/document-collection.cpp +++ b/arangod/VocBase/document-collection.cpp @@ -1688,8 +1688,6 @@ static bool OpenIterator (TRI_df_marker_t const* marker, //////////////////////////////////////////////////////////////////////////////// static int FillInternalIndexes (TRI_document_collection_t* document) { - TRI_ASSERT(! triagens::wal::LogfileManager::instance()->isInRecovery()); - int res = TRI_ERROR_NO_ERROR; for (size_t i = 0; i < document->_allIndexes._length; ++i) { @@ -2697,16 +2695,11 @@ TRI_document_collection_t* TRI_OpenDocumentCollection (TRI_vocbase_t* vocbase, TRI_InitVocShaper(document->getShaper()); // ONLY in OPENCOLLECTION, PROTECTED by fake trx here - // secondary indexes must not be loaded during recovery - // this is because creating indexes might write attribute markers into the WAL, - // but the WAL is read-only at the point of recovery - if (! triagens::wal::LogfileManager::instance()->isInRecovery()) { - // fill internal indexes (this is, the edges index at the moment) - FillInternalIndexes(document); + // fill internal indexes (this is, the edges index at the moment) + FillInternalIndexes(document); - // fill user-defined secondary indexes - TRI_IterateIndexCollection(collection, OpenIndexIterator, collection); - } + // fill user-defined secondary indexes + TRI_IterateIndexCollection(collection, OpenIndexIterator, collection); return document; } diff --git a/arangod/Wal/LogfileManager.cpp b/arangod/Wal/LogfileManager.cpp index 9fed279637..735752a6a2 100644 --- a/arangod/Wal/LogfileManager.cpp +++ b/arangod/Wal/LogfileManager.cpp @@ -730,6 +730,10 @@ SlotInfo LogfileManager::allocate (void const* src, uint32_t size) { if (! _allowWrites) { // no writes allowed +#ifdef TRI_ENABLE_MAINTAINER_MODE + TRI_ASSERT(false); +#endif + return SlotInfo(TRI_ERROR_ARANGO_READ_ONLY); } diff --git a/arangod/Wal/LogfileManager.h b/arangod/Wal/LogfileManager.h index ecbc0a938d..a3076af0cc 100644 --- a/arangod/Wal/LogfileManager.h +++ b/arangod/Wal/LogfileManager.h @@ -358,14 +358,6 @@ namespace triagens { _throttleWhenPending = value; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief whether or not we are in the recovery mode -//////////////////////////////////////////////////////////////////////////////// - - inline bool isInRecovery () const { - return _inRecovery; - } - //////////////////////////////////////////////////////////////////////////////// /// @brief registers a transaction //////////////////////////////////////////////////////////////////////////////// From 357be2cbbe000a90c1b6a94ceaef7437db7fcfea Mon Sep 17 00:00:00 2001 From: scottashton Date: Thu, 26 Jun 2014 15:51:11 +0200 Subject: [PATCH 07/11] adjusted tests for new graph management --- .../js/templates/graphManagementView.ejs | 1 - .../frontend/js/views/graphManagementView.js | 4 ++-- js/apps/system/aardvark/test/karma/files.json | 1 + .../specs/collections/graphCollectionSpec.js | 6 +++--- .../aardvark/test/specs/models/graphSpec.js | 11 +++++------ .../aardvark/test/specs/router/routerSpec.js | 9 +++++---- .../specs/views/graphManagementViewSpec.js | 18 +++++++++--------- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/templates/graphManagementView.ejs b/js/apps/system/aardvark/frontend/js/templates/graphManagementView.ejs index 290e55ba40..c9ac732c89 100644 --- a/js/apps/system/aardvark/frontend/js/templates/graphManagementView.ejs +++ b/js/apps/system/aardvark/frontend/js/templates/graphManagementView.ejs @@ -67,7 +67,6 @@
-
diff --git a/js/apps/system/aardvark/frontend/js/views/graphManagementView.js b/js/apps/system/aardvark/frontend/js/views/graphManagementView.js index ddb0b1df98..6eb6408af0 100644 --- a/js/apps/system/aardvark/frontend/js/views/graphManagementView.js +++ b/js/apps/system/aardvark/frontend/js/views/graphManagementView.js @@ -43,7 +43,7 @@ addNewGraph: function(e) { e.preventDefault(); - this.createNewGraphModal2(); + this.createNewGraphModal(); }, deleteGraph: function(e) { @@ -442,7 +442,7 @@ return edgeDefinitionMap; }, - createNewGraphModal2: function() { + createNewGraphModal: function() { var buttons = [], collList = [], eCollList = [], tableContent = [], collections = this.options.collectionCollection.models; diff --git a/js/apps/system/aardvark/test/karma/files.json b/js/apps/system/aardvark/test/karma/files.json index 95868132e6..1bc5ae1fad 100755 --- a/js/apps/system/aardvark/test/karma/files.json +++ b/js/apps/system/aardvark/test/karma/files.json @@ -261,6 +261,7 @@ "test/specs/views/documentsViewSpec.js", "test/specs/views/documentViewSpec.js", "test/specs/views/dashboardViewSpec.js", + "test/specs/views/graphManagementViewSpec.js", "test/specs/views/newLogsViewSpec.js", "test/specs/views/notificationViewSpec.js", "test/specs/views/statisticBarViewSpec.js", diff --git a/js/apps/system/aardvark/test/specs/collections/graphCollectionSpec.js b/js/apps/system/aardvark/test/specs/collections/graphCollectionSpec.js index 72785c7e9b..c97b465f8e 100644 --- a/js/apps/system/aardvark/test/specs/collections/graphCollectionSpec.js +++ b/js/apps/system/aardvark/test/specs/collections/graphCollectionSpec.js @@ -13,13 +13,13 @@ col = new window.GraphCollection(); }); - /* it("parse", function () { + it("parse", function () { expect(col.model).toEqual(window.Graph); expect(col.comparator).toEqual("_key"); - expect(col.url).toEqual("/_api/graph"); + expect(col.url).toEqual("/_api/gharial"); expect(col.parse({error: false, graphs: "blub"})).toEqual("blub"); expect(col.parse({error: true, graphs: "blub"})).toEqual(undefined); - });*/ + }); }); }()); diff --git a/js/apps/system/aardvark/test/specs/models/graphSpec.js b/js/apps/system/aardvark/test/specs/models/graphSpec.js index 3624d4b858..3ec8dff1d8 100644 --- a/js/apps/system/aardvark/test/specs/models/graphSpec.js +++ b/js/apps/system/aardvark/test/specs/models/graphSpec.js @@ -38,15 +38,14 @@ }); }); - /*it("should request /_api/graph on save", function() { + it("should request /_api/graph on save", function() { ajaxVerify = function(opt) { - expect(opt.url).toEqual("/_api/graph"); + expect(opt.url).toEqual("/_api/gharial"); expect(opt.type).toEqual("POST"); }; model.save(); expect($.ajax).toHaveBeenCalled(); }); -*/ it("should store the attributes in the model", function() { var id = "_graph/" + myKey, rev = "12345"; @@ -81,15 +80,15 @@ expect(model.get("graph")).toBeUndefined(); }); - /* it("should request /_api/graph/_key on delete", function() { + it("should request /_api/graph/_key on delete", function() { model.save(); ajaxVerify = function(opt) { - expect(opt.url).toEqual("/_api/graph/" + myKey); + expect(opt.url).toEqual("/_api/gharial/" + myKey); expect(opt.type).toEqual("DELETE"); }; model.destroy(); expect($.ajax).toHaveBeenCalled(); - });*/ + }); diff --git a/js/apps/system/aardvark/test/specs/router/routerSpec.js b/js/apps/system/aardvark/test/specs/router/routerSpec.js index a69b21eb2a..5957eb0f3f 100644 --- a/js/apps/system/aardvark/test/specs/router/routerSpec.js +++ b/js/apps/system/aardvark/test/specs/router/routerSpec.js @@ -500,7 +500,7 @@ ); }); - /*it("should navigate to the graphView", function () { + /* it("should navigate to the graphView", function () { spyOn(graphDummy, "render"); simpleNavigationCheck( "graph", @@ -654,14 +654,15 @@ ); }); - /*it("should route to the graph management tab", function () { + it("should route to the graph management tab", function () { simpleNavigationCheck( "graphManagement", "GraphManagementView", "graphviewer-menu", - { collection: graphsDummy} + { collection: graphsDummy , + collectionCollection : { id : 'store', fetch : jasmine.any(Function) } } ); - });*/ + }); it("should route to the applications tab", function () { simpleNavigationCheck( diff --git a/js/apps/system/aardvark/test/specs/views/graphManagementViewSpec.js b/js/apps/system/aardvark/test/specs/views/graphManagementViewSpec.js index 124b7902c4..04b1a46306 100644 --- a/js/apps/system/aardvark/test/specs/views/graphManagementViewSpec.js +++ b/js/apps/system/aardvark/test/specs/views/graphManagementViewSpec.js @@ -26,7 +26,8 @@ div.id = "content"; document.body.appendChild(div); view = new window.GraphManagementView({ - collection: graphs + collection: graphs, + collectionCollection: new window.arangoCollections() }); }); @@ -98,14 +99,13 @@ $("#newGraphEdges").val("newEdges"); spyOn($, "ajax").andCallFake(function (opts) { expect(opts.type).toEqual("POST"); - expect(opts.url).toEqual("/_api/graph"); - expect(opts.data).toEqual(JSON.stringify({ - _key: "newGraph", - vertices: "newVertices", - edges: "newEdges", - _id: "", - _rev: "" - })); + expect(opts.url).toEqual("/_api/gharial"); + expect(opts.data).toEqual(JSON.stringify( + { + "name":"newGraph", + "edgeDefinitions":[], + "orphanCollections":[] + })); }); $("#modalButton1").click(); expect($.ajax).toHaveBeenCalled(); From c26b9341f8dcdc4db06a045032cdc91e9d557cb5 Mon Sep 17 00:00:00 2001 From: Thomas Schmidts Date: Thu, 26 Jun 2014 15:54:29 +0200 Subject: [PATCH 08/11] Some changes in the code documentation and fixed spelling errors --- Documentation/Books/Users/Aql/Invoke.mdpp | 4 +- Documentation/Books/Users/Aql/Operators.mdpp | 50 +-- .../Users/SimpleQueries/FulltextQueries.mdpp | 4 +- .../SimpleQueries/ModificationQueries.mdpp | 8 +- .../Books/Users/SimpleQueries/Pagination.mdpp | 4 +- .../Books/Users/Transactions/Durability.mdpp | 2 +- .../Transactions/LockingAndIsolation.mdpp | 2 +- .../Books/Users/Transactions/README.mdpp | 9 +- .../Transactions/TransactionInvocation.mdpp | 371 ++++++++++-------- 9 files changed, 239 insertions(+), 215 deletions(-) diff --git a/Documentation/Books/Users/Aql/Invoke.mdpp b/Documentation/Books/Users/Aql/Invoke.mdpp index 7fe065adbf..9857b1017e 100644 --- a/Documentation/Books/Users/Aql/Invoke.mdpp +++ b/Documentation/Books/Users/Aql/Invoke.mdpp @@ -6,8 +6,8 @@ API description is available at [Http Interface for AQL Query Cursor](../HttpAql You can also run AQL queries from arangosh. To do so, first create an ArangoStatement object as follows: - arangosh> stmt = db._createStatement( { "query": "FOR i IN [ 1, 2 ] RETURN i * 2" } ); - [object ArangoStatement] + stmt = db._createStatement( { "query": "FOR i IN [ 1, 2 ] RETURN i * 2" } ); + [object ArangoQueryCursor] To execute the query, use the *execute* method: diff --git a/Documentation/Books/Users/Aql/Operators.mdpp b/Documentation/Books/Users/Aql/Operators.mdpp index 1d01932bae..a7795c5218 100644 --- a/Documentation/Books/Users/Aql/Operators.mdpp +++ b/Documentation/Books/Users/Aql/Operators.mdpp @@ -102,7 +102,7 @@ evaluation. The ternary operator expects a boolean condition as its first operand, and it returns the result of the second operand if the condition evaluates to true, and the third operand otherwise. -Example: +@EXAMPLES: u.age > 15 || u.active == true ? u.userId : null @@ -115,7 +115,7 @@ values. The *..* operator will produce a list of values in the defined range, with both bounding values included. -Example: +@EXAMPLES 2010..2013 @@ -274,7 +274,7 @@ For string processing, AQL offers the following functions: - *UPPER(value)*: Upper-case *value* - *SUBSTRING(value, offset, length)*: Return a substring of *value*, - starting at @FA{offset} and with a maximum length of *length* characters. Offsets + starting at *offset* and with a maximum length of *length* characters. Offsets start at position 0 - *LEFT(value, LENGTH)*: Returns the *LENGTH* leftmost characters of @@ -453,12 +453,12 @@ AQL supports the following functions to operate on list values: *list* is a document, returns the number of attribute keys of the document, regardless of their values. -- @FN{FLATTEN(list), depth)*: Turns a list of lists into a flat list. All +- *FLATTEN(list), depth)*: Turns a list of lists into a flat list. All list elements in *list* will be expanded in the result list. Non-list elements are added as they are. The function will recurse into sub-lists up to a depth of *depth*. *depth* has a default value of 1. - Example: + @EXAMPLES FLATTEN([ 1, 2, [ 3, 4 ], 5, [ 6, 7 ], [ 8, [ 9, 10 ] ]) @@ -523,7 +523,7 @@ AQL supports the following functions to operate on list values: - *LAST(list)*: Returns the last element in *list* or *null* if the list is empty. -- *NTH(list, position)*: Returns the list element at position @FA{position}. +- *NTH(list, position)*: Returns the list element at position *position*. Positions start at 0. If *position* is negative or beyond the upper bound of the list specified by *list*, then *null* will be returned. @@ -536,11 +536,11 @@ AQL supports the following functions to operate on list values: - *SLICE(list, start, length)*: Extracts a slice of the list specified by *list*. The extraction will start at list element with position *start*. Positions start at 0. Up to *length* elements will be extracted. If *length* is - not specified, all list elements starting at @FA{start} will be returned. + not specified, all list elements starting at *start* will be returned. If *start* is negative, it can be used to indicate positions from the end of the list. - Examples: +@EXAMPLES: SLICE([ 1, 2, 3, 4, 5 ], 0, 1) @@ -573,7 +573,7 @@ AQL supports the following functions to operate on list values: Note: No duplicates will be removed. In order to remove duplicates, please use either *UNION_DISTINCT* function or apply the *UNIQUE* on the result of *union*. - Example: + @EXAMPLES RETURN UNION( [ 1, 2, 3 ], @@ -638,7 +638,7 @@ AQL supports the following functions to operate on document values: The *examples* must be a list of 1..n example documents, with any number of attributes each. Note: specifying an empty list of examples is not allowed. - Example usage: + @EXAMPLE RETURN MATCHES( { "test" : 1 }, [ @@ -850,7 +850,7 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. returns a list of paths through the graph defined by the nodes in the collection *vertexcollection* and edges in the collection *edgecollection*. For each vertex in *vertexcollection*, it will determine the paths through the graph depending on the - value of @FA{direction}: + value of *direction*: - *"outbound"*: Follow all paths that start at the current vertex and lead to another vertex - *"inbound"*: Follow all paths that lead from another vertex to the current vertex - *"any"*: Combination of *"outbound"* and *"inbound"* @@ -865,7 +865,7 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. - *source*: start vertex of path - *destination*: destination vertex of path - Example calls: +@EXAMPLES PATHS(friends, friendrelations, "outbound", false) @@ -962,9 +962,9 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. - *vertex*: The vertex at the traversal point - *path*: The path history for the traversal point. The path is a document with the attributes *vertices* and *edges*, which are both lists. Note that *path* is only present - in the result if the *paths* attribute is set in the @FA{options} + in the result if the *paths* attribute is set in the *options* - Example calls: +@EXAMPLES TRAVERSAL(friends, friendrelations, "friends/john", "outbound", { strategy: "depthfirst", @@ -1021,7 +1021,7 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. - *TRAVERSAL_TREE(vertexcollection, edgecollection, startVertex, direction, connectName, options)*: Traverses the graph described by *vertexcollection* and *edgecollection*, - starting at the vertex identified by id @FA{startVertex} and creates a hierarchical result. + starting at the vertex identified by id *startVertex* and creates a hierarchical result. Vertex connectivity is establish by inserted an attribute which has the name specified via the *connectName* parameter. Connected vertices will be placed in this attribute as a list. @@ -1030,7 +1030,7 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. be set up in a way that resembles a depth-first, pre-order visitation result. Thus, the *strategy* and *order* attributes of the *options* attribute will be ignored. - Example calls: +@EXAMPLES TRAVERSAL_TREE(friends, friendrelations, "friends/john", "outbound", "likes", { itemOrder: "forward" @@ -1049,10 +1049,10 @@ This query is deprecated and will be removed soon. Please use [Graph operations](../Aql/GraphOperations.md) instead. - *SHORTEST_PATH(vertexcollection, edgecollection, startVertex, endVertex, direction, options)*: - Determines the first shortest path from the @FA{startVertex} to the *endVertex*. + Determines the first shortest path from the *startVertex* to the *endVertex*. Both vertices must be present in the vertex collection specified in *vertexcollection*, and any connecting edges must be present in the collection specified by *edgecollection*. - Vertex connectivity is specified by the @FA{direction} parameter: + Vertex connectivity is specified by the *direction* parameter: - *"outbound"*: Vertices are connected in *_from* to *_to* order - *"inbound"*: Vertices are connected in *_to* to *_from* order - *"any"*: Vertices are connected in both *_to* to *_from* and in @@ -1114,9 +1114,9 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. - *vertex*: The vertex at the traversal point - *path*: The path history for the traversal point. The path is a document with the attributes *vertices* and *edges*, which are both lists. Note that *path* is only present - in the result if the *paths* attribute is set in the @FA{options}. + in the result if the *paths* attribute is set in the *options*. - Example calls: +@EXAMPLES SHORTEST_PATH(cities, motorways, "cities/CGN", "cities/MUC", "outbound", { paths: true @@ -1160,7 +1160,7 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. To not restrict the result to specific connections, *edgeexamples* should be left unspecified. - Example calls: +@EXAMPLES EDGES(friendrelations, "friends/john", "outbound") EDGES(friendrelations, "friends/john", "any", [ { "$label": "knows" } ]) @@ -1182,7 +1182,7 @@ Please use [Graph operations](../Aql/GraphOperations.md) instead. To not restrict the result to specific connections, *edgeexamples* should be left unspecified. - Example calls: +@EXAMPLES NEIGHBORS(friends, friendrelations, "friends/john", "outbound") NEIGHBORS(users, usersrelations, "users/john", "any", [ { "$label": "recommends" } ] ) @@ -1221,7 +1221,7 @@ function categories: found, *null* will be returned. This function also allows *id* to be a list of ids. In this case, the function will return a list of all documents that could be found. - Examples: +@EXAMPLES: DOCUMENT(users, "users/john") DOCUMENT(users, "john") @@ -1239,10 +1239,10 @@ function categories: DOCUMENT([ "users/john", "users/amy" ]) - *SKIPLIST(collection, condition, skip, limit)*: Return all documents - from a skiplist index on collection *collection* that match the specified @FA{condition}. + from a skiplist index on collection *collection* that match the specified *condition*. This is a shortcut method to use a skiplist index for retrieving specific documents in indexed order. The skiplist index supports equality and less than/greater than queries. The - @FA{skip} and *limit* parameters are optional but can be specified to further limit the + *skip* and *limit* parameters are optional but can be specified to further limit the results: SKIPLIST(test, { created: [[ '>', 0 ]] }, 0, 100) diff --git a/Documentation/Books/Users/SimpleQueries/FulltextQueries.mdpp b/Documentation/Books/Users/SimpleQueries/FulltextQueries.mdpp index f5c9b823ac..44e3b8c2d2 100644 --- a/Documentation/Books/Users/SimpleQueries/FulltextQueries.mdpp +++ b/Documentation/Books/Users/SimpleQueries/FulltextQueries.mdpp @@ -12,9 +12,9 @@ When a fulltext index exists, it can be queried using a fulltext query. !SUBSECTION Fulltext -@startDocuBlock simple-query-fulltext +@startDocuBlock collectionFulltext -!SUBSECTION Fulltext query syntax: +!SUBSECTION Fulltext Syntax: In the simplest form, a fulltext query contains just the sought word. If multiple search words are given in a query, they should be separated by commas. diff --git a/Documentation/Books/Users/SimpleQueries/ModificationQueries.mdpp b/Documentation/Books/Users/SimpleQueries/ModificationQueries.mdpp index 864599e06c..12b11fa545 100644 --- a/Documentation/Books/Users/SimpleQueries/ModificationQueries.mdpp +++ b/Documentation/Books/Users/SimpleQueries/ModificationQueries.mdpp @@ -11,8 +11,8 @@ modify lots of documents in a collection. All methods can optionally be restricted to a specific number of operations. However, if a limit is specific but is less than the number of matches, it will be undefined which of the matching documents will get removed/modified. -[Remove by Example](../Documents/DocumentMethods.html#remove_by_example), -[Replace by Example](../Documents/DocumentMethods.html#replace_by_example) and -[Update by Example](../Documents/DocumentMethods.html#update_by_example) -are described with examples in the subchapter +[Remove by Example](../Documents/DocumentMethods.html#remove_by_example), + [Replace by Example](../Documents/DocumentMethods.html#replace_by_example) and +[Update by Example](../Documents/DocumentMethods.html#update_by_example) + are described with examples in the subchapter [Collection Methods](../Documents/DocumentMethods.md). \ No newline at end of file diff --git a/Documentation/Books/Users/SimpleQueries/Pagination.mdpp b/Documentation/Books/Users/SimpleQueries/Pagination.mdpp index aae32464f6..c860fd21fd 100644 --- a/Documentation/Books/Users/SimpleQueries/Pagination.mdpp +++ b/Documentation/Books/Users/SimpleQueries/Pagination.mdpp @@ -8,8 +8,8 @@ MySQL. *skip* used together with *limit* can be used to implement pagination. The *skip* operator skips over the first n documents. So, in order to create -result pages with 10 result documents per page, you can use `skip(n * -10).limit(10)` to access the 10 documents on the n.th page. This result should +result pages with 10 result documents per page, you can use *skip(n * +10).limit(10)* to access the 10 documents on the n.th page. This result should be sorted, so that the pagination works in a predicable way. !SUBSECTION Limit diff --git a/Documentation/Books/Users/Transactions/Durability.mdpp b/Documentation/Books/Users/Transactions/Durability.mdpp index 7b1beea2ad..a0f95e9a37 100644 --- a/Documentation/Books/Users/Transactions/Durability.mdpp +++ b/Documentation/Books/Users/Transactions/Durability.mdpp @@ -28,7 +28,7 @@ whether the delayed synchronization had kicked in or not. To ensure durability of transactions on a collection that have the *waitForSync* property set to *false*, you can set the *waitForSync* attribute of the object that is passed to *executeTransaction*. This will force a synchronization of the -transaction to disk even for collections that have *waitForSync set to *false*: +transaction to disk even for collections that have *waitForSync* set to *false*: db._executeTransaction({ collections: { diff --git a/Documentation/Books/Users/Transactions/LockingAndIsolation.mdpp b/Documentation/Books/Users/Transactions/LockingAndIsolation.mdpp index 0ff6a7d76b..9ab77528a0 100644 --- a/Documentation/Books/Users/Transactions/LockingAndIsolation.mdpp +++ b/Documentation/Books/Users/Transactions/LockingAndIsolation.mdpp @@ -29,7 +29,7 @@ from the collection as usual. However, as the collection ie added lazily, there isolation from other concurrent operations or transactions. Reads from such collections are potentially non-repeatable. -Example: +@EXAMPLES db._executeTransaction({ collections: { diff --git a/Documentation/Books/Users/Transactions/README.mdpp b/Documentation/Books/Users/Transactions/README.mdpp index cc4b3997ff..f4c3827831 100644 --- a/Documentation/Books/Users/Transactions/README.mdpp +++ b/Documentation/Books/Users/Transactions/README.mdpp @@ -6,13 +6,14 @@ transactions. Transactions in ArangoDB are atomic, consistent, isolated, and durable (*ACID*). These *ACID* properties provide the following guarantees: -- The *atomicity* priniciple makes transactions either complete in their + +* The *atomicity* principle makes transactions either complete in their entirety or have no effect at all. -- The *consistency* principle ensures that no constraints or other invariants +* The *consistency* principle ensures that no constraints or other invariants will be violated during or after any transaction. -- The *isolation* property will hide the modifications of a transaction from +* The *isolation* property will hide the modifications of a transaction from other transactions until the transaction commits. -- Finally, the *durability* proposition makes sure that operations from +* Finally, the *durability* proposition makes sure that operations from transactions that have committed will be made persistent. The amount of transaction durability is configurable in ArangoDB, as is the durability on collection level. \ No newline at end of file diff --git a/Documentation/Books/Users/Transactions/TransactionInvocation.mdpp b/Documentation/Books/Users/Transactions/TransactionInvocation.mdpp index 567b302435..7fce3fff01 100644 --- a/Documentation/Books/Users/Transactions/TransactionInvocation.mdpp +++ b/Documentation/Books/Users/Transactions/TransactionInvocation.mdpp @@ -18,7 +18,9 @@ in ArangoDB. Instead, a transaction in ArangoDB is started by providing a description of the transaction to the *db._executeTransaction* Javascript function: - db._executeTransaction(description); +```js +db._executeTransaction(description); +``` This function will then automatically start a transaction, execute all required data retrieval and/or modification operations, and at the end automatically @@ -45,33 +47,36 @@ Collections for a transaction are declared by providing them in the *collections attribute of the object passed to the *_executeTransaction* function. The *collections* attribute has the sub-attributes *read* and *write*: - db._executeTransaction({ - collections: { - write: [ "users", "logins" ], - read: [ "recommendations" ] - }, - ... - }); +```js +db._executeTransaction({ + collections: { + write: [ "users", "logins" ], + read: [ "recommendations" ] + }, + ... +}); +``` *read* and *write* are optional attributes, and only need to be specified if the operations inside the transactions demand for it. The contents of *read* or *write* can each be lists with collection names or a single collection name (as a string): - - db._executeTransaction({ - collections: { - write: "users", - read: "recommendations" - }, - ... - }); +```js +db._executeTransaction({ + collections: { + write: "users", + read: "recommendations" + }, + ... +}); +``` Note that it is currently optional to specify collections for read-only access. Even without specifying them, it is still possible to read from such collections from within a transaction, but with relaxed isolation. Please refer to -@ref TransactionsLocking for more details. +[Transactions Locking](../Transactions/LockingAndIsolation.md) for more details. !SUBSECTION Declaration of data modification and retrieval operations @@ -79,42 +84,47 @@ All data modification and retrieval operations that are to be executed inside the transaction need to be specified in a Javascript function, using the *action* attribute: - db._executeTransaction({ - collections: { - write: "users" - }, - action: function () { - // all operations go here - } - }); +```js +db._executeTransaction({ + collections: { + write: "users" + }, + action: function () { + // all operations go here + } +}); +``` Any valid Javascript code is allowed inside *action* but the code may only access the collections declared in *collections*. *action* may be a Javascript function as shown above, or a string representation of a Javascript function: - db._executeTransaction({ - collections: { - write: "users" - }, - action: "function () { doSomething(); }" - }); - +``` +db._executeTransaction({ + collections: { + write: "users" + }, + action: "function () { doSomething(); }" +}); +``` Please note that any operations specified in *action* will be executed on the server, in a separate scope. Variables will be bound late. Accessing any Javascript variables defined on the client-side or in some other server context from inside a transaction may not work. Instead, any variables used inside *action* should be defined inside *action* itself: - db._executeTransaction({ - collections: { - write: "users" - }, - action: function () { - var db = require(...).db; - db.users.save({ ... }); - } - }); +``` +db._executeTransaction({ + collections: { + write: "users" + }, + action: function () { + var db = require(...).db; + db.users.save({ ... }); + } +}); +``` When the code inside the *action* attribute is executed, the transaction is already started and all required locks have been acquired. When the code inside @@ -124,18 +134,20 @@ There is no explicit commit command. To make a transaction abort and roll back all changes, an exception needs to be thrown and not caught inside the transaction: - db._executeTransaction({ - collections: { - write: "users" - }, - action: function () { - var db = require("internal").db; - db.users.save({ _key: "hello" }); +```js +db._executeTransaction({ + collections: { + write: "users" + }, + action: function () { + var db = require("internal").db; + db.users.save({ _key: "hello" }); - // will abort and roll back the transaction - throw "doh!"; - } - }); + // will abort and roll back the transaction + throw "doh!"; + } +}); +``` There is no explicit abort or roll back command. @@ -143,18 +155,20 @@ As mentioned earlier, a transaction will commit automatically when the end of the *action* function is reached and no exception has been thrown. In this case, the user can return any legal Javascript value from the function: - db._executeTransaction({ - collections: { - write: "users" - }, - action: function () { - var db = require("internal").db; - db.users.save({ _key: "hello" }); +```js +db._executeTransaction({ + collections: { + write: "users" + }, + action: function () { + var db = require("internal").db; + db.users.save({ _key: "hello" }); - // will commit the transaction and return the value "hello" - return "hello"; - } - }); + // will commit the transaction and return the value "hello" + return "hello"; + } +}); +``` !SUBSECTION Examples @@ -165,108 +179,114 @@ The *c1* collection needs to be declared in the *write* attribute of the The *action* attribute contains the actual transaction code to be executed. This code contains all data modification operations (3 in this example). - // setup - db._create("c1"); - - db._executeTransaction({ - collections: { - write: [ "c1" ] - }, - action: function () { - var db = require("internal").db; - db.c1.save({ _key: "key1" }); - db.c1.save({ _key: "key2" }); - db.c1.save({ _key: "key3" }); - } - }); +```js +// setup +db._create("c1"); +db._executeTransaction({ + collections: { + write: [ "c1" ] + }, + action: function () { + var db = require("internal").db; + db.c1.save({ _key: "key1" }); + db.c1.save({ _key: "key2" }); + db.c1.save({ _key: "key3" }); + } +}); db.c1.count(); // 3 +``` + + Aborting the transaction by throwing an exception in the *action* function will revert all changes, so as if the transaction never happened: - - // setup - db._create("c1"); +``` js +// setup +db._create("c1"); - db._executeTransaction({ - collections: { - write: [ "c1" ] - }, - action: function () { - var db = require("internal").db; - db.c1.save({ _key: "key1" }); - db.c1.count(); // 1 +db._executeTransaction({ + collections: { + write: [ "c1" ] + }, + action: function () { + var db = require("internal").db; + db.c1.save({ _key: "key1" }); + db.c1.count(); // 1 - db.c1.save({ _key: "key2" }); - db.c1.count(); // 2 + db.c1.save({ _key: "key2" }); + db.c1.count(); // 2 - throw "doh!"; - } - }); - - db.c1.count(); // 0 + throw "doh!"; + } +}); +db.c1.count(); // 0 +``` The automatic rollback is also executed when an internal exception is thrown at some point during transaction execution: - // setup - db._create("c1"); +```js +// setup +db._create("c1"); - db._executeTransaction({ - collections: { - write: [ "c1" ] - }, - action: function () { - var db = require("internal").db; - db.c1.save({ _key: "key1" }); - - // will throw duplicate a key error, not explicitly requested by the user - db.c1.save({ _key: "key1" }); +db._executeTransaction({ + collections: { + write: [ "c1" ] + }, + action: function () { + var db = require("internal").db; + db.c1.save({ _key: "key1" }); + + // will throw duplicate a key error, not explicitly requested by the user + db.c1.save({ _key: "key1" }); - // we'll never get here... - } - }); - - db.c1.count(); // 0 + // we'll never get here... + } +}); +db.c1.count(); // 0 +``` As required by the *consistency* principle, aborting or rolling back a transaction will also restore secondary indexes to the state at transaction start. The following example using a cap constraint should illustrate that: - // setup - db._create("c1"); - - // limit the number of documents to 3 - db.c1.ensureCapConstraint(3); +```js +// setup +db._create("c1"); - // insert 3 documents - db.c1.save({ _key: "key1" }); - db.c1.save({ _key: "key2" }); - db.c1.save({ _key: "key3" }); +// limit the number of documents to 3 +db.c1.ensureCapConstraint(3); - // this will push out key1 - // we now have these keys: [ "key1", "key2", "key3" ] - db.c1.save({ _key: "key4" }); +// insert 3 documents +db.c1.save({ _key: "key1" }); +db.c1.save({ _key: "key2" }); +db.c1.save({ _key: "key3" }); + +// this will push out key1 +// we now have these keys: [ "key1", "key2", "key3" ] +db.c1.save({ _key: "key4" }); - db._executeTransaction({ - collections: { - write: [ "c1" ] - }, - action: function () { - var db = require("internal").db; - // this will push out key2. we now have keys [ "key3", "key4", "key5" ] - db.c1.save({ _key: "key5" }); +db._executeTransaction({ + collections: { + write: [ "c1" ] + }, + action: function () { + var db = require("internal").db; + // this will push out key2. we now have keys [ "key3", "key4", "key5" ] + db.c1.save({ _key: "key5" }); - // will abort the transaction - throw "doh!" - } - }); + // will abort the transaction + throw "doh!" + } +}); - // we now have these keys back: [ "key2", "key3", "key4" ] +// we now have these keys back: [ "key2", "key3", "key4" ] +``` !SUBSECTION Cross-collection transactions @@ -274,50 +294,53 @@ There's also the possibility to run a transaction across multiple collections. In this case, multiple collections need to be declared in the *collections* attribute, e.g.: - // setup - db._create("c1"); - db._create("c2"); +```js +// setup +db._create("c1"); +db._create("c2"); - db._executeTransaction({ - collections: { - write: [ "c1", "c2" ] - }, - action: function () { - var db = require("internal").db; - db.c1.save({ _key: "key1" }); - db.c2.save({ _key: "key2" }); - } - }); - - db.c1.count(); // 1 - db.c2.count(); // 1 +db._executeTransaction({ + collections: { + write: [ "c1", "c2" ] + }, + action: function () { + var db = require("internal").db; + db.c1.save({ _key: "key1" }); + db.c2.save({ _key: "key2" }); + } +}); +db.c1.count(); // 1 +db.c2.count(); // 1 +``` Again, throwing an exception from inside the *action* function will make the transaction abort and roll back all changes in all collections: - // setup - db._create("c1"); - db._create("c2"); +```js +// setup +db._create("c1"); +db._create("c2"); - db._executeTransaction({ - collections: { - write: [ "c1", "c2" ] - }, - action: function () { - var db = require("internal").db; - for (var i = 0; i < 100; ++i) { - db.c1.save({ _key: "key" + i }); - db.c2.save({ _key: "key" + i }); - } +db._executeTransaction({ + collections: { + write: [ "c1", "c2" ] + }, + action: function () { + var db = require("internal").db; + for (var i = 0; i < 100; ++i) { + db.c1.save({ _key: "key" + i }); + db.c2.save({ _key: "key" + i }); + } - db.c1.count(); // 100 - db.c2.count(); // 100 + db.c1.count(); // 100 + db.c2.count(); // 100 - // abort - throw "doh!" - } - }); + // abort + throw "doh!" + } +}); - db.c1.count(); // 0 - db.c2.count(); // 0 +db.c1.count(); // 0 +db.c2.count(); // 0 +``` \ No newline at end of file From 95e60c069b5be330419b00ce833edb0e6615ecf0 Mon Sep 17 00:00:00 2001 From: gschwab Date: Thu, 26 Jun 2014 15:54:50 +0200 Subject: [PATCH 09/11] edit graph --- .../aardvark/frontend/js/models/graph.js | 35 +++++++- .../js/templates/edgeDefinitionTable.ejs | 2 +- .../frontend/js/views/graphManagementView.js | 88 ++++++++++++++++++- .../modules/org/arangodb/general-graph.js | 9 ++ 4 files changed, 130 insertions(+), 4 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/models/graph.js b/js/apps/system/aardvark/frontend/js/models/graph.js index a39b1b3b9b..c8afb862f0 100644 --- a/js/apps/system/aardvark/frontend/js/models/graph.js +++ b/js/apps/system/aardvark/frontend/js/models/graph.js @@ -1,5 +1,5 @@ /*jslint indent: 2, nomen: true, maxlen: 100, vars: true, white: true, plusplus: true */ -/*global window, Backbone */ +/*global window, Backbone, $ */ (function() { "use strict"; @@ -17,6 +17,39 @@ return raw.graph || raw; }, + addEdgeDefinition: function(edgeDefinition) { + $.ajax( + { + async: false, + type: "POST", + url: this.urlRoot + "/" + this.get("_key") + "/edge", + data: JSON.stringify(edgeDefinition) + } + ); + }, + + deleteEdgeDefinition: function(edgeCollection) { + $.ajax( + { + async: false, + type: "DELETE", + url: this.urlRoot + "/" + this.get("_key") + "/edge/" + edgeCollection + } + ); + }, + + modifyEdgeDefinition: function(edgeDefinition) { + $.ajax( + { + async: false, + type: "PUT", + url: this.urlRoot + "/" + this.get("_key") + "/edge/" + edgeDefinition.collection, + data: JSON.stringify(edgeDefinition) + } + ); + }, + + defaults: { name: "", edgeDefinitions: [], diff --git a/js/apps/system/aardvark/frontend/js/templates/edgeDefinitionTable.ejs b/js/apps/system/aardvark/frontend/js/templates/edgeDefinitionTable.ejs index 5b40d504b2..0d2801fd4d 100644 --- a/js/apps/system/aardvark/frontend/js/templates/edgeDefinitionTable.ejs +++ b/js/apps/system/aardvark/frontend/js/templates/edgeDefinitionTable.ejs @@ -1,5 +1,5 @@