//////////////////////////////////////////////////////////////////////////////// /// @brief Infrastructure for ExecutionPlans /// /// @file arangod/Aql/ExecutionNode.cpp /// /// DISCLAIMER /// /// Copyright 2010-2014 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Max Neunhoeffer /// @author Copyright 2014, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "Aql/ExecutionNode.h" #include "Aql/Collection.h" #include "Aql/WalkerWorker.h" #include "Aql/Ast.h" using namespace triagens::basics; using namespace triagens::aql; const static bool Optional = true; // ----------------------------------------------------------------------------- // --SECTION-- static initialization // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief type names //////////////////////////////////////////////////////////////////////////////// std::unordered_map const ExecutionNode::TypeNames{ { static_cast(ILLEGAL), "ExecutionNode (abstract)" }, { static_cast(SINGLETON), "SingletonNode" }, { static_cast(ENUMERATE_COLLECTION), "EnumerateCollectionNode" }, { static_cast(ENUMERATE_LIST), "EnumerateListNode" }, { static_cast(INDEX_RANGE), "IndexRangeNode" }, { static_cast(LIMIT), "LimitNode" }, { static_cast(CALCULATION), "CalculationNode" }, { static_cast(SUBQUERY), "SubqueryNode" }, { static_cast(FILTER), "FilterNode" }, { static_cast(SORT), "SortNode" }, { static_cast(AGGREGATE), "AggregateNode" }, { static_cast(RETURN), "ReturnNode" }, { static_cast(REMOVE), "RemoveNode" }, { static_cast(INSERT), "InsertNode" }, { static_cast(UPDATE), "UpdateNode" }, { static_cast(REPLACE), "ReplaceNode" }, { static_cast(NORESULTS), "NoResultsNode" } }; // ----------------------------------------------------------------------------- // --SECTION-- methods of ExecutionNode // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief returns the type name of the node //////////////////////////////////////////////////////////////////////////////// const std::string& ExecutionNode::getTypeString () const { auto it = TypeNames.find(static_cast(getType())); if (it != TypeNames.end()) { return (*it).second; } THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "missing type in TypeNames"); } void ExecutionNode::validateType (int type) { auto it = TypeNames.find(static_cast(type)); if (it == TypeNames.end()) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "unknown TypeID"); } } ExecutionNode* ExecutionNode::fromJsonFactory (Ast* ast, Json const& oneNode) { auto JsonString = oneNode.toString(); int nodeTypeID = JsonHelper::getNumericValue(oneNode.json(), "typeID", 0); validateType(nodeTypeID); NodeType nodeType = (NodeType) nodeTypeID; switch (nodeType) { case SINGLETON: return new SingletonNode(ast, oneNode); case ENUMERATE_COLLECTION: return new EnumerateCollectionNode(ast, oneNode); case ENUMERATE_LIST: return new EnumerateListNode(ast, oneNode); case FILTER: return new FilterNode(ast, oneNode); case LIMIT: return new LimitNode(ast, oneNode); case CALCULATION: return new CalculationNode(ast, oneNode); case SUBQUERY: return new SubqueryNode(ast, oneNode); case SORT: { Json jsonElements = oneNode.get("elements"); if (! jsonElements.isList()){ THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unexpected value for SortNode elements"); } size_t len = jsonElements.size(); std::vector> elements; elements.reserve(len); for (size_t i = 0; i < len; i++) { Json oneJsonElement = jsonElements.at(i); bool ascending = JsonHelper::getBooleanValue(oneJsonElement.json(), "ascending", false); Variable *v = varFromJson(ast, oneJsonElement, "inVariable"); elements.push_back(std::make_pair(v, ascending)); } return new SortNode(ast, oneNode, elements); } case AGGREGATE: { Variable *outVariable = varFromJson(ast, oneNode, "outVariable", Optional); Json jsonAaggregates = oneNode.get("aggregates"); if (! jsonAaggregates.isList()){ THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "missing node type in valueTypeNames"); } int len = jsonAaggregates.size(); std::vector> aggregateVariables; aggregateVariables.reserve(len); for (int i = 0; i < len; i++) { Json oneJsonAggregate = jsonAaggregates.at(i); Variable* outVariable = varFromJson(ast, oneJsonAggregate, "outVariable"); Variable* inVariable = varFromJson(ast, oneJsonAggregate, "inVariable"); aggregateVariables.push_back(std::make_pair(outVariable, inVariable)); } return new AggregateNode(ast, oneNode, outVariable, ast->variables()->variables(false), aggregateVariables); } case INSERT: return new InsertNode(ast, oneNode); case REMOVE: return new RemoveNode(ast, oneNode); case REPLACE: return new ReplaceNode(ast, oneNode); case UPDATE: return new UpdateNode(ast, oneNode); case RETURN: return new ReturnNode(ast, oneNode); case NORESULTS: return new NoResultsNode(ast, oneNode); case INTERSECTION: case PROJECTION: case LOOKUP_JOIN: case MERGE_JOIN: case LOOKUP_INDEX_UNIQUE: case LOOKUP_INDEX_RANGE: case LOOKUP_FULL_COLLECTION: case CONCATENATION: case INDEX_RANGE: case MERGE: case REMOTE: // TODO: handle these types of nodes THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "unhandled node type"); case ILLEGAL: THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "invalid node type"); } return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief create an ExecutionNode from JSON //////////////////////////////////////////////////////////////////////////////// ExecutionNode::ExecutionNode (triagens::basics::Json const& json) : ExecutionNode(JsonHelper::getNumericValue(json.json(), "id", 0), JsonHelper::getNumericValue(json.json(), "estimatedCost", 0.0)) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, export an ExecutionNode to JSON //////////////////////////////////////////////////////////////////////////////// Json ExecutionNode::toJson (TRI_memory_zone_t* zone) { Json json; Json nodes; try { nodes = Json(Json::List, 10); toJsonHelper(nodes, zone); json = Json(Json::Array, 1) ("nodes", nodes); } catch (std::exception& e) { return Json(); } return json; } //////////////////////////////////////////////////////////////////////////////// /// @brief convert to a string, basically for debugging purposes //////////////////////////////////////////////////////////////////////////////// void ExecutionNode::appendAsString (std::string& st, int indent) { for (int i = 0; i < indent; i++) { st.push_back(' '); } st.push_back('<'); st.append(getTypeString()); if (_dependencies.size() != 0) { st.push_back('\n'); for (size_t i = 0; i < _dependencies.size(); i++) { _dependencies[i]->appendAsString(st, indent + 2); if (i != _dependencies.size() - 1) { st.push_back(','); } else { st.push_back(' '); } } } st.push_back('>'); } //////////////////////////////////////////////////////////////////////////////// /// @brief functionality to walk an execution plan recursively //////////////////////////////////////////////////////////////////////////////// void ExecutionNode::walk (WalkerWorker* worker) { // Only do every node exactly once: if (worker->done(this)) { return; } worker->before(this); // Now the children in their natural order: for (auto it = _dependencies.begin(); it != _dependencies.end(); ++it) { (*it)->walk(worker); } // Now handle a subquery: if (getType() == SUBQUERY) { auto p = static_cast(this); if (worker->enterSubquery(this, p->getSubquery())) { p->getSubquery()->walk(worker); worker->leaveSubquery(this, p->getSubquery()); } } worker->after(this); } // ----------------------------------------------------------------------------- // --SECTION-- protected methods // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief factory for (optional) variables from json. //////////////////////////////////////////////////////////////////////////////// Variable* ExecutionNode::varFromJson (Ast* ast, triagens::basics::Json const& base, const char *variableName, bool optional) { Json variableJson = base.get(variableName); if (variableJson.isEmpty()) { if (optional) { return nullptr; } else { std::string msg; msg += "Mandatory variable \"" + std::string(variableName) + "\"not found."; THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, msg.c_str()); } } else { return ast->variables()->createVariable(variableJson); } } //////////////////////////////////////////////////////////////////////////////// /// @brief toJsonHelper, for a generic node //////////////////////////////////////////////////////////////////////////////// Json ExecutionNode::toJsonHelperGeneric (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { size_t const n = _dependencies.size(); for (size_t i = 0; i < n; i++) { _dependencies[i]->toJsonHelper(nodes, zone); } Json json; json = Json(Json::Array, 2) ("type", Json(getTypeString())); json ("typeID", Json(static_cast(getType()))); Json deps(Json::List, n); for (size_t i = 0; i < n; i++) { deps(Json(static_cast(_dependencies[i]->id()))); } json("dependencies", deps); json("id", Json(static_cast(id()))); if(this->_estimatedCost != 0){ json("estimatedCost", Json(this->_estimatedCost)); } return json; } // ----------------------------------------------------------------------------- // --SECTION-- methods of SingletonNode // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for SingletonNode //////////////////////////////////////////////////////////////////////////////// SingletonNode::SingletonNode (Ast* ast, basics::Json const& base) : ExecutionNode(base) { } void SingletonNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of EnumerateCollectionNode // ----------------------------------------------------------------------------- EnumerateCollectionNode::EnumerateCollectionNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _vocbase(ast->query()->vocbase()), _collection(ast->query()->collections()->add(JsonHelper::getStringValue(base.json(), "collection", ""), TRI_TRANSACTION_READ)), _outVariable(varFromJson(ast, base, "outVariable")) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for EnumerateCollectionNode //////////////////////////////////////////////////////////////////////////////// void EnumerateCollectionNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("outVariable", _outVariable->toJson()); // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of EnumerateListNode // ----------------------------------------------------------------------------- EnumerateListNode::EnumerateListNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _inVariable(varFromJson(ast, base, "inVariable")), _outVariable(varFromJson(ast, base, "outVariable")) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for EnumerateListNode //////////////////////////////////////////////////////////////////////////////// void EnumerateListNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } json("inVariable", _inVariable->toJson()) ("outVariable", _outVariable->toJson()); // And add it: nodes(json); } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for IndexRangeNode //////////////////////////////////////////////////////////////////////////////// void IndexRangeNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // put together the range info . . . Json ranges(Json::List); for (auto x : *_ranges) { ranges(x->toJson()); } // Now put info about vocbase and cid in there /*json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("outVariable", _outVariable->toJson()) ("index", _index->index()->json(_index->index())) ("ranges", ranges);*/ // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of LimitNode // ----------------------------------------------------------------------------- LimitNode::LimitNode (Ast* ast, basics::Json const& base) : ExecutionNode(base) { _offset = JsonHelper::getNumericValue(base.json(), "offset", 0.0); _limit = JsonHelper::getNumericValue(base.json(), "limit", 0.0); } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for LimitNode //////////////////////////////////////////////////////////////////////////////// void LimitNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // Now put info about offset and limit in json("offset", Json(static_cast(_offset))) ("limit", Json(static_cast(_limit))); // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of CalculationNode // ----------------------------------------------------------------------------- CalculationNode::CalculationNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _expression(new Expression(ast, base)), _outVariable(varFromJson(ast, base, "outVariable")) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for CalculationNode //////////////////////////////////////////////////////////////////////////////// void CalculationNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } json("expression", _expression->toJson(TRI_UNKNOWN_MEM_ZONE)) ("outVariable", _outVariable->toJson()) ("canThrow", Json(_expression->canThrow())); // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of SubqueryNode // ----------------------------------------------------------------------------- SubqueryNode::SubqueryNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _subquery(nullptr), _outVariable(varFromJson(ast, base, "outVariable")) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for SubqueryNode //////////////////////////////////////////////////////////////////////////////// void SubqueryNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } json("subquery", _subquery->toJson(TRI_UNKNOWN_MEM_ZONE)) ("outVariable", _outVariable->toJson()); // And add it: nodes(json); } //////////////////////////////////////////////////////////////////////////////// /// @brief helper struct to find all (outer) variables used in a SubqueryNode //////////////////////////////////////////////////////////////////////////////// struct SubqueryVarUsageFinder : public WalkerWorker { std::unordered_set _usedLater; std::unordered_set _valid; SubqueryVarUsageFinder () { } ~SubqueryVarUsageFinder () { } void before (ExecutionNode* en) { // Add variables used here to _usedLater: auto&& usedHere = en->getVariablesUsedHere(); for (auto v : usedHere) { _usedLater.insert(v); } } void after (ExecutionNode* en) { // Add variables set here to _valid: auto&& setHere = en->getVariablesSetHere(); for (auto v : setHere) { _valid.insert(v); } } bool enterSubquery (ExecutionNode* super, ExecutionNode* sub) { SubqueryVarUsageFinder subfinder; sub->walk(&subfinder); // keep track of all variables used by a (dependent) subquery // this is, all variables in the subqueries _usedLater that are not in _valid // create the set difference. note: cannot use std::set_difference as our sets are NOT sorted for (auto it = subfinder._usedLater.begin(); it != subfinder._usedLater.end(); ++it) { if (_valid.find(*it) != _valid.end()) { _usedLater.insert((*it)); } } return false; } }; //////////////////////////////////////////////////////////////////////////////// /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// std::vector SubqueryNode::getVariablesUsedHere () { SubqueryVarUsageFinder finder; _subquery->walk(&finder); std::vector v; for (auto it = finder._usedLater.begin(); it != finder._usedLater.end(); ++it) { if (finder._valid.find(*it) == finder._valid.end()) { v.push_back((*it)); } } return v; } // ----------------------------------------------------------------------------- // --SECTION-- methods of FilterNode // ----------------------------------------------------------------------------- FilterNode::FilterNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _inVariable(varFromJson(ast, base, "inVariable")), _resultIsEmpty(false) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for FilterNode //////////////////////////////////////////////////////////////////////////////// void FilterNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } json("inVariable", _inVariable->toJson()); // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of SortNode // ----------------------------------------------------------------------------- SortNode::SortNode (Ast* ast, basics::Json const& base, std::vector> elements) : ExecutionNode(base), _elements(elements) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for SortNode //////////////////////////////////////////////////////////////////////////////// void SortNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } Json values(Json::List, _elements.size()); for (auto it = _elements.begin(); it != _elements.end(); ++it) { Json element(Json::Array); element("inVariable", (*it).first->toJson()) ("ascending", Json((*it).second)); values(element); } json("elements", values); // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of AggregateNode // ----------------------------------------------------------------------------- AggregateNode::AggregateNode (Ast* ast, basics::Json const& base, Variable const* outVariable, std::unordered_map const& variableMap, std::vector> aggregateVariables) : ExecutionNode(base), _aggregateVariables(aggregateVariables), _outVariable(outVariable), _variableMap(variableMap) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for AggregateNode //////////////////////////////////////////////////////////////////////////////// void AggregateNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } Json values(Json::List, _aggregateVariables.size()); for (auto it = _aggregateVariables.begin(); it != _aggregateVariables.end(); ++it) { Json variable(Json::Array); variable("outVariable", (*it).first->toJson()) ("inVariable", (*it).second->toJson()); values(variable); } json("aggregates", values); // output variable might be empty if (_outVariable != nullptr) { json("outVariable", _outVariable->toJson()); } // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of ReturnNode // ----------------------------------------------------------------------------- ReturnNode::ReturnNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _inVariable(varFromJson(ast, base, "inVariable")) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for ReturnNode //////////////////////////////////////////////////////////////////////////////// void ReturnNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } json("inVariable", _inVariable->toJson()); // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- ModificationNode // ----------------------------------------------------------------------------- ModificationNode::ModificationNode (Ast* ast, basics::Json const& base) : ExecutionNode(base), _vocbase(ast->query()->vocbase()), _collection(ast->query()->collections()->add(JsonHelper::getStringValue(base.json(), "collection", ""), TRI_TRANSACTION_WRITE)), _options(base) { TRI_ASSERT(_vocbase != nullptr); TRI_ASSERT(_collection != nullptr); } // ----------------------------------------------------------------------------- // --SECTION-- methods of RemoveNode // ----------------------------------------------------------------------------- RemoveNode::RemoveNode (Ast* ast, basics::Json const& base) : ModificationNode(ast, base), _inVariable(varFromJson(ast, base, "inVariable")), _outVariable(varFromJson(ast, base, "outVariable", Optional)) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson //////////////////////////////////////////////////////////////////////////////// void RemoveNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("inVariable", _inVariable->toJson()); // output variable might be empty if (_outVariable != nullptr) { json("outVariable", _outVariable->toJson()); } // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of InsertNode // ----------------------------------------------------------------------------- InsertNode::InsertNode (Ast* ast, basics::Json const& base) : ModificationNode(ast, base), _inVariable(varFromJson(ast, base, "inVariable")), _outVariable(varFromJson(ast, base, "outVariable", Optional)) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson //////////////////////////////////////////////////////////////////////////////// void InsertNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("inVariable", _inVariable->toJson()); // output variable might be empty if (_outVariable != nullptr) { json("outVariable", _outVariable->toJson()); } // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of UpdateNode // ----------------------------------------------------------------------------- UpdateNode::UpdateNode (Ast* ast, basics::Json const& base) : ModificationNode(ast, base), _inDocVariable(varFromJson(ast, base, "inDocVariable")), _inKeyVariable(varFromJson(ast, base, "inKeyVariable", Optional)), _outVariable(varFromJson(ast, base, "outVariable", Optional)) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson //////////////////////////////////////////////////////////////////////////////// void UpdateNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("inDocVariable", _inDocVariable->toJson()); // inKeyVariable might be empty if (_inKeyVariable != nullptr) { json("inKeyVariable", _inKeyVariable->toJson()); } // output variable might be empty if (_outVariable != nullptr) { json("outVariable", _outVariable->toJson()); } // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of ReplaceNode // ----------------------------------------------------------------------------- ReplaceNode::ReplaceNode (Ast* ast, basics::Json const& base) : ModificationNode(ast, base), _inDocVariable(varFromJson(ast, base, "inDocVariable")), _inKeyVariable(varFromJson(ast, base, "inKeyVariable", Optional)), _outVariable(varFromJson(ast, base, "outVariable", Optional)) { } //////////////////////////////////////////////////////////////////////////////// /// @brief toJson //////////////////////////////////////////////////////////////////////////////// void ReplaceNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // Now put info about vocbase and cid in there json("database", Json(_vocbase->_name)) ("collection", Json(_collection->name)) ("inDocVariable", _inDocVariable->toJson()); // inKeyVariable might be empty if (_inKeyVariable != nullptr) { json("inKeyVariable", _inKeyVariable->toJson()); } // output variable might be empty if (_outVariable != nullptr) { json("outVariable", _outVariable->toJson()); } // And add it: nodes(json); } // ----------------------------------------------------------------------------- // --SECTION-- methods of NoResultsNode // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief toJson, for NoResultsNode //////////////////////////////////////////////////////////////////////////////// void NoResultsNode::toJsonHelper (triagens::basics::Json& nodes, TRI_memory_zone_t* zone) { Json json(ExecutionNode::toJsonHelperGeneric(nodes, zone)); // call base class method if (json.isEmpty()) { return; } // And add it: nodes(json); } // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: