1
0
Fork 0

Merge branch 'aql2' of ssh://github.com/triAGENS/ArangoDB into aql2

This commit is contained in:
James 2014-08-18 14:12:10 +02:00
commit fb1d69bbae
12 changed files with 606 additions and 266 deletions

View File

@ -234,15 +234,21 @@ AstNode* Ast::createNodeInsert (AstNode const* expression,
AstNode* Ast::createNodeUpdate (AstNode const* keyExpression, AstNode* Ast::createNodeUpdate (AstNode const* keyExpression,
AstNode const* docExpression, AstNode const* docExpression,
AstNode const* collection, AstNode const* collection,
AstNode* options) { AstNode const* options) {
AstNode* node = createNode(NODE_TYPE_UPDATE); AstNode* node = createNode(NODE_TYPE_UPDATE);
if (options == nullptr) {
// no options given. now use default options
options = &NopNode;
}
node->addMember(options);
node->addMember(collection); node->addMember(collection);
node->addMember(docExpression); node->addMember(docExpression);
if (keyExpression != nullptr) { if (keyExpression != nullptr) {
node->addMember(keyExpression); node->addMember(keyExpression);
} }
// TODO: handle options
return node; return node;
} }
@ -254,15 +260,21 @@ AstNode* Ast::createNodeUpdate (AstNode const* keyExpression,
AstNode* Ast::createNodeReplace (AstNode const* keyExpression, AstNode* Ast::createNodeReplace (AstNode const* keyExpression,
AstNode const* docExpression, AstNode const* docExpression,
AstNode const* collection, AstNode const* collection,
AstNode* options) { AstNode const* options) {
AstNode* node = createNode(NODE_TYPE_REPLACE); AstNode* node = createNode(NODE_TYPE_REPLACE);
if (options == nullptr) {
// no options given. now use default options
options = &NopNode;
}
node->addMember(options);
node->addMember(collection); node->addMember(collection);
node->addMember(docExpression); node->addMember(docExpression);
if (keyExpression != nullptr) { if (keyExpression != nullptr) {
node->addMember(keyExpression); node->addMember(keyExpression);
} }
// TODO: handle options
return node; return node;
} }

View File

@ -262,7 +262,7 @@ namespace triagens {
AstNode* createNodeUpdate (AstNode const*, AstNode* createNodeUpdate (AstNode const*,
AstNode const*, AstNode const*,
AstNode const*, AstNode const*,
AstNode*); AstNode const*);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST replace node /// @brief create an AST replace node
@ -271,7 +271,7 @@ namespace triagens {
AstNode* createNodeReplace (AstNode const*, AstNode* createNodeReplace (AstNode const*,
AstNode const*, AstNode const*,
AstNode const*, AstNode const*,
AstNode*); AstNode const*);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node /// @brief create an AST collect node

View File

@ -27,6 +27,7 @@
#include "Aql/ExecutionBlock.h" #include "Aql/ExecutionBlock.h"
#include "Basics/StringUtils.h" #include "Basics/StringUtils.h"
#include "Basics/json-utilities.h"
#include "Utils/Exception.h" #include "Utils/Exception.h"
#include "VocBase/vocbase.h" #include "VocBase/vocbase.h"
@ -1877,24 +1878,24 @@ AqlItemBlock* ReturnBlock::getSome (size_t atLeast,
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- class RemoveBlock // --SECTION-- class ModificationBlock
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
RemoveBlock::RemoveBlock (AQL_TRANSACTION_V8* trx, ModificationBlock::ModificationBlock (AQL_TRANSACTION_V8* trx,
RemoveNode const* ep) ModificationNode const* ep)
: ExecutionBlock(trx, ep), : ExecutionBlock(trx, ep),
_collection(ep->_collection) { _collection(ep->_collection) {
} }
RemoveBlock::~RemoveBlock () { ModificationBlock::~ModificationBlock () {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief get some - this accumulates all input and calls the remove() method /// @brief get some - this accumulates all input and calls the work() method
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* RemoveBlock::getSome (size_t atLeast, AqlItemBlock* ModificationBlock::getSome (size_t atLeast,
size_t atMost) { size_t atMost) {
std::vector<AqlItemBlock*> blocks; std::vector<AqlItemBlock*> blocks;
@ -1919,7 +1920,7 @@ AqlItemBlock* RemoveBlock::getSome (size_t atLeast,
blocks.push_back(res); blocks.push_back(res);
} }
remove(blocks); work(blocks);
freeBlocks(blocks); freeBlocks(blocks);
return nullptr; return nullptr;
@ -1930,11 +1931,73 @@ AqlItemBlock* RemoveBlock::getSome (size_t atLeast,
} }
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief resolve a collection name and return cid and document key
////////////////////////////////////////////////////////////////////////////////
int ModificationBlock::resolve (char const* handle,
TRI_voc_cid_t& cid,
std::string& key) const {
char const* p = strchr(handle, TRI_DOCUMENT_HANDLE_SEPARATOR_CHR);
if (p == nullptr || *p == '\0') {
return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD;
}
if (*handle >= '0' && *handle <= '9') {
cid = triagens::basics::StringUtils::uint64(handle, p - handle);
}
else {
std::string const name(handle, p - handle);
cid = _trx->resolver()->getCollectionIdCluster(name);
}
if (cid == 0) {
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
}
key = std::string(p + 1);
return TRI_ERROR_NO_ERROR;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief extract a key from the AqlValue passed
////////////////////////////////////////////////////////////////////////////////
int ModificationBlock::extractKey (AqlValue const& value,
TRI_document_collection_t const* document,
std::string& key) const {
Json member(value.extractArrayMember(_trx, document, TRI_VOC_ATTRIBUTE_KEY));
// TODO: allow _id, too
TRI_json_t const* json = member.json();
if (TRI_IsStringJson(json)) {
key = std::string(json->_value._string.data, json->_value._string.length - 1);
return TRI_ERROR_NO_ERROR;
}
return TRI_ERROR_ARANGO_DOCUMENT_KEY_MISSING;
}
// -----------------------------------------------------------------------------
// --SECTION-- class RemoveBlock
// -----------------------------------------------------------------------------
RemoveBlock::RemoveBlock (AQL_TRANSACTION_V8* trx,
RemoveNode const* ep)
: ModificationBlock(trx, ep) {
}
RemoveBlock::~RemoveBlock () {
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for removing data /// @brief the actual work horse for removing data
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void RemoveBlock::remove (std::vector<AqlItemBlock*>& blocks) { void RemoveBlock::work (std::vector<AqlItemBlock*>& blocks) {
auto ep = static_cast<RemoveNode const*>(getPlanNode()); auto ep = static_cast<RemoveNode const*>(getPlanNode());
auto it = _varOverview->varInfo.find(ep->_inVariable->id); auto it = _varOverview->varInfo.find(ep->_inVariable->id);
TRI_ASSERT(it != _varOverview->varInfo.end()); TRI_ASSERT(it != _varOverview->varInfo.end());
@ -1956,20 +2019,12 @@ void RemoveBlock::remove (std::vector<AqlItemBlock*>& blocks) {
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
AqlValue a = res->getValue(i, registerId); AqlValue a = res->getValue(i, registerId);
char const* key = nullptr; std::string key;
int errorCode = TRI_ERROR_NO_ERROR; int errorCode = TRI_ERROR_NO_ERROR;
if (a.isArray()) { if (a.isArray()) {
// value is an array. now extract the _key attribute // value is an array. now extract the _key attribute
Json member(a.extractArrayMember(_trx, document, "_key")); errorCode = extractKey(a, document, key);
TRI_json_t const* json = member.json();
if (TRI_IsStringJson(json)) {
key = json->_value._string.data;
}
else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_KEY_MISSING;
}
} }
else if (a.isString()) { else if (a.isString()) {
// value is a string // value is a string
@ -1981,10 +2036,10 @@ void RemoveBlock::remove (std::vector<AqlItemBlock*>& blocks) {
if (errorCode == TRI_ERROR_NO_ERROR) { if (errorCode == TRI_ERROR_NO_ERROR) {
// no error. we expect to have a key // no error. we expect to have a key
TRI_ASSERT(key != nullptr);
// all exceptions are caught in _trx->remove()
errorCode = _trx->remove(trxCollection, errorCode = _trx->remove(trxCollection,
std::string(key), key,
0, 0,
TRI_DOC_UPDATE_LAST_WRITE, TRI_DOC_UPDATE_LAST_WRITE,
0, 0,
@ -2013,59 +2068,17 @@ void RemoveBlock::remove (std::vector<AqlItemBlock*>& blocks) {
InsertBlock::InsertBlock (AQL_TRANSACTION_V8* trx, InsertBlock::InsertBlock (AQL_TRANSACTION_V8* trx,
InsertNode const* ep) InsertNode const* ep)
: ExecutionBlock(trx, ep), : ModificationBlock(trx, ep) {
_collection(ep->_collection) {
} }
InsertBlock::~InsertBlock () { InsertBlock::~InsertBlock () {
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief get some - this accumulates all input and calls the insert() method
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* InsertBlock::getSome (size_t atLeast,
size_t atMost) {
std::vector<AqlItemBlock*> blocks;
auto freeBlocks = [](std::vector<AqlItemBlock*>& blocks) {
for (auto it = blocks.begin(); it != blocks.end(); ++it) {
if ((*it) != nullptr) {
delete (*it);
}
}
};
// loop over input until it is exhausted
try {
while (true) {
auto res = ExecutionBlock::getSome(atLeast, atMost);
if (res == nullptr) {
break;
}
blocks.push_back(res);
}
insert(blocks);
freeBlocks(blocks);
return nullptr;
}
catch (...) {
freeBlocks(blocks);
throw;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for inserting data /// @brief the actual work horse for inserting data
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void InsertBlock::insert (std::vector<AqlItemBlock*>& blocks) { void InsertBlock::work (std::vector<AqlItemBlock*>& blocks) {
auto ep = static_cast<InsertNode const*>(getPlanNode()); auto ep = static_cast<InsertNode const*>(getPlanNode());
auto it = _varOverview->varInfo.find(ep->_inVariable->id); auto it = _varOverview->varInfo.find(ep->_inVariable->id);
TRI_ASSERT(it != _varOverview->varInfo.end()); TRI_ASSERT(it != _varOverview->varInfo.end());
@ -2075,30 +2088,6 @@ void InsertBlock::insert (std::vector<AqlItemBlock*>& blocks) {
bool const isEdgeCollection = _collection->isEdgeCollection(); bool const isEdgeCollection = _collection->isEdgeCollection();
auto resolve = [&](char const* handle, TRI_voc_cid_t& cid, std::string& key) -> int {
char const* p = strchr(handle, TRI_DOCUMENT_HANDLE_SEPARATOR_CHR);
if (p == nullptr || *p == '\0') {
return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD;
}
if (*handle >= '0' && *handle <= '9') {
cid = triagens::basics::StringUtils::uint64(handle, p - handle);
}
else {
std::string const name(handle, p - handle);
cid = _trx->resolver()->getCollectionIdCluster(name);
}
if (cid == 0) {
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
}
key = std::string(p + 1);
return TRI_ERROR_NO_ERROR;
};
if (ep->_outVariable == nullptr) { if (ep->_outVariable == nullptr) {
// don't return anything // don't return anything
@ -2129,10 +2118,10 @@ void InsertBlock::insert (std::vector<AqlItemBlock*>& blocks) {
// value is an array // value is an array
if (isEdgeCollection) { if (isEdgeCollection) {
// array must have "_from" and "_to" // array must have _from and _to attributes
TRI_json_t const* json; TRI_json_t const* json;
Json member(a.extractArrayMember(_trx, document, "_from")); Json member(a.extractArrayMember(_trx, document, TRI_VOC_ATTRIBUTE_FROM));
json = member.json(); json = member.json();
if (TRI_IsStringJson(json)) { if (TRI_IsStringJson(json)) {
@ -2143,7 +2132,7 @@ void InsertBlock::insert (std::vector<AqlItemBlock*>& blocks) {
} }
if (errorCode == TRI_ERROR_NO_ERROR) { if (errorCode == TRI_ERROR_NO_ERROR) {
Json member(a.extractArrayMember(_trx, document, "_to")); Json member(a.extractArrayMember(_trx, document, TRI_VOC_ATTRIBUTE_TO));
json = member.json(); json = member.json();
if (TRI_IsStringJson(json)) { if (TRI_IsStringJson(json)) {
errorCode = resolve(json->_value._string.data, edge._toCid, to); errorCode = resolve(json->_value._string.data, edge._toCid, to);
@ -2188,6 +2177,177 @@ void InsertBlock::insert (std::vector<AqlItemBlock*>& blocks) {
} }
} }
// -----------------------------------------------------------------------------
// --SECTION-- class UpdateBlock
// -----------------------------------------------------------------------------
UpdateBlock::UpdateBlock (AQL_TRANSACTION_V8* trx,
UpdateNode const* ep)
: ModificationBlock(trx, ep) {
}
UpdateBlock::~UpdateBlock () {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for inserting data
////////////////////////////////////////////////////////////////////////////////
void UpdateBlock::work (std::vector<AqlItemBlock*>& blocks) {
auto ep = static_cast<UpdateNode const*>(getPlanNode());
auto it = _varOverview->varInfo.find(ep->_inVariable->id);
TRI_ASSERT(it != _varOverview->varInfo.end());
RegisterId const registerId = it->second.registerId;
auto trxCollection = _trx->trxCollection(_collection->cid());
if (ep->_outVariable == nullptr) {
// don't return anything
// loop over all blocks
for (auto it = blocks.begin(); it != blocks.end(); ++it) {
auto res = (*it);
auto document = res->getDocumentCollection(registerId);
size_t const n = res->size();
// loop over the complete block
for (size_t i = 0; i < n; ++i) {
AqlValue a = res->getValue(i, registerId);
int errorCode = TRI_ERROR_NO_ERROR;
std::string key;
if (a.isArray()) {
// value is an array. now extract the _key attribute
errorCode = extractKey(a, document, key);
}
else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
}
if (errorCode == TRI_ERROR_NO_ERROR) {
TRI_doc_mptr_copy_t mptr;
auto json = a.toJson(_trx, document);
// read old document
TRI_doc_mptr_copy_t oldDocument;
errorCode = _trx->readSingle(trxCollection, &oldDocument, key);
if (errorCode == TRI_ERROR_NO_ERROR) {
if (oldDocument.getDataPtr() != nullptr) {
TRI_shaped_json_t shapedJson;
TRI_EXTRACT_SHAPED_JSON_MARKER(shapedJson, oldDocument.getDataPtr()); // PROTECTED by trx here
TRI_json_t* old = TRI_JsonShapedJson(_collection->documentCollection()->getShaper(), &shapedJson);
if (old != nullptr) {
TRI_json_t* patchedJson = TRI_MergeJson(TRI_UNKNOWN_MEM_ZONE, old, json.json(), ep->_options.nullMeansRemove);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, old);
if (patchedJson != nullptr) {
// all exceptions are caught in _trx->update()
errorCode = _trx->update(trxCollection, key, 0, &mptr, patchedJson, TRI_DOC_UPDATE_LAST_WRITE, 0, nullptr, ep->_options.waitForSync);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, patchedJson);
}
else {
errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
}
else {
errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
}
else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
}
}
}
if (errorCode != TRI_ERROR_NO_ERROR &&
! ep->_options.ignoreErrors) {
THROW_ARANGO_EXCEPTION(errorCode);
}
}
// done with a block
// now free it already
(*it) = nullptr;
delete res;
}
}
}
// -----------------------------------------------------------------------------
// --SECTION-- class ReplaceBlock
// -----------------------------------------------------------------------------
ReplaceBlock::ReplaceBlock (AQL_TRANSACTION_V8* trx,
ReplaceNode const* ep)
: ModificationBlock(trx, ep) {
}
ReplaceBlock::~ReplaceBlock () {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for replacing data
////////////////////////////////////////////////////////////////////////////////
void ReplaceBlock::work (std::vector<AqlItemBlock*>& blocks) {
auto ep = static_cast<ReplaceNode const*>(getPlanNode());
auto it = _varOverview->varInfo.find(ep->_inVariable->id);
TRI_ASSERT(it != _varOverview->varInfo.end());
RegisterId const registerId = it->second.registerId;
auto trxCollection = _trx->trxCollection(_collection->cid());
if (ep->_outVariable == nullptr) {
// don't return anything
// loop over all blocks
for (auto it = blocks.begin(); it != blocks.end(); ++it) {
auto res = (*it);
auto document = res->getDocumentCollection(registerId);
size_t const n = res->size();
// loop over the complete block
for (size_t i = 0; i < n; ++i) {
AqlValue a = res->getValue(i, registerId);
int errorCode = TRI_ERROR_NO_ERROR;
std::string key;
if (a.isArray()) {
// value is an array. now extract the _key attribute
errorCode = extractKey(a, document, key);
}
else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
}
if (errorCode == TRI_ERROR_NO_ERROR) {
TRI_doc_mptr_copy_t mptr;
auto json = a.toJson(_trx, document);
// all exceptions are caught in _trx->update()
errorCode = _trx->update(trxCollection, key, 0, &mptr, json.json(), TRI_DOC_UPDATE_LAST_WRITE, 0, nullptr, ep->_options.waitForSync);
}
if (errorCode != TRI_ERROR_NO_ERROR &&
! ep->_options.ignoreErrors) {
THROW_ARANGO_EXCEPTION(errorCode);
}
}
// done with a block
// now free it already
(*it) = nullptr;
delete res;
}
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- struct ExecutionBlock::VarOverview // --SECTION-- struct ExecutionBlock::VarOverview
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -1031,11 +1031,81 @@ namespace triagens {
}; };
// -----------------------------------------------------------------------------
// --SECTION-- ModificationBlock
// -----------------------------------------------------------------------------
class ModificationBlock : public ExecutionBlock {
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
ModificationBlock (AQL_TRANSACTION_V8*, ModificationNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////
virtual ~ModificationBlock ();
////////////////////////////////////////////////////////////////////////////////
/// @brief getSome
////////////////////////////////////////////////////////////////////////////////
virtual AqlItemBlock* getSome (size_t atLeast,
size_t atMost);
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse
////////////////////////////////////////////////////////////////////////////////
virtual void work (std::vector<AqlItemBlock*>&) = 0;
////////////////////////////////////////////////////////////////////////////////
/// @brief resolve a collection name and return cid and document key
////////////////////////////////////////////////////////////////////////////////
int resolve (char const*,
TRI_voc_cid_t&,
std::string&) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief extract a key from the AqlValue passed
////////////////////////////////////////////////////////////////////////////////
int extractKey (AqlValue const&,
TRI_document_collection_t const*,
std::string&) const;
// -----------------------------------------------------------------------------
// --SECTION-- protected variables
// -----------------------------------------------------------------------------
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief collection
////////////////////////////////////////////////////////////////////////////////
Collection* _collection;
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- RemoveBlock // --SECTION-- RemoveBlock
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class RemoveBlock : public ExecutionBlock { class RemoveBlock : public ModificationBlock {
public: public:
@ -1051,36 +1121,17 @@ namespace triagens {
~RemoveBlock (); ~RemoveBlock ();
////////////////////////////////////////////////////////////////////////////////
/// @brief getSome
////////////////////////////////////////////////////////////////////////////////
virtual AqlItemBlock* getSome (size_t atLeast,
size_t atMost);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private methods // --SECTION-- protected methods
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
private: protected:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for removing data /// @brief the actual work horse for removing data
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void remove (std::vector<AqlItemBlock*>&); void work (std::vector<AqlItemBlock*>&);
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief collection
////////////////////////////////////////////////////////////////////////////////
Collection* _collection;
}; };
@ -1088,7 +1139,7 @@ namespace triagens {
// --SECTION-- InsertBlock // --SECTION-- InsertBlock
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class InsertBlock : public ExecutionBlock { class InsertBlock : public ModificationBlock {
public: public:
@ -1104,36 +1155,85 @@ namespace triagens {
~InsertBlock (); ~InsertBlock ();
////////////////////////////////////////////////////////////////////////////////
/// @brief getSome
////////////////////////////////////////////////////////////////////////////////
virtual AqlItemBlock* getSome (size_t atLeast,
size_t atMost);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private methods // --SECTION-- protected methods
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
private: protected:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for inserting data /// @brief the actual work horse for inserting data
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void insert (std::vector<AqlItemBlock*>&); void work (std::vector<AqlItemBlock*>&);
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private variables // --SECTION-- UpdateBlock
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
private: class UpdateBlock : public ModificationBlock {
public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief collection /// @brief constructor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Collection* _collection; UpdateBlock (AQL_TRANSACTION_V8* trx, UpdateNode const* ep);
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////
~UpdateBlock ();
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for updating data
////////////////////////////////////////////////////////////////////////////////
void work (std::vector<AqlItemBlock*>&);
};
// -----------------------------------------------------------------------------
// --SECTION-- ReplaceBlock
// -----------------------------------------------------------------------------
class ReplaceBlock : public ModificationBlock {
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
ReplaceBlock (AQL_TRANSACTION_V8* trx, ReplaceNode const* ep);
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////
~ReplaceBlock ();
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief the actual work horse for replacing data
////////////////////////////////////////////////////////////////////////////////
void work (std::vector<AqlItemBlock*>&);
}; };

View File

@ -159,6 +159,20 @@ struct Instanciator : public WalkerWorker<ExecutionNode> {
root = eb; root = eb;
break; break;
} }
case ExecutionNode::UPDATE: {
eb = new UpdateBlock(engine->getTransaction(),
static_cast<UpdateNode const*>(en));
root = eb;
break;
}
case ExecutionNode::REPLACE: {
eb = new ReplaceBlock(engine->getTransaction(),
static_cast<ReplaceNode const*>(en));
root = eb;
break;
}
default: { default: {
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);

View File

@ -294,7 +294,7 @@ void IndexRangeNode::toJsonHelper (std::map<ExecutionNode*, int>& indexTab,
// put together the range info . . . // put together the range info . . .
Json ranges(Json::List); Json ranges(Json::List);
/*
for (auto x : *_ranges) { for (auto x : *_ranges) {
Json item(Json::Array); Json item(Json::Array);
item("name", Json(x._name)) item("name", Json(x._name))
@ -304,7 +304,7 @@ void IndexRangeNode::toJsonHelper (std::map<ExecutionNode*, int>& indexTab,
("highOpen", Json(x._highOpen)); ("highOpen", Json(x._highOpen));
ranges(item); ranges(item);
} }
*/
// Now put info about vocbase and cid in there // Now put info about vocbase and cid in there
json("database", Json(_vocbase->_name)) json("database", Json(_vocbase->_name))
("collection", Json(_collection->name)) ("collection", Json(_collection->name))
@ -594,8 +594,13 @@ void UpdateNode::toJsonHelper (std::map<ExecutionNode*, int>& indexTab,
// Now put info about vocbase and cid in there // Now put info about vocbase and cid in there
json("database", Json(_vocbase->_name)) json("database", Json(_vocbase->_name))
("collection", Json(_collname)) ("collection", Json(_collection->name))
("outVariable", _outVariable->toJson()); ("inVariable", _inVariable->toJson());
// output variable might be empty
if (_outVariable != nullptr) {
json("outVariable", _outVariable->toJson());
}
// And add it: // And add it:
int len = static_cast<int>(nodes.size()); int len = static_cast<int>(nodes.size());
@ -622,8 +627,13 @@ void ReplaceNode::toJsonHelper (std::map<ExecutionNode*, int>& indexTab,
// Now put info about vocbase and cid in there // Now put info about vocbase and cid in there
json("database", Json(_vocbase->_name)) json("database", Json(_vocbase->_name))
("collection", Json(_collname)) ("collection", Json(_collection->name))
("outVariable", _outVariable->toJson()); ("inVariable", _inVariable->toJson());
// output variable might be empty
if (_outVariable != nullptr) {
json("outVariable", _outVariable->toJson());
}
// And add it: // And add it:
int len = static_cast<int>(nodes.size()); int len = static_cast<int>(nodes.size());

View File

@ -1455,6 +1455,7 @@ namespace triagens {
class ModificationNode : public ExecutionNode { class ModificationNode : public ExecutionNode {
friend class ExecutionBlock; friend class ExecutionBlock;
friend class ModificationBlock;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief constructor with a vocbase and a collection and options /// @brief constructor with a vocbase and a collection and options
@ -1728,7 +1729,7 @@ namespace triagens {
/// @brief class UpdateNode /// @brief class UpdateNode
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class UpdateNode : public ExecutionNode { class UpdateNode : public ModificationNode {
friend class ExecutionBlock; friend class ExecutionBlock;
friend class UpdateBlock; friend class UpdateBlock;
@ -1740,13 +1741,16 @@ namespace triagens {
public: public:
UpdateNode (TRI_vocbase_t* vocbase, UpdateNode (TRI_vocbase_t* vocbase,
std::string collname, Collection* collection,
ModificationOptions const& options,
Variable const* inVariable,
Variable const* outVariable) Variable const* outVariable)
: ExecutionNode(), _vocbase(vocbase), _collname(collname), : ModificationNode(vocbase, collection, options),
_inVariable(inVariable),
_outVariable(outVariable) { _outVariable(outVariable) {
TRI_ASSERT(_vocbase != nullptr); TRI_ASSERT(_inVariable != nullptr);
TRI_ASSERT(_outVariable != nullptr); // _outVariable might be a nullptr
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1770,7 +1774,7 @@ namespace triagens {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
virtual ExecutionNode* clone () const { virtual ExecutionNode* clone () const {
auto c = new UpdateNode(_vocbase, _collname, _outVariable); auto c = new UpdateNode(_vocbase, _collection, _options, _inVariable, _outVariable);
cloneDependencies(c); cloneDependencies(c);
return static_cast<ExecutionNode*>(c); return static_cast<ExecutionNode*>(c);
} }
@ -1784,6 +1788,28 @@ namespace triagens {
return 1000 * _dependencies.at(0)->getCost(); //FIXME change this! return 1000 * _dependencies.at(0)->getCost(); //FIXME change this!
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
std::vector<Variable const*> v;
if (_outVariable != nullptr) {
v.push_back(_outVariable);
}
return v;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private variables // --SECTION-- private variables
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -1791,16 +1817,10 @@ namespace triagens {
private: private:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief _vocbase, the database /// @brief input variable
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TRI_vocbase_t* _vocbase; Variable const* _inVariable;
////////////////////////////////////////////////////////////////////////////////
/// @brief _collname, the collection name
////////////////////////////////////////////////////////////////////////////////
std::string _collname;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief output variable /// @brief output variable
@ -1818,7 +1838,7 @@ namespace triagens {
/// @brief class ReplaceNode /// @brief class ReplaceNode
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class ReplaceNode : public ExecutionNode { class ReplaceNode : public ModificationNode {
friend class ExecutionBlock; friend class ExecutionBlock;
friend class ReplaceBlock; friend class ReplaceBlock;
@ -1830,13 +1850,16 @@ namespace triagens {
public: public:
ReplaceNode (TRI_vocbase_t* vocbase, ReplaceNode (TRI_vocbase_t* vocbase,
std::string collname, Collection* collection,
ModificationOptions const& options,
Variable const* inVariable,
Variable const* outVariable) Variable const* outVariable)
: ExecutionNode(), _vocbase(vocbase), _collname(collname), : ModificationNode(vocbase, collection, options),
_inVariable(inVariable),
_outVariable(outVariable) { _outVariable(outVariable) {
TRI_ASSERT(_vocbase != nullptr); TRI_ASSERT(_inVariable != nullptr);
TRI_ASSERT(_outVariable != nullptr); // _outVariable might be a nullptr
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1860,7 +1883,7 @@ namespace triagens {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
virtual ExecutionNode* clone () const { virtual ExecutionNode* clone () const {
auto c = new UpdateNode(_vocbase, _collname, _outVariable); auto c = new ReplaceNode(_vocbase, _collection, _options, _inVariable, _outVariable);
cloneDependencies(c); cloneDependencies(c);
return static_cast<ExecutionNode*>(c); return static_cast<ExecutionNode*>(c);
} }
@ -1874,6 +1897,28 @@ namespace triagens {
return 1000 * _dependencies.at(0)->getCost(); //FIXME change this! return 1000 * _dependencies.at(0)->getCost(); //FIXME change this!
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesUsedHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () {
std::vector<Variable const*> v;
v.push_back(_inVariable);
return v;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief getVariablesSetHere
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesSetHere () {
std::vector<Variable const*> v;
if (_outVariable != nullptr) {
v.push_back(_outVariable);
}
return v;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private variables // --SECTION-- private variables
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -1881,16 +1926,10 @@ namespace triagens {
private: private:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief _vocbase, the database /// @brief input variable
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TRI_vocbase_t* _vocbase; Variable const* _inVariable;
////////////////////////////////////////////////////////////////////////////////
/// @brief _collname, the collection name
////////////////////////////////////////////////////////////////////////////////
std::string _collname;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief output variable /// @brief output variable

View File

@ -120,7 +120,6 @@ ModificationOptions ExecutionPlan::createOptions (AstNode const* node) {
auto name = member->getStringValue(); auto name = member->getStringValue();
auto value = member->getMember(0); auto value = member->getMember(0);
std::cout << "VALUE: " << value->typeString() << "\n";
TRI_ASSERT(value->isConstant()); TRI_ASSERT(value->isConstant());
if (strcmp(name, "waitForSync") == 0) { if (strcmp(name, "waitForSync") == 0) {
@ -129,6 +128,10 @@ std::cout << "VALUE: " << value->typeString() << "\n";
else if (strcmp(name, "ignoreErrors") == 0) { else if (strcmp(name, "ignoreErrors") == 0) {
options.ignoreErrors = value->toBoolean(); options.ignoreErrors = value->toBoolean();
} }
else if (strcmp(name, "keepNull") == 0) {
// nullMeansRemove is the opposite of keepNull
options.nullMeansRemove = (! value->toBoolean());
}
} }
} }
} }
@ -597,26 +600,27 @@ ExecutionNode* ExecutionPlan::fromNodeUpdate (Ast const* ast,
ExecutionNode* previous, ExecutionNode* previous,
AstNode const* node) { AstNode const* node) {
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_UPDATE); TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_UPDATE);
TRI_ASSERT(node->numMembers() >= 2); TRI_ASSERT(node->numMembers() >= 3);
auto collection = node->getMember(0); auto options = createOptions(node->getMember(0));
auto expression = node->getMember(1); char const* collectionName = node->getMember(1)->getStringValue();
auto collections = ast->query()->collections();
// collection, expression auto collection = collections->get(collectionName);
char const* collectionName = collection->getStringValue(); auto expression = node->getMember(2);
// auto keyExpression = node->getMember(3);
ExecutionNode* en = nullptr; ExecutionNode* en = nullptr;
if (expression->type == NODE_TYPE_REFERENCE) { if (expression->type == NODE_TYPE_REFERENCE) {
// operand is already a variable // operand is already a variable
auto v = static_cast<Variable*>(expression->getData()); auto v = static_cast<Variable*>(expression->getData());
TRI_ASSERT(v != nullptr); TRI_ASSERT(v != nullptr);
en = addNode(new UpdateNode(ast->query()->vocbase(), std::string(collectionName), v)); en = addNode(new UpdateNode(ast->query()->vocbase(), collection, options, v, nullptr));
} }
else { else {
// operand is some misc expression // operand is some misc expression
auto calc = createTemporaryCalculation(ast, expression); auto calc = createTemporaryCalculation(ast, expression);
calc->addDependency(previous); calc->addDependency(previous);
en = addNode(new UpdateNode(ast->query()->vocbase(), std::string(collectionName), calc->outVariable())); en = addNode(new UpdateNode(ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr));
previous = calc; previous = calc;
} }
@ -631,26 +635,27 @@ ExecutionNode* ExecutionPlan::fromNodeReplace (Ast const* ast,
ExecutionNode* previous, ExecutionNode* previous,
AstNode const* node) { AstNode const* node) {
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_REPLACE); TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_REPLACE);
TRI_ASSERT(node->numMembers() >= 2); TRI_ASSERT(node->numMembers() >= 3);
auto collection = node->getMember(0); auto options = createOptions(node->getMember(0));
auto expression = node->getMember(1); char const* collectionName = node->getMember(1)->getStringValue();
auto collections = ast->query()->collections();
// collection, expression auto collection = collections->get(collectionName);
char const* collectionName = collection->getStringValue(); auto expression = node->getMember(2);
// auto keyExpression = node->getMember(3);
ExecutionNode* en = nullptr; ExecutionNode* en = nullptr;
if (expression->type == NODE_TYPE_REFERENCE) { if (expression->type == NODE_TYPE_REFERENCE) {
// operand is already a variable // operand is already a variable
auto v = static_cast<Variable*>(expression->getData()); auto v = static_cast<Variable*>(expression->getData());
TRI_ASSERT(v != nullptr); TRI_ASSERT(v != nullptr);
en = addNode(new ReplaceNode(ast->query()->vocbase(), std::string(collectionName), v)); en = addNode(new ReplaceNode(ast->query()->vocbase(), collection, options, v, nullptr));
} }
else { else {
// operand is some misc expression // operand is some misc expression
auto calc = createTemporaryCalculation(ast, expression); auto calc = createTemporaryCalculation(ast, expression);
calc->addDependency(previous); calc->addDependency(previous);
en = addNode(new ReplaceNode(ast->query()->vocbase(), std::string(collectionName), calc->outVariable())); en = addNode(new ReplaceNode(ast->query()->vocbase(), collection, options, calc->outVariable(), nullptr));
previous = calc; previous = calc;
} }
@ -723,13 +728,11 @@ ExecutionNode* ExecutionPlan::fromNode (Ast const* ast,
} }
case NODE_TYPE_UPDATE: { case NODE_TYPE_UPDATE: {
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
en = fromNodeUpdate(ast, en, member); en = fromNodeUpdate(ast, en, member);
break; break;
} }
case NODE_TYPE_REPLACE: { case NODE_TYPE_REPLACE: {
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
en = fromNodeReplace(ast, en, member); en = fromNodeReplace(ast, en, member);
break; break;
} }

View File

@ -49,7 +49,8 @@ namespace triagens {
ModificationOptions () ModificationOptions ()
: ignoreErrors(false), : ignoreErrors(false),
waitForSync(false) { waitForSync(false),
nullMeansRemove(false) {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -58,6 +59,7 @@ namespace triagens {
bool ignoreErrors; bool ignoreErrors;
bool waitForSync; bool waitForSync;
bool nullMeansRemove;
}; };

View File

@ -637,8 +637,8 @@ namespace triagens {
#define YY_INPUT(resultBuffer, resultState, maxBytesToRead) { \ #define YY_INPUT(resultBuffer, resultState, maxBytesToRead) { \
size_t length = yyextra->remainingLength(); \ size_t length = yyextra->remainingLength(); \
if (length > maxBytesToRead) { \ if (length > static_cast<size_t>(maxBytesToRead)) { \
length = maxBytesToRead; \ length = static_cast<size_t>(maxBytesToRead); \
} \ } \
if (length > 0) { \ if (length > 0) { \
yyextra->fillBuffer(resultBuffer, length); \ yyextra->fillBuffer(resultBuffer, length); \

View File

@ -38,8 +38,8 @@ namespace triagens {
#define YY_INPUT(resultBuffer, resultState, maxBytesToRead) { \ #define YY_INPUT(resultBuffer, resultState, maxBytesToRead) { \
size_t length = yyextra->remainingLength(); \ size_t length = yyextra->remainingLength(); \
if (length > maxBytesToRead) { \ if (length > static_cast<size_t>(maxBytesToRead)) { \
length = maxBytesToRead; \ length = static_cast<size_t>(maxBytesToRead); \
} \ } \
if (length > 0) { \ if (length > 0) { \
yyextra->fillBuffer(resultBuffer, length); \ yyextra->fillBuffer(resultBuffer, length); \

View File

@ -495,6 +495,74 @@ namespace triagens {
return res; return res;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief update a single document, using JSON
////////////////////////////////////////////////////////////////////////////////
int update (TRI_transaction_collection_t* trxCollection,
std::string const& key,
TRI_voc_rid_t rid,
TRI_doc_mptr_copy_t* mptr,
TRI_json_t* const json,
TRI_doc_update_policy_e policy,
TRI_voc_rid_t expectedRevision,
TRI_voc_rid_t* actualRevision,
bool forceSync) {
TRI_shaper_t* shaper = this->shaper(trxCollection);
TRI_memory_zone_t* zone = shaper->_memoryZone;
TRI_shaped_json_t* shaped = TRI_ShapedJsonJson(shaper, json, true, isLocked(trxCollection, TRI_TRANSACTION_WRITE));
if (shaped == nullptr) {
return TRI_ERROR_ARANGO_SHAPER_FAILED;
}
if (orderBarrier(trxCollection) == nullptr) {
return TRI_ERROR_OUT_OF_MEMORY;
}
int res = update(trxCollection,
key,
rid,
mptr,
shaped,
policy,
expectedRevision,
actualRevision,
forceSync);
TRI_FreeShapedJson(zone, shaped);
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief read a single document, identified by key
////////////////////////////////////////////////////////////////////////////////
int readSingle (TRI_transaction_collection_t* trxCollection,
TRI_doc_mptr_copy_t* mptr,
std::string const& key) {
TRI_ASSERT(mptr != nullptr);
if (orderBarrier(trxCollection) == nullptr) {
return TRI_ERROR_OUT_OF_MEMORY;
}
try {
return TRI_ReadShapedJsonDocumentCollection(trxCollection,
(TRI_voc_key_t) key.c_str(),
mptr,
! isLocked(trxCollection, TRI_TRANSACTION_READ));
}
catch (triagens::arango::Exception const& ex) {
return ex.code();
}
catch (...) {
return TRI_ERROR_INTERNAL;
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- protected methods // --SECTION-- protected methods
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -696,34 +764,6 @@ namespace triagens {
return TRI_ERROR_NO_ERROR; return TRI_ERROR_NO_ERROR;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief read a single document, identified by key
////////////////////////////////////////////////////////////////////////////////
int readSingle (TRI_transaction_collection_t* trxCollection,
TRI_doc_mptr_copy_t* mptr,
std::string const& key) {
TRI_ASSERT(mptr != nullptr);
if (orderBarrier(trxCollection) == nullptr) {
return TRI_ERROR_OUT_OF_MEMORY;
}
try {
return TRI_ReadShapedJsonDocumentCollection(trxCollection,
(TRI_voc_key_t) key.c_str(),
mptr,
! isLocked(trxCollection, TRI_TRANSACTION_READ));
}
catch (triagens::arango::Exception const& ex) {
return ex.code();
}
catch (...) {
return TRI_ERROR_INTERNAL;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief read all documents /// @brief read all documents
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -997,46 +1037,6 @@ namespace triagens {
} }
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief update a single document, using JSON
////////////////////////////////////////////////////////////////////////////////
int update (TRI_transaction_collection_t* trxCollection,
std::string const& key,
TRI_voc_rid_t rid,
TRI_doc_mptr_copy_t* mptr,
TRI_json_t* const json,
TRI_doc_update_policy_e policy,
TRI_voc_rid_t expectedRevision,
TRI_voc_rid_t* actualRevision,
bool forceSync) {
TRI_shaper_t* shaper = this->shaper(trxCollection);
TRI_memory_zone_t* zone = shaper->_memoryZone;
TRI_shaped_json_t* shaped = TRI_ShapedJsonJson(shaper, json, true, isLocked(trxCollection, TRI_TRANSACTION_WRITE));
if (shaped == nullptr) {
return TRI_ERROR_ARANGO_SHAPER_FAILED;
}
if (orderBarrier(trxCollection) == nullptr) {
return TRI_ERROR_OUT_OF_MEMORY;
}
int res = update(trxCollection,
key,
rid,
mptr,
shaped,
policy,
expectedRevision,
actualRevision,
forceSync);
TRI_FreeShapedJson(zone, shaped);
return res;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief update a single document, using shaped json /// @brief update a single document, using shaped json
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////