diff --git a/arangod/RestHandler/RestCursorHandler.cpp b/arangod/RestHandler/RestCursorHandler.cpp index 033db261f7..ca1e1a4223 100644 --- a/arangod/RestHandler/RestCursorHandler.cpp +++ b/arangod/RestHandler/RestCursorHandler.cpp @@ -178,9 +178,10 @@ void RestCursorHandler::processQuery(VPackSlice const& slice) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } + auto transactionContext = std::make_shared(_vocbase); arangodb::basics::VPackStringBufferAdapter bufferAdapter( _response->body().stringBuffer()); - VPackDumper dumper(&bufferAdapter); + VPackDumper dumper(&bufferAdapter, transactionContext->getVPackOptions()); dumper.dump(result.slice()); return; } diff --git a/arangod/RestHandler/RestDocumentHandler.cpp b/arangod/RestHandler/RestDocumentHandler.cpp index 8b67062fd6..407ba7f9cb 100644 --- a/arangod/RestHandler/RestDocumentHandler.cpp +++ b/arangod/RestHandler/RestDocumentHandler.cpp @@ -337,7 +337,14 @@ bool RestDocumentHandler::checkDocument() { /// @brief was docuBlock REST_DOCUMENT_REPLACE //////////////////////////////////////////////////////////////////////////////// -bool RestDocumentHandler::replaceDocument() { return modifyDocument(false); } +bool RestDocumentHandler::replaceDocument() { + bool found; + std::string value = _request->value("onlyget", found); + if (found) { + return readManyDocuments(); + } + return modifyDocument(false); +} //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock REST_DOCUMENT_UPDATE @@ -581,3 +588,59 @@ bool RestDocumentHandler::deleteDocument() { return true; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief was docuBlock REST_DOCUMENT_READ_MANY +//////////////////////////////////////////////////////////////////////////////// + +bool RestDocumentHandler::readManyDocuments() { + std::vector const& suffix = _request->suffix(); + + if (suffix.size() != 1) { + generateError(HttpResponse::BAD, TRI_ERROR_HTTP_BAD_PARAMETER, + "expecting PUT /_api/document/ with a BODY"); + return false; + } + + // split the document reference + std::string const& collectionName = suffix[0]; + + OperationOptions opOptions; + opOptions.ignoreRevs = extractBooleanParameter("ignoreRevs", false); + + auto transactionContext(StandaloneTransactionContext::Create(_vocbase)); + SingleCollectionTransaction trx(transactionContext, + collectionName, TRI_TRANSACTION_READ); + + // ........................................................................... + // inside read transaction + // ........................................................................... + + int res = trx.begin(); + + if (res != TRI_ERROR_NO_ERROR) { + generateTransactionError(collectionName, res, ""); + return false; + } + + auto builderPtr = _request->toVelocyPack(transactionContext->getVPackOptions()); + VPackSlice search = builderPtr->slice(); + + OperationResult result = trx.document(collectionName, search, opOptions); + + res = trx.finish(result.code); + + if (!result.successful()) { + generateTransactionError(result); + return false; + } + + if (res != TRI_ERROR_NO_ERROR) { + generateTransactionError(collectionName, res, ""); + return false; + } + + generateDocument(result.slice(), true, + transactionContext->getVPackOptions()); + return true; +} + diff --git a/arangod/RestHandler/RestDocumentHandler.h b/arangod/RestHandler/RestDocumentHandler.h index 7ba5ccfebf..2eb97398dc 100644 --- a/arangod/RestHandler/RestDocumentHandler.h +++ b/arangod/RestHandler/RestDocumentHandler.h @@ -67,6 +67,12 @@ class RestDocumentHandler : public RestVocbaseBaseHandler { bool readSingleDocument(bool generateBody); + ////////////////////////////////////////////////////////////////////////////// + /// @brief reads multiple documents + ////////////////////////////////////////////////////////////////////////////// + + bool readManyDocuments(); + ////////////////////////////////////////////////////////////////////////////// /// @brief reads all documents ////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index dcbd01760b..8630d480fd 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -30,6 +30,7 @@ #include "Basics/VPackStringBufferAdapter.h" #include "Cluster/ServerState.h" #include "Rest/HttpRequest.h" +#include "Utils/StandaloneTransactionContext.h" #include "Utils/Transaction.h" #include "VocBase/document-collection.h" @@ -292,7 +293,8 @@ void RestVocbaseBaseHandler::generatePreconditionFailed( } VPackStringBufferAdapter buffer(_response->body().stringBuffer()); - VPackDumper dumper(&buffer); + auto transactionContext(StandaloneTransactionContext::Create(_vocbase)); + VPackDumper dumper(&buffer, transactionContext->getVPackOptions()); try { dumper.dump(builder.slice()); @@ -408,6 +410,10 @@ void RestVocbaseBaseHandler::generateTransactionError( generateError(HttpResponse::BAD, res, "invalid document key"); return; + case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD: + generateError(HttpResponse::BAD, res, "invalid document handle"); + return; + case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE: generateError(HttpResponse::BAD, res, "invalid edge attribute"); return; @@ -499,6 +505,10 @@ void RestVocbaseBaseHandler::generateTransactionError( generateError(HttpResponse::BAD, result.code, "invalid document key"); return; + case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD: + generateError(HttpResponse::BAD, result.code, "invalid document handle"); + return; + case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE: generateError(HttpResponse::BAD, result.code, "invalid edge attribute"); return; diff --git a/arangod/Utils/Transaction.cpp b/arangod/Utils/Transaction.cpp index 9c3d920a95..a0b2a42850 100644 --- a/arangod/Utils/Transaction.cpp +++ b/arangod/Utils/Transaction.cpp @@ -545,7 +545,12 @@ std::string Transaction::extractKey(VPackSlice const slice) { return k.copyString(); } if (slice.isString()) { - return slice.copyString(); + std::string key = slice.copyString(); + size_t pos = key.find('/'); + if (pos != std::string::npos) { + key = key.substr(pos+1); + } + return key; } return ""; } @@ -968,7 +973,7 @@ OperationResult Transaction::documentLocal(std::string const& collectionName, auto workOnOneDocument = [&](VPackSlice const value) -> int { std::string key(Transaction::extractKey(value)); if (key.empty()) { - return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD; + return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; } VPackSlice expectedRevision; @@ -1541,21 +1546,32 @@ OperationResult Transaction::removeLocal(std::string const& collectionName, VPackBuilder resultBuilder; - auto workOnOneDocument = [&](VPackSlice const value) -> int { + auto workOnOneDocument = [&](VPackSlice value) -> int { VPackSlice actualRevision; TRI_doc_mptr_t previous; + std::string key; + std::shared_ptr builder; + if (value.isString()) { + key = value.copyString(); + size_t pos = key.find('/'); + if (pos != std::string::npos) { + key = key.substr(pos+1); + builder = std::make_shared(); + builder->add(VPackValue(key)); + value = builder->slice(); + } + } else if (value.isObject()) { + key = value.get(TRI_VOC_ATTRIBUTE_KEY).copyString(); + } else { + return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; + } + int res = document->remove(this, value, options, !isLocked(document, TRI_TRANSACTION_WRITE), actualRevision, previous); if (res != TRI_ERROR_NO_ERROR) { if (res == TRI_ERROR_ARANGO_CONFLICT && !options.silent) { - std::string key; - if (value.isString()) { - key = value.copyString(); - } else { - key = value.get(TRI_VOC_ATTRIBUTE_KEY).copyString(); - } buildDocumentIdentity(resultBuilder, cid, key, actualRevision, VPackSlice(), options.returnOld ? &previous : nullptr, nullptr); @@ -1564,12 +1580,6 @@ OperationResult Transaction::removeLocal(std::string const& collectionName, } if (!options.silent) { - std::string key; - if (value.isString()) { - key = value.copyString(); - } else { - key = value.get(TRI_VOC_ATTRIBUTE_KEY).copyString(); - } buildDocumentIdentity(resultBuilder, cid, key, actualRevision, VPackSlice(), options.returnOld ? &previous : nullptr, nullptr); diff --git a/arangod/VocBase/vocbase.cpp b/arangod/VocBase/vocbase.cpp index 79bf099008..6215ef45c0 100644 --- a/arangod/VocBase/vocbase.cpp +++ b/arangod/VocBase/vocbase.cpp @@ -2341,7 +2341,9 @@ TRI_voc_rid_t TRI_ExtractRevisionId(VPackSlice const slice) { //////////////////////////////////////////////////////////////////////////////// VPackSlice TRI_ExtractRevisionIdAsSlice(VPackSlice const slice) { - TRI_ASSERT(slice.isObject()); + if (!slice.isObject()) { + return VPackSlice(); + } return slice.get(TRI_VOC_ATTRIBUTE_REV); } diff --git a/js/client/modules/@arangodb/arango-collection.js b/js/client/modules/@arangodb/arango-collection.js index d9ad89b17a..3cb3fcea60 100644 --- a/js/client/modules/@arangodb/arango-collection.js +++ b/js/client/modules/@arangodb/arango-collection.js @@ -789,23 +789,37 @@ ArangoCollection.prototype.document = function (id) { var rev = null; var requestResult; - if (typeof id === "object") { - if (id.hasOwnProperty("_rev")) { - rev = id._rev; - } - if (id.hasOwnProperty("_id")) { - id = id._id; - } else if (id.hasOwnProperty("_key")) { - id = id._key; - } + if (id === undefined || id === null) { + throw new ArangoError({ + errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, + errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message + }); } - if (rev === null) { - requestResult = this._database._connection.GET(this._documenturl(id)); - } - else { - requestResult = this._database._connection.GET(this._documenturl(id), - {'if-match' : JSON.stringify(rev) }); + var url; + if (Array.isArray(id)) { + url = this._documentcollectionurl() + "?onlyget=true&ignoreRevs=false"; + var body = JSON.stringify(id); + requestResult = this._database._connection.PUT(url, body); + } else { + if (typeof id === "object") { + if (id.hasOwnProperty("_rev")) { + rev = id._rev; + } + if (id.hasOwnProperty("_id")) { + id = id._id; + } else if (id.hasOwnProperty("_key")) { + id = id._key; + } + } + url = this._documenturl(id); + if (rev === null) { + requestResult = this._database._connection.GET(this._documenturl(id)); + } + else { + requestResult = this._database._connection.GET(this._documenturl(id), + {'if-match' : JSON.stringify(rev) }); + } } if (requestResult !== null && requestResult.error === true) { @@ -1034,12 +1048,13 @@ ArangoCollection.prototype.remove = function (id, overwrite, waitForSync) { } var url; + var body = ""; if (Array.isArray(id)) { url = this._documentcollectionurl(); body = JSON.stringify(id); } else { - if (typeof id === "object") + if (typeof id === "object") { if (id.hasOwnProperty("_rev")) { rev = id._rev; } @@ -1048,6 +1063,7 @@ ArangoCollection.prototype.remove = function (id, overwrite, waitForSync) { } else if (id.hasOwnProperty("_key")) { id = id._key; } + } url = this._documenturl(id); } @@ -1073,6 +1089,7 @@ ArangoCollection.prototype.remove = function (id, overwrite, waitForSync) { return options.silent ? true :requestResult; }; + //////////////////////////////////////////////////////////////////////////////// /// @brief replaces a document in the collection /// @param id the id of the document @@ -1084,29 +1101,72 @@ ArangoCollection.prototype.remove = function (id, overwrite, waitForSync) { /// @example replace("example/996280832675", { a : 1, c : 2}, {waitForSync: false, overwrite: true}) //////////////////////////////////////////////////////////////////////////////// +function fillInSpecial(id, data) { + var pos; + if (data === null || typeof data !== "object" || Array.isArray(data)) { + throw new ArangoError({ + error : true, + errorCode : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message + }); + } + if (typeof id === "object" && id !== null && !Array.isArray(id)) { + if (id.hasOwnProperty("_rev")) { + data._rev = id._rev; + } + if (id.hasOwnProperty("_id")) { + data._id = id._id; + pos = id._id.indexOf("/"); + if (pos >= 0) { + data._key = id._id.substr(pos+1); + } else { + delete data._key; + } + } else if (id.hasOwnProperty("_key")) { + data._key = id._key; + delete data._id; + } else { + throw new ArangoError({ + error : true, + errorCode : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message + }); + } + } else if (typeof id === "string") { + delete data._rev; + pos = id.indexOf("/"); + if (pos >= 0) { + data._id = id; + data._key = id.substr(pos+1); + } else { + data._key = id; + delete data._id; + } + } else { + throw new ArangoError({ + error : true, + errorCode : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, + errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, + errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message + }); + } +} + ArangoCollection.prototype.replace = function (id, data, overwrite, waitForSync) { var rev = null; var requestResult; if (id === undefined || id === null) { throw new ArangoError({ + error : true, + errorCode : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message }); } - if (typeof id === "object") { - if (id.hasOwnProperty("_rev")) { - rev = id._rev; - } - if (id.hasOwnProperty("_id")) { - id = id._id; - } else if (id.hasOwnProperty("_key")) { - id = id._key; - } - } - - var params = ""; var ignoreRevs = false; var options; if (typeof overwrite === "object") { @@ -1127,11 +1187,38 @@ ArangoCollection.prototype.replace = function (id, data, overwrite, waitForSync) } options = {}; } - var url = this._documenturl(id); + + var url; + if (Array.isArray(id)) { + if (!Array.isArray(data) || id.length !== data.length) { + throw new ArangoError({ + error : true, + errorCode : internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, + errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message + }); + } + for (var i = 0; i < id.length; i++) { + fillInSpecial(id[i], data[i]); + } + url = this._documentcollectionurl(); + } else if (typeof id === "object") { + if (id.hasOwnProperty("_rev")) { + rev = id._rev; + } + if (id.hasOwnProperty("_id")) { + id = id._id; + } else if (id.hasOwnProperty("_key")) { + id = id._key; + } + url = this._documenturl(id); + } + url = this._appendSyncParameter(url, waitForSync); - url = this._appendBoolParameter(url, "ignoreRevs", true); + url = this._appendBoolParameter(url, "ignoreRevs", ignoreRevs); url = this._appendBoolParameter(url, "returnOld", options.returnOld); url = this._appendBoolParameter(url, "returnNew", options.returnNew); + if (rev === null || ignoreRevs) { requestResult = this._database._connection.PUT(url, JSON.stringify(data)); } @@ -1175,17 +1262,6 @@ ArangoCollection.prototype.update = function (id, data, overwrite, keepNull, wai }); } - if (typeof id === "object") { - if (id.hasOwnProperty("_rev")) { - rev = id._rev; - } - if (id.hasOwnProperty("_id")) { - id = id._id; - } else if (id.hasOwnProperty("_key")) { - id = id._key; - } - } - var params = ""; var ignoreRevs = false; var options; @@ -1223,9 +1299,32 @@ ArangoCollection.prototype.update = function (id, data, overwrite, keepNull, wai } - var url = this._documenturl(id) + params; + var url; + if (Array.isArray(id)) { + if (!Array.isArray(data) || id.length !== data.length) { + throw new ArangoError({ + errorNum : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorMessage : internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message + }); + } + for (var i = 0; i < id.length; i++) { + fillInSpecial(id[i], data[i]); + } + url = this._documentcollectionurl() + params; + } else if (typeof id === "object") { + if (id.hasOwnProperty("_rev")) { + rev = id._rev; + } + if (id.hasOwnProperty("_id")) { + id = id._id; + } else if (id.hasOwnProperty("_key")) { + id = id._key; + } + url = this._documenturl(id) + params; + } + url = this._appendSyncParameter(url, waitForSync); - url = this._appendBoolParameter(url, "ignoreRevs", true); + url = this._appendBoolParameter(url, "ignoreRevs", ignoreRevs); url = this._appendBoolParameter(url, "returnOld", options.returnOld); url = this._appendBoolParameter(url, "returnNew", options.returnNew); @@ -1426,4 +1525,3 @@ ArangoCollection.prototype.removeByKeys = function (keys) { }; }; - diff --git a/js/client/modules/@arangodb/arango-database.js b/js/client/modules/@arangodb/arango-database.js index bcc1670bc6..3ea8a4e6d1 100644 --- a/js/client/modules/@arangodb/arango-database.js +++ b/js/client/modules/@arangodb/arango-database.js @@ -625,6 +625,14 @@ ArangoDatabase.prototype._remove = function (id, overwrite, waitForSync) { var requestResult; if (typeof id === "object") { + if (Array.isArray(id)) { + throw new ArangoError({ + error: true, + code: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, + errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.code, + errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_HANDLE_BAD.message + }); + } if (id.hasOwnProperty("_rev")) { rev = id._rev; } @@ -689,6 +697,14 @@ ArangoDatabase.prototype._replace = function (id, data, overwrite, waitForSync) var requestResult; if (typeof id === "object") { + if (Array.isArray(id)) { + throw new ArangoError({ + error: true, + code: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message + }); + } if (id.hasOwnProperty("_rev")) { rev = id._rev; } @@ -753,6 +769,14 @@ ArangoDatabase.prototype._update = function (id, data, overwrite, keepNull, wait var requestResult; if (typeof id === "object") { + if (Array.isArray(id)) { + throw new ArangoError({ + error: true, + code: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorNum: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, + errorMessage: internal.errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message + }); + } if (id.hasOwnProperty("_rev")) { rev = id._rev; }