diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 6b03fd82e3..a80dc99ae5 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -2170,6 +2170,15 @@ static v8::Handle ModifyVocbaseColCoordinator ( return scope.Close(ret); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal struct which is used for reading the different option +/// parameters for the replace function +//////////////////////////////////////////////////////////////////////////////// + +struct ReplaceOptions { + bool overwrite = true; + bool waitForSync = false; +}; //////////////////////////////////////////////////////////////////////////////// /// @brief replaces a document @@ -2178,10 +2187,14 @@ static v8::Handle ModifyVocbaseColCoordinator ( static v8::Handle ReplaceVocbaseCol (bool useCollection, v8::Arguments const& argv) { v8::HandleScope scope; + ReplaceOptions options; + TRI_doc_update_policy_e policy = TRI_DOC_UPDATE_ERROR; // check the arguments if (argv.Length() < 2) { - TRI_V8_EXCEPTION_USAGE(scope, "replace(, , , )"); + TRI_V8_EXCEPTION_USAGE(scope, "replace(, , , ) or" + "replace(, , {overwrite: booleanValue, waitForSync: booleanValue})" + ); } // we're only accepting "real" object documents @@ -2189,8 +2202,26 @@ static v8::Handle ReplaceVocbaseCol (bool useCollection, TRI_V8_EXCEPTION(scope, TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } - const TRI_doc_update_policy_e policy = ExtractUpdatePolicy(argv, 3); - const bool forceSync = ExtractForceSync(argv, 4); + 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("waitForSync"))) { + options.waitForSync = TRI_ObjectToBoolean(optionsObject->Get(v8::String::New("waitForSync"))); + } + } else {// old variant replace(, , , ) + if (argv.Length() > 2 ) { + options.overwrite = TRI_ObjectToBoolean(argv[2]); + policy = ExtractUpdatePolicy(options.overwrite); + } + if (argv.Length() > 3 ) { + options.waitForSync = TRI_ObjectToBoolean(argv[3]); + } + } + } TRI_voc_key_t key = 0; TRI_voc_rid_t rid; @@ -2238,7 +2269,7 @@ static v8::Handle ReplaceVocbaseCol (bool useCollection, if (ServerState::instance()->isCoordinator()) { return scope.Close(ModifyVocbaseColCoordinator(col, policy, - forceSync, + options.waitForSync, false, // isPatch true, // keepNull, does not matter argv)); @@ -2309,7 +2340,7 @@ static v8::Handle ReplaceVocbaseCol (bool useCollection, TRI_V8_EXCEPTION_MESSAGE(scope, TRI_errno(), " cannot be converted into JSON shape"); } - res = trx.updateDocument(key, &document, shaped, policy, forceSync, rid, &actualRevision); + res = trx.updateDocument(key, &document, shaped, policy, options.waitForSync, rid, &actualRevision); res = trx.finish(res); @@ -7229,12 +7260,14 @@ static v8::Handle JS_RenameVocbaseCol (v8::Arguments const& argv) { /// If there is a conflict, i. e. if the revision of the @LIT{document} does not /// match the revision in the collection, then an error is thrown. /// -/// @FUN{@FA{collection}.replace(@FA{document}, @FA{data}, true)} +/// @FUN{@FA{collection}.replace(@FA{document}, @FA{data}, true)} or +/// @FUN{@FA{collection}.replace(@FA{document}, @FA{data}, {@FA{overwrite}: true})} /// /// As before, but in case of a conflict, the conflict is ignored and the old /// document is overwritten. /// -/// @FUN{@FA{collection}.replace(@FA{document}, @FA{data}, true, @FA{waitForSync})} +/// @FUN{@FA{collection}.replace(@FA{document}, @FA{data}, true, @FA{waitForSync})} or +/// @FUN{@FA{collection}.replace(@FA{document}, @FA{data}, {@FA{overwrite}: true, @FA{waitForSync}: true or false})} /// /// The optional @FA{waitForSync} parameter can be used to force /// synchronisation of the document replacement operation to disk even in case @@ -7401,7 +7434,9 @@ static v8::Handle JS_RotateVocbaseCol (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// /// @brief updates a document /// -/// @FUN{@FA{collection}.update(@FA{document}, @FA{data}, @FA{overwrite}, @FA{keepNull}, @FA{waitForSync})} +/// @FUN{@FA{collection}.update(@FA{document}, @FA{data}, @FA{overwrite}, @FA{keepNull}, @FA{waitForSync})} or +/// @FUN{@FA{collection}.update(@FA{document}, @FA{data}, +/// { @FA{overwrite} : true or false, @FA{keepNull} : true or false, @FA{waitForSync} : true or false})} /// /// Updates an existing @FA{document}. The @FA{document} must be a document in /// the current collection. This document is then patched with the 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 257d6a02e1..4ddcca2b1a 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 @@ -1158,15 +1158,23 @@ ArangoCollection.prototype.replace = function (id, data, overwrite, waitForSync) id = id._id; } - var policy = ""; - - if (overwrite) { - policy = "?policy=last"; + var params = ""; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "?policy=last"; + } + if (options.hasOwnProperty("waitForSync") ) { + waitForSync = options.waitForSync; + } + } else { + if (overwrite) { + params += "?policy=last"; + } } - - var url = this._documenturl(id) + policy; + var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); - if (rev === null) { requestResult = this._database._connection.PUT(url, JSON.stringify(data)); } 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 f5973f0746..083070f21a 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 @@ -705,13 +705,23 @@ ArangoDatabase.prototype._replace = function (id, data, overwrite, waitForSync) id = id._id; } - var policy = ""; + var params = ""; - if (overwrite) { - policy = "?policy=last"; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "?policy=last"; + } + if (options.hasOwnProperty("waitForSync") ) { + waitForSync = options.waitForSync; + } + } else { + if (overwrite) { + params += "?policy=last"; + } } - - var url = this._documenturl(id) + policy; + var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); if (rev === null) { diff --git a/js/client/modules/org/arangodb/arango-collection.js b/js/client/modules/org/arangodb/arango-collection.js index 5ca8d2bb58..52da5116dc 100644 --- a/js/client/modules/org/arangodb/arango-collection.js +++ b/js/client/modules/org/arangodb/arango-collection.js @@ -1157,15 +1157,23 @@ ArangoCollection.prototype.replace = function (id, data, overwrite, waitForSync) id = id._id; } - var policy = ""; - - if (overwrite) { - policy = "?policy=last"; + var params = ""; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "?policy=last"; + } + if (options.hasOwnProperty("waitForSync") ) { + waitForSync = options.waitForSync; + } + } else { + if (overwrite) { + params += "?policy=last"; + } } - - var url = this._documenturl(id) + policy; + var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); - if (rev === null) { requestResult = this._database._connection.PUT(url, JSON.stringify(data)); } diff --git a/js/client/modules/org/arangodb/arango-database.js b/js/client/modules/org/arangodb/arango-database.js index 45cdf83059..dddb9992a2 100644 --- a/js/client/modules/org/arangodb/arango-database.js +++ b/js/client/modules/org/arangodb/arango-database.js @@ -704,13 +704,23 @@ ArangoDatabase.prototype._replace = function (id, data, overwrite, waitForSync) id = id._id; } - var policy = ""; + var params = ""; - if (overwrite) { - policy = "?policy=last"; + if (typeof overwrite === "object") { + // we assume the caller uses new signature (id, data, options) + var options = overwrite; + if (options.hasOwnProperty("overwrite") && options.overwrite) { + params += "?policy=last"; + } + if (options.hasOwnProperty("waitForSync") ) { + waitForSync = options.waitForSync; + } + } else { + if (overwrite) { + params += "?policy=last"; + } } - - var url = this._documenturl(id) + policy; + var url = this._documenturl(id) + params; url = this._appendSyncParameter(url, waitForSync); if (rev === null) { diff --git a/js/common/tests/shell-document.js b/js/common/tests/shell-document.js index 93c57b49d0..eabf6bd7e3 100644 --- a/js/common/tests/shell-document.js +++ b/js/common/tests/shell-document.js @@ -713,6 +713,48 @@ function CollectionDocumentSuite () { assertEqual(4, doc4.a); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests the replace function with the new signature +//////////////////////////////////////////////////////////////////////////////// + + testReplaceWithNewSignatureDocument : function () { + var a1 = collection.save({ a : 1}); + + assertTypeOf("string", a1._id); + assertTypeOf("string", a1._rev); + // important test, the server has to compute the overwrite policy in correct wise + var a2 = collection.replace(a1, { a : 2 }); + + assertEqual(a1._id, a2._id); + assertNotEqual(a1._rev, a2._rev); + + try { + collection.replace(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); + // new signature + var a4 = collection.replace(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); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief replace a document, waitForSync=false //////////////////////////////////////////////////////////////////////////////// @@ -725,6 +767,21 @@ function CollectionDocumentSuite () { var a2 = collection.replace(a1, { a : 2 }, true, false); + assertEqual(a1._id, a2._id); + assertNotEqual(a1._rev, a2._rev); + }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests the replace function with new signature +//////////////////////////////////////////////////////////////////////////////// + + testReplaceWithNewSignatureDocumentSyncFalse : function () { + var a1 = collection.save({ a : 1}); + + assertTypeOf("string", a1._id); + assertTypeOf("string", a1._rev); + + var a2 = collection.replace(a1, { a : 2 }, {"overwrite": true, "waitForSync": false}); + assertEqual(a1._id, a2._id); assertNotEqual(a1._rev, a2._rev); }, @@ -745,6 +802,22 @@ function CollectionDocumentSuite () { assertNotEqual(a1._rev, a2._rev); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests the replace function with new signature +//////////////////////////////////////////////////////////////////////////////// + + testReplaceDocumentSyncTrue : function () { + var a1 = collection.save({ a : 1}); + + assertTypeOf("string", a1._id); + assertTypeOf("string", a1._rev); + + var a2 = collection.replace(a1, { a : 2 }, {"overwrite": true, "waitForSync": true}); + + assertEqual(a1._id, a2._id); + assertNotEqual(a1._rev, a2._rev); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief update a document //////////////////////////////////////////////////////////////////////////////// @@ -1407,6 +1480,47 @@ function DatabaseDocumentSuite () { var doc4 = db._document(a1._id); + assertEqual(a1._id, doc4._id); + assertEqual(a4._rev, doc4._rev); + assertEqual(4, doc4.a); + }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests the _replace function with new signature +//////////////////////////////////////////////////////////////////////////////// + + test_ReplaceWithNewSignatureDocument : function () { + var a1 = collection.save({ a : 1}); + + assertTypeOf("string", a1._id); + assertTypeOf("string", a1._rev); + + var a2 = db._replace(a1, { a : 2 }); + + assertEqual(a1._id, a2._id); + assertNotEqual(a1._rev, a2._rev); + + try { + db._replace(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._replace(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); diff --git a/js/common/tests/shell-general-graph.js b/js/common/tests/shell-general-graph.js index 1a4d8b005c..cdcc9f4b42 100644 --- a/js/common/tests/shell-general-graph.js +++ b/js/common/tests/shell-general-graph.js @@ -599,7 +599,8 @@ function GeneralGraphAQLQueriesSuite() { fail(); } catch (err) { assertEqual(err.errorNum, ERRORS.ERROR_BAD_PARAMETER.code); - assertEqual(err.errorMessage, "edge collections: failed and unknown and foxxle are not known to the graph"); + assertEqual(err.errorMessage, + "edge collections: failed and unknown and foxxle are not known to the graph"); } }, @@ -750,32 +751,38 @@ function GeneralGraphAQLQueriesSuite() { assertFalse(findIdInResult(result, e3), "e3 is not excluded"); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief test: let construct on edges -//////////////////////////////////////////////////////////////////////////////// - -/* Broken string replacement - test_letOnEdges: function() { - var query = g._edges("v1/1").let("myVal = e.val"); - assertEqual(query.printQuery(), "FOR edges_0 IN GRAPH_EDGES(" - + "@graphName,@startVertex_0,any) LET myVal = edges_0.val"); - var bindVars = query.bindVars; - assertEqual(bindVars.graphName, "graph"); - assertEqual(bindVars.startVertex_0, "v1/1"); - */ - /* - var result = query.toArray(); - assertEqual(result.length, 1); - assertTrue(findIdInResult(result, e3)); - assertFalse(findIdInResult(result, e1)); - assertFalse(findIdInResult(result, e2)); - */ - /* - } - */ + }; + +} + +function ChainedFluentAQLResultsSuite() { + + var gn = "UnitTestGraph"; + var g; + + var edgeDef = []; + + + var createTestData = function() { + g = graph._create(gn, edgeDef); }; + var dropData = function() { + graph._drop(gn); + }; + + return { + + setUp: function() { + + }, + + tearDown: dropData + + }; + + } function EdgesAndVerticesSuite() {