1
0
Fork 0

refactoring for Aql COLLECT

This commit is contained in:
Jan Steemann 2016-01-07 13:35:12 +01:00
parent d58d1dd9dc
commit ad1163e3bb
35 changed files with 2150 additions and 1783 deletions

View File

@ -573,6 +573,26 @@ AstNode* Ast::createNodeCollectCount(AstNode const* list, char const* name,
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node, AGGREGATE
////////////////////////////////////////////////////////////////////////////////
AstNode* Ast::createNodeCollectAggregate(AstNode const* list, AstNode const* aggregations,
AstNode const* options) {
AstNode* node = createNode(NODE_TYPE_COLLECT_AGGREGATE);
if (options == nullptr) {
// no options given. now use default options
options = &NopNode;
}
node->addMember(options);
node->addMember(list);
node->addMember(aggregations);
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST sort node
////////////////////////////////////////////////////////////////////////////////

View File

@ -314,6 +314,12 @@ class Ast {
AstNode* createNodeCollectCount(AstNode const*, char const*, size_t length,
AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node, AGGREGATE
////////////////////////////////////////////////////////////////////////////////
AstNode* createNodeCollectAggregate(AstNode const*, AstNode const*, AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST sort node

View File

@ -158,7 +158,8 @@ std::unordered_map<int, std::string const> const AstNode::TypeNames{
{static_cast<int>(NODE_TYPE_DIRECTION), "direction"},
{static_cast<int>(NODE_TYPE_COLLECTION_LIST), "collection list"},
{static_cast<int>(NODE_TYPE_OPERATOR_NARY_AND), "n-ary and"},
{static_cast<int>(NODE_TYPE_OPERATOR_NARY_OR), "n-ary or"}};
{static_cast<int>(NODE_TYPE_OPERATOR_NARY_OR), "n-ary or"},
{static_cast<int>(NODE_TYPE_COLLECT_AGGREGATE), "collect aggregate"}};
////////////////////////////////////////////////////////////////////////////////
/// @brief names for AST node value types
@ -572,6 +573,7 @@ AstNode::AstNode(Ast* ast, triagens::basics::Json const& json)
case NODE_TYPE_COLLECT:
case NODE_TYPE_COLLECT_COUNT:
case NODE_TYPE_COLLECT_EXPRESSION:
case NODE_TYPE_COLLECT_AGGREGATE:
case NODE_TYPE_SORT:
case NODE_TYPE_SORT_ELEMENT:
case NODE_TYPE_LIMIT:
@ -720,6 +722,7 @@ AstNode::AstNode(std::function<void(AstNode*)> registerNode,
case NODE_TYPE_COLLECT:
case NODE_TYPE_COLLECT_COUNT:
case NODE_TYPE_COLLECT_EXPRESSION:
case NODE_TYPE_COLLECT_AGGREGATE:
case NODE_TYPE_SORT:
case NODE_TYPE_SORT_ELEMENT:
case NODE_TYPE_LIMIT:
@ -2435,6 +2438,7 @@ void AstNode::findVariableAccess(
case NODE_TYPE_NOP:
case NODE_TYPE_COLLECT_COUNT:
case NODE_TYPE_COLLECT_EXPRESSION:
case NODE_TYPE_COLLECT_AGGREGATE:
case NODE_TYPE_CALCULATED_OBJECT_ELEMENT:
case NODE_TYPE_UPSERT:
case NODE_TYPE_EXAMPLE:
@ -2576,6 +2580,7 @@ AstNode const* AstNode::findReference(AstNode const* findme) const {
case NODE_TYPE_NOP:
case NODE_TYPE_COLLECT_COUNT:
case NODE_TYPE_COLLECT_EXPRESSION:
case NODE_TYPE_COLLECT_AGGREGATE:
case NODE_TYPE_CALCULATED_OBJECT_ELEMENT:
case NODE_TYPE_UPSERT:
case NODE_TYPE_EXAMPLE:

View File

@ -189,7 +189,8 @@ enum AstNodeType : uint32_t {
NODE_TYPE_COLLECTION_LIST = 60,
NODE_TYPE_DIRECTION = 61,
NODE_TYPE_OPERATOR_NARY_AND = 62,
NODE_TYPE_OPERATOR_NARY_OR = 63
NODE_TYPE_OPERATOR_NARY_OR = 63,
NODE_TYPE_COLLECT_AGGREGATE = 64
};
static_assert(NODE_TYPE_VALUE < NODE_TYPE_ARRAY, "incorrect node types order");

View File

@ -21,7 +21,7 @@
/// @author Max Neunhoeffer
////////////////////////////////////////////////////////////////////////////////
#include "Aql/AggregateBlock.h"
#include "Aql/CollectBlock.h"
#include "Aql/AqlItemBlock.h"
#include "Aql/ExecutionEngine.h"
#include "Basics/Exceptions.h"
@ -36,14 +36,14 @@ using JsonHelper = triagens::basics::JsonHelper;
using StringBuffer = triagens::basics::StringBuffer;
AggregatorGroup::AggregatorGroup(bool count)
CollectGroup::CollectGroup(bool count)
: firstRow(0),
lastRow(0),
groupLength(0),
rowsAreValid(false),
count(count) {}
AggregatorGroup::~AggregatorGroup() {
CollectGroup::~CollectGroup() {
// reset();
for (auto& it : groupBlocks) {
delete it;
@ -54,7 +54,7 @@ AggregatorGroup::~AggregatorGroup() {
}
void AggregatorGroup::initialize(size_t capacity) {
void CollectGroup::initialize(size_t capacity) {
// TRI_ASSERT(capacity > 0);
groupValues.clear();
@ -73,7 +73,7 @@ void AggregatorGroup::initialize(size_t capacity) {
groupLength = 0;
}
void AggregatorGroup::reset() {
void CollectGroup::reset() {
for (auto& it : groupBlocks) {
delete it;
}
@ -91,7 +91,7 @@ void AggregatorGroup::reset() {
groupLength = 0;
}
void AggregatorGroup::addValues(AqlItemBlock const* src,
void CollectGroup::addValues(AqlItemBlock const* src,
RegisterId groupRegister) {
if (groupRegister == ExecutionNode::MaxRegisterId) {
// nothing to do
@ -107,7 +107,7 @@ void AggregatorGroup::addValues(AqlItemBlock const* src,
} else {
auto block = src->slice(firstRow, lastRow + 1);
try {
TRI_IF_FAILURE("AggregatorGroup::addValues") {
TRI_IF_FAILURE("CollectGroup::addValues") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
}
groupBlocks.emplace_back(block);
@ -124,15 +124,15 @@ void AggregatorGroup::addValues(AqlItemBlock const* src,
}
SortedAggregateBlock::SortedAggregateBlock(ExecutionEngine* engine,
AggregateNode const* en)
SortedCollectBlock::SortedCollectBlock(ExecutionEngine* engine,
CollectNode const* en)
: ExecutionBlock(engine, en),
_aggregateRegisters(),
_currentGroup(en->_count),
_expressionRegister(ExecutionNode::MaxRegisterId),
_groupRegister(ExecutionNode::MaxRegisterId),
_variableNames() {
for (auto const& p : en->_aggregateVariables) {
for (auto const& p : en->_collectVariables) {
// We know that planRegisters() has been run, so
// getPlanNode()->_registerPlan is set up
auto itOut = en->getRegisterPlan()->varInfo.find(p.first->id);
@ -177,7 +177,7 @@ SortedAggregateBlock::SortedAggregateBlock(ExecutionEngine* engine,
// (which means no FOR in which we are contained)
if (usedVariableIds.find(vi.first) == usedVariableIds.end()) {
// variable is not visible to the AggregateBlock
// variable is not visible to the CollectBlock
continue;
}
@ -201,13 +201,13 @@ SortedAggregateBlock::SortedAggregateBlock(ExecutionEngine* engine,
}
}
SortedAggregateBlock::~SortedAggregateBlock() {}
SortedCollectBlock::~SortedCollectBlock() {}
////////////////////////////////////////////////////////////////////////////////
/// @brief initialize
////////////////////////////////////////////////////////////////////////////////
int SortedAggregateBlock::initialize() {
int SortedCollectBlock::initialize() {
int res = ExecutionBlock::initialize();
if (res != TRI_ERROR_NO_ERROR) {
@ -220,7 +220,7 @@ int SortedAggregateBlock::initialize() {
return TRI_ERROR_NO_ERROR;
}
int SortedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
int SortedCollectBlock::getOrSkipSome(size_t atLeast, size_t atMost,
bool skipping, AqlItemBlock*& result,
size_t& skipped) {
TRI_ASSERT(result == nullptr && skipped == 0);
@ -337,7 +337,7 @@ int SortedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
if (!hasMore) {
try {
TRI_IF_FAILURE("SortedAggregateBlock::hasMore") {
TRI_IF_FAILURE("SortedCollectBlock::hasMore") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
}
hasMore = ExecutionBlock::getBlock(atLeast, atMost);
@ -353,7 +353,7 @@ int SortedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
try {
// emit last buffered group
if (!skipping) {
TRI_IF_FAILURE("SortedAggregateBlock::getOrSkipSome") {
TRI_IF_FAILURE("SortedCollectBlock::getOrSkipSome") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
}
@ -397,7 +397,7 @@ int SortedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
/// @brief writes the current group data into the result
////////////////////////////////////////////////////////////////////////////////
void SortedAggregateBlock::emitGroup(AqlItemBlock const* cur, AqlItemBlock* res,
void SortedCollectBlock::emitGroup(AqlItemBlock const* cur, AqlItemBlock* res,
size_t row) {
if (row > 0) {
// re-use already copied aqlvalues
@ -433,12 +433,12 @@ void SortedAggregateBlock::emitGroup(AqlItemBlock const* cur, AqlItemBlock* res,
// set the group values
_currentGroup.addValues(cur, _groupRegister);
if (static_cast<AggregateNode const*>(_exeNode)->_count) {
if (static_cast<CollectNode const*>(_exeNode)->_count) {
// only set group count in result register
res->setValue(
row, _groupRegister,
AqlValue(new Json(static_cast<double>(_currentGroup.groupLength))));
} else if (static_cast<AggregateNode const*>(_exeNode)
} else if (static_cast<CollectNode const*>(_exeNode)
->_expressionVariable != nullptr) {
// copy expression result into result register
res->setValue(row, _groupRegister,
@ -457,12 +457,12 @@ void SortedAggregateBlock::emitGroup(AqlItemBlock const* cur, AqlItemBlock* res,
}
HashedAggregateBlock::HashedAggregateBlock(ExecutionEngine* engine,
AggregateNode const* en)
HashedCollectBlock::HashedCollectBlock(ExecutionEngine* engine,
CollectNode const* en)
: ExecutionBlock(engine, en),
_aggregateRegisters(),
_groupRegister(ExecutionNode::MaxRegisterId) {
for (auto const& p : en->_aggregateVariables) {
for (auto const& p : en->_collectVariables) {
// We know that planRegisters() has been run, so
// getPlanNode()->_registerPlan is set up
auto itOut = en->getRegisterPlan()->varInfo.find(p.first->id);
@ -477,7 +477,7 @@ HashedAggregateBlock::HashedAggregateBlock(ExecutionEngine* engine,
}
if (en->_outVariable != nullptr) {
TRI_ASSERT(static_cast<AggregateNode const*>(_exeNode)->_count);
TRI_ASSERT(static_cast<CollectNode const*>(_exeNode)->_count);
auto const& registerPlan = en->getRegisterPlan()->varInfo;
auto it = registerPlan.find(en->_outVariable->id);
@ -486,19 +486,19 @@ HashedAggregateBlock::HashedAggregateBlock(ExecutionEngine* engine,
TRI_ASSERT(_groupRegister > 0 &&
_groupRegister < ExecutionNode::MaxRegisterId);
} else {
TRI_ASSERT(!static_cast<AggregateNode const*>(_exeNode)->_count);
TRI_ASSERT(!static_cast<CollectNode const*>(_exeNode)->_count);
}
TRI_ASSERT(!_aggregateRegisters.empty());
}
HashedAggregateBlock::~HashedAggregateBlock() {}
HashedCollectBlock::~HashedCollectBlock() {}
////////////////////////////////////////////////////////////////////////////////
/// @brief initialize
////////////////////////////////////////////////////////////////////////////////
int HashedAggregateBlock::initialize() {
int HashedCollectBlock::initialize() {
int res = ExecutionBlock::initialize();
if (res != TRI_ERROR_NO_ERROR) {
@ -508,7 +508,7 @@ int HashedAggregateBlock::initialize() {
return TRI_ERROR_NO_ERROR;
}
int HashedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
int HashedCollectBlock::getOrSkipSome(size_t atLeast, size_t atMost,
bool skipping, AqlItemBlock*& result,
size_t& skipped) {
TRI_ASSERT(result == nullptr && skipped == 0);
@ -539,7 +539,7 @@ int HashedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
allGroups(1024, GroupKeyHash(_trx, colls), GroupKeyEqual(_trx, colls));
auto buildResult = [&](AqlItemBlock const* src) {
auto planNode = static_cast<AggregateNode const*>(getPlanNode());
auto planNode = static_cast<CollectNode const*>(getPlanNode());
auto nrRegs = planNode->getRegisterPlan()->nrRegs[planNode->getDepth()];
auto result = std::make_unique<AqlItemBlock>(allGroups.size(), nrRegs);
@ -635,7 +635,7 @@ int HashedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
try {
// emit last buffered group
if (!skipping) {
TRI_IF_FAILURE("HashedAggregateBlock::getOrSkipSome") {
TRI_IF_FAILURE("HashedCollectBlock::getOrSkipSome") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
}
}
@ -689,7 +689,7 @@ int HashedAggregateBlock::getOrSkipSome(size_t atLeast, size_t atMost,
/// @brief hasher for groups
////////////////////////////////////////////////////////////////////////////////
size_t HashedAggregateBlock::GroupKeyHash::operator()(
size_t HashedCollectBlock::GroupKeyHash::operator()(
std::vector<AqlValue> const& value) const {
uint64_t hash = 0x12345678;
@ -704,7 +704,7 @@ size_t HashedAggregateBlock::GroupKeyHash::operator()(
/// @brief comparator for groups
////////////////////////////////////////////////////////////////////////////////
bool HashedAggregateBlock::GroupKeyEqual::operator()(
bool HashedCollectBlock::GroupKeyEqual::operator()(
std::vector<AqlValue> const& lhs, std::vector<AqlValue> const& rhs) const {
size_t const n = lhs.size();

View File

@ -21,11 +21,11 @@
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_AQL_AGGREGATE_BLOCK_H
#define ARANGOD_AQL_AGGREGATE_BLOCK_H 1
#ifndef ARANGOD_AQL_COLLECT_BLOCK_H
#define ARANGOD_AQL_COLLECT_BLOCK_H 1
#include "Basics/Common.h"
#include "Aql/AggregateNode.h"
#include "Aql/CollectNode.h"
#include "Aql/ExecutionBlock.h"
#include "Aql/ExecutionNode.h"
@ -43,7 +43,7 @@ class ExecutionEngine;
/// @brief details about the current group
///////////////////////////////////////////////////////////////////////////////
struct AggregatorGroup {
struct CollectGroup {
std::vector<AqlValue> groupValues;
std::vector<TRI_document_collection_t const*> collections;
@ -54,11 +54,11 @@ struct AggregatorGroup {
bool rowsAreValid;
bool const count;
AggregatorGroup() = delete;
CollectGroup() = delete;
explicit AggregatorGroup(bool);
explicit CollectGroup(bool);
~AggregatorGroup();
~CollectGroup();
void initialize(size_t capacity);
void reset();
@ -77,11 +77,11 @@ struct AggregatorGroup {
};
class SortedAggregateBlock : public ExecutionBlock {
class SortedCollectBlock : public ExecutionBlock {
public:
SortedAggregateBlock(ExecutionEngine*, AggregateNode const*);
SortedCollectBlock(ExecutionEngine*, CollectNode const*);
~SortedAggregateBlock();
~SortedCollectBlock();
int initialize() override;
@ -106,7 +106,7 @@ class SortedAggregateBlock : public ExecutionBlock {
/// @brief details about the current group
////////////////////////////////////////////////////////////////////////////////
AggregatorGroup _currentGroup;
CollectGroup _currentGroup;
////////////////////////////////////////////////////////////////////////////////
/// @brief the optional register that contains the input expression values for
@ -132,11 +132,11 @@ class SortedAggregateBlock : public ExecutionBlock {
};
class HashedAggregateBlock : public ExecutionBlock {
class HashedCollectBlock : public ExecutionBlock {
public:
HashedAggregateBlock(ExecutionEngine*, AggregateNode const*);
HashedCollectBlock(ExecutionEngine*, CollectNode const*);
~HashedAggregateBlock();
~HashedCollectBlock();
int initialize() override;

View File

@ -21,7 +21,7 @@
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#include "AggregateNode.h"
#include "CollectNode.h"
#include "Aql/Ast.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/WalkerWorker.h"
@ -31,17 +31,17 @@ using namespace triagens::basics;
using namespace triagens::aql;
AggregateNode::AggregateNode(
CollectNode::CollectNode(
ExecutionPlan* plan, triagens::basics::Json const& base,
Variable const* expressionVariable, Variable const* outVariable,
std::vector<Variable const*> const& keepVariables,
std::unordered_map<VariableId, std::string const> const& variableMap,
std::vector<std::pair<Variable const*, Variable const*>> const&
aggregateVariables,
collectVariables,
bool count, bool isDistinctCommand)
: ExecutionNode(plan, base),
_options(base),
_aggregateVariables(aggregateVariables),
_collectVariables(collectVariables),
_expressionVariable(expressionVariable),
_outVariable(outVariable),
_keepVariables(keepVariables),
@ -51,10 +51,10 @@ AggregateNode::AggregateNode(
_specialized(false) {}
////////////////////////////////////////////////////////////////////////////////
/// @brief toJson, for AggregateNode
/// @brief toJson, for CollectNode
////////////////////////////////////////////////////////////////////////////////
void AggregateNode::toJsonHelper(triagens::basics::Json& nodes,
void CollectNode::toJsonHelper(triagens::basics::Json& nodes,
TRI_memory_zone_t* zone, bool verbose) const {
triagens::basics::Json json(ExecutionNode::toJsonHelperGeneric(
nodes, zone, verbose)); // call base class method
@ -64,9 +64,9 @@ void AggregateNode::toJsonHelper(triagens::basics::Json& nodes,
}
triagens::basics::Json values(triagens::basics::Json::Array,
_aggregateVariables.size());
_collectVariables.size());
for (auto it = _aggregateVariables.begin(); it != _aggregateVariables.end();
for (auto it = _collectVariables.begin(); it != _collectVariables.end();
++it) {
triagens::basics::Json variable(triagens::basics::Json::Object);
variable("outVariable", (*it).first->toJson())("inVariable",
@ -110,11 +110,11 @@ void AggregateNode::toJsonHelper(triagens::basics::Json& nodes,
/// @brief clone ExecutionNode recursively
////////////////////////////////////////////////////////////////////////////////
ExecutionNode* AggregateNode::clone(ExecutionPlan* plan, bool withDependencies,
ExecutionNode* CollectNode::clone(ExecutionPlan* plan, bool withDependencies,
bool withProperties) const {
auto outVariable = _outVariable;
auto expressionVariable = _expressionVariable;
auto aggregateVariables = _aggregateVariables;
auto collectVariables = _collectVariables;
if (withProperties) {
if (expressionVariable != nullptr) {
@ -127,16 +127,16 @@ ExecutionNode* AggregateNode::clone(ExecutionPlan* plan, bool withDependencies,
}
// need to re-create all variables
aggregateVariables.clear();
collectVariables.clear();
for (auto& it : _aggregateVariables) {
for (auto& it : _collectVariables) {
auto out = plan->getAst()->variables()->createVariable(it.first);
auto in = plan->getAst()->variables()->createVariable(it.second);
aggregateVariables.emplace_back(std::make_pair(out, in));
collectVariables.emplace_back(std::make_pair(out, in));
}
}
auto c = new AggregateNode(plan, _id, _options, aggregateVariables,
auto c = new CollectNode(plan, _id, _options, collectVariables,
expressionVariable, outVariable, _keepVariables,
_variableMap, _count, _isDistinctCommand);
@ -174,7 +174,7 @@ struct UserVarFinder final : public WalkerWorker<ExecutionNode> {
en->getType() == ExecutionNode::INDEX ||
en->getType() == ExecutionNode::ENUMERATE_LIST ||
en->getType() == ExecutionNode::TRAVERSAL ||
en->getType() == ExecutionNode::AGGREGATE) {
en->getType() == ExecutionNode::COLLECT) {
depth += 1;
}
// Now depth is set correct for this node.
@ -193,7 +193,7 @@ struct UserVarFinder final : public WalkerWorker<ExecutionNode> {
/// @brief getVariablesUsedHere, returning a vector
////////////////////////////////////////////////////////////////////////////////
std::vector<Variable const*> AggregateNode::getVariablesUsedHere() const {
std::vector<Variable const*> CollectNode::getVariablesUsedHere() const {
std::unordered_set<Variable const*> v;
// actual work is done by that method
getVariablesUsedHere(v);
@ -211,9 +211,9 @@ std::vector<Variable const*> AggregateNode::getVariablesUsedHere() const {
/// @brief getVariablesUsedHere, modifying the set in-place
////////////////////////////////////////////////////////////////////////////////
void AggregateNode::getVariablesUsedHere(
void CollectNode::getVariablesUsedHere(
std::unordered_set<Variable const*>& vars) const {
for (auto const& p : _aggregateVariables) {
for (auto const& p : _collectVariables) {
vars.emplace(p.second);
}
@ -226,7 +226,7 @@ void AggregateNode::getVariablesUsedHere(
// Here we have to find all user defined variables in this query
// amongst our dependencies:
UserVarFinder finder(1);
auto myselfAsNonConst = const_cast<AggregateNode*>(this);
auto myselfAsNonConst = const_cast<CollectNode*>(this);
myselfAsNonConst->walk(&finder);
if (finder.depth == 1) {
// we are top level, let's run again with mindepth = 0
@ -251,7 +251,7 @@ void AggregateNode::getVariablesUsedHere(
/// @brief estimateCost
////////////////////////////////////////////////////////////////////////////////
double AggregateNode::estimateCost(size_t& nrItems) const {
double CollectNode::estimateCost(size_t& nrItems) const {
double depCost = _dependencies.at(0)->getCost(nrItems);
// As in the FilterNode case, we are pessimistic here by not reducing the
@ -259,10 +259,10 @@ double AggregateNode::estimateCost(size_t& nrItems) const {
// as there are input items. In any case, we have to look at all incoming
// items, and in particular in the COLLECT ... INTO ... case, we have
// to actually hand on all data anyway, albeit not as separate items.
// Nevertheless, the optimizer does not do much with AggregateNodes
// Nevertheless, the optimizer does not do much with CollectNodes
// and thus this potential overestimation does not really matter.
if (_count && _aggregateVariables.empty()) {
if (_count && _collectVariables.empty()) {
// we are known to only produce a single output row
nrItems = 1;
} else {

View File

@ -21,11 +21,11 @@
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_AQL_AGGREGATE_NODE_H
#define ARANGOD_AQL_AGGREGATE_NODE_H 1
#ifndef ARANGOD_AQL_COLLECT_NODE_H
#define ARANGOD_AQL_COLLECT_NODE_H 1
#include "Basics/Common.h"
#include "Aql/AggregationOptions.h"
#include "Aql/CollectOptions.h"
#include "Aql/ExecutionNode.h"
#include "Aql/types.h"
#include "Aql/Variable.h"
@ -41,15 +41,15 @@ class RedundantCalculationsReplacer;
////////////////////////////////////////////////////////////////////////////////
/// @brief class AggregateNode
/// @brief class CollectNode
////////////////////////////////////////////////////////////////////////////////
class AggregateNode : public ExecutionNode {
class CollectNode : public ExecutionNode {
friend class ExecutionNode;
friend class ExecutionBlock;
friend class SortedAggregateBlock;
friend class HashedAggregateBlock;
friend class HashedCollectBlock;
friend class RedundantCalculationsReplacer;
friend class SortedCollectBlock;
public:
////////////////////////////////////////////////////////////////////////////////
@ -57,17 +57,17 @@ class AggregateNode : public ExecutionNode {
////////////////////////////////////////////////////////////////////////////////
public:
AggregateNode(
ExecutionPlan* plan, size_t id, AggregationOptions const& options,
CollectNode(
ExecutionPlan* plan, size_t id, CollectOptions const& options,
std::vector<std::pair<Variable const*, Variable const*>> const&
aggregateVariables,
collectVariables,
Variable const* expressionVariable, Variable const* outVariable,
std::vector<Variable const*> const& keepVariables,
std::unordered_map<VariableId, std::string const> const& variableMap,
bool count, bool isDistinctCommand)
: ExecutionNode(plan, id),
_options(options),
_aggregateVariables(aggregateVariables),
_collectVariables(collectVariables),
_expressionVariable(expressionVariable),
_outVariable(outVariable),
_keepVariables(keepVariables),
@ -79,20 +79,20 @@ class AggregateNode : public ExecutionNode {
TRI_ASSERT(!_count || _outVariable != nullptr);
}
AggregateNode(
CollectNode(
ExecutionPlan*, triagens::basics::Json const& base,
Variable const* expressionVariable, Variable const* outVariable,
std::vector<Variable const*> const& keepVariables,
std::unordered_map<VariableId, std::string const> const& variableMap,
std::vector<std::pair<Variable const*, Variable const*>> const&
aggregateVariables,
collectVariables,
bool count, bool isDistinctCommand);
////////////////////////////////////////////////////////////////////////////////
/// @brief return the type of the node
////////////////////////////////////////////////////////////////////////////////
NodeType getType() const override final { return AGGREGATE; }
NodeType getType() const override final { return COLLECT; }
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the node requires an additional post SORT
@ -116,7 +116,7 @@ class AggregateNode : public ExecutionNode {
/// @brief return the aggregation method
////////////////////////////////////////////////////////////////////////////////
AggregationOptions::AggregationMethod aggregationMethod() const {
CollectOptions::CollectMethod aggregationMethod() const {
return _options.method;
}
@ -124,7 +124,7 @@ class AggregateNode : public ExecutionNode {
/// @brief set the aggregation method
////////////////////////////////////////////////////////////////////////////////
void aggregationMethod(AggregationOptions::AggregationMethod method) {
void aggregationMethod(CollectOptions::CollectMethod method) {
_options.method = method;
}
@ -132,13 +132,13 @@ class AggregateNode : public ExecutionNode {
/// @brief getOptions
////////////////////////////////////////////////////////////////////////////////
AggregationOptions const& getOptions() const { return _options; }
CollectOptions const& getOptions() const { return _options; }
////////////////////////////////////////////////////////////////////////////////
/// @brief getOptions
////////////////////////////////////////////////////////////////////////////////
AggregationOptions& getOptions() { return _options; }
CollectOptions& getOptions() { return _options; }
////////////////////////////////////////////////////////////////////////////////
/// @brief export to JSON
@ -219,8 +219,8 @@ class AggregateNode : public ExecutionNode {
////////////////////////////////////////////////////////////////////////////////
std::vector<std::pair<Variable const*, Variable const*>> const&
aggregateVariables() const {
return _aggregateVariables;
collectVariables() const {
return _collectVariables;
}
////////////////////////////////////////////////////////////////////////////////
@ -243,10 +243,10 @@ class AggregateNode : public ExecutionNode {
std::vector<Variable const*> getVariablesSetHere() const override final {
std::vector<Variable const*> v;
size_t const n =
_aggregateVariables.size() + (_outVariable == nullptr ? 0 : 1);
_collectVariables.size() + (_outVariable == nullptr ? 0 : 1);
v.reserve(n);
for (auto const& p : _aggregateVariables) {
for (auto const& p : _collectVariables) {
v.emplace_back(p.first);
}
if (_outVariable != nullptr) {
@ -261,13 +261,13 @@ class AggregateNode : public ExecutionNode {
/// @brief options for the aggregation
////////////////////////////////////////////////////////////////////////////////
AggregationOptions _options;
CollectOptions _options;
////////////////////////////////////////////////////////////////////////////////
/// @brief input/output variables for the aggregation (out, in)
////////////////////////////////////////////////////////////////////////////////
std::vector<std::pair<Variable const*, Variable const*>> _aggregateVariables;
std::vector<std::pair<Variable const*, Variable const*>> _collectVariables;
////////////////////////////////////////////////////////////////////////////////
/// @brief input expression variable (might be null)

View File

@ -21,7 +21,7 @@
/// @author Max Neunhoeffer
////////////////////////////////////////////////////////////////////////////////
#include "Aql/AggregationOptions.h"
#include "Aql/CollectOptions.h"
#include "Basics/Exceptions.h"
using namespace triagens::aql;
@ -32,7 +32,7 @@ using JsonHelper = triagens::basics::JsonHelper;
/// @brief constructor, using JSON
////////////////////////////////////////////////////////////////////////////////
AggregationOptions::AggregationOptions(Json const& json) {
CollectOptions::CollectOptions(Json const& json) {
Json obj = json.get("aggregationOptions");
method =
@ -43,8 +43,8 @@ AggregationOptions::AggregationOptions(Json const& json) {
/// @brief whether or not the hash method can be used
////////////////////////////////////////////////////////////////////////////////
bool AggregationOptions::canUseHashMethod() const {
if (method == AggregationMethod::AGGREGATION_METHOD_SORTED) {
bool CollectOptions::canUseHashMethod() const {
if (method == CollectMethod::COLLECT_METHOD_SORTED) {
return false;
}
@ -55,7 +55,7 @@ bool AggregationOptions::canUseHashMethod() const {
/// @brief convert the options to JSON
////////////////////////////////////////////////////////////////////////////////
void AggregationOptions::toJson(triagens::basics::Json& json,
void CollectOptions::toJson(triagens::basics::Json& json,
TRI_memory_zone_t* zone) const {
Json options;
@ -68,28 +68,28 @@ void AggregationOptions::toJson(triagens::basics::Json& json,
/// @brief get the aggregation method from a string
////////////////////////////////////////////////////////////////////////////////
AggregationOptions::AggregationMethod AggregationOptions::methodFromString(
CollectOptions::CollectMethod CollectOptions::methodFromString(
std::string const& method) {
if (method == "hash") {
return AggregationMethod::AGGREGATION_METHOD_HASH;
return CollectMethod::COLLECT_METHOD_HASH;
}
if (method == "sorted") {
return AggregationMethod::AGGREGATION_METHOD_SORTED;
return CollectMethod::COLLECT_METHOD_SORTED;
}
return AggregationMethod::AGGREGATION_METHOD_UNDEFINED;
return CollectMethod::COLLECT_METHOD_UNDEFINED;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief stringify the aggregation method
////////////////////////////////////////////////////////////////////////////////
std::string AggregationOptions::methodToString(
AggregationOptions::AggregationMethod method) {
if (method == AggregationMethod::AGGREGATION_METHOD_HASH) {
std::string CollectOptions::methodToString(
CollectOptions::CollectMethod method) {
if (method == CollectMethod::COLLECT_METHOD_HASH) {
return std::string("hash");
}
if (method == AggregationMethod::AGGREGATION_METHOD_SORTED) {
if (method == CollectMethod::COLLECT_METHOD_SORTED) {
return std::string("sorted");
}

View File

@ -21,8 +21,8 @@
/// @author Max Neunhoeffer
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGOD_AQL_AGGREGATION_OPTIONS_H
#define ARANGOD_AQL_AGGREGATION_OPTIONS_H 1
#ifndef ARANGOD_AQL_COLLECT_OPTIONS_H
#define ARANGOD_AQL_COLLECT_OPTIONS_H 1
#include "Basics/Common.h"
#include "Basics/JsonHelper.h"
@ -31,19 +31,19 @@ namespace triagens {
namespace aql {
////////////////////////////////////////////////////////////////////////////////
/// @brief AggregationOptions
/// @brief CollectOptions
////////////////////////////////////////////////////////////////////////////////
struct AggregationOptions {
struct CollectOptions {
////////////////////////////////////////////////////////////////////////////////
/// @brief selected aggregation method
////////////////////////////////////////////////////////////////////////////////
enum AggregationMethod {
AGGREGATION_METHOD_UNDEFINED,
AGGREGATION_METHOD_HASH,
AGGREGATION_METHOD_SORTED
enum CollectMethod {
COLLECT_METHOD_UNDEFINED,
COLLECT_METHOD_HASH,
COLLECT_METHOD_SORTED
};
@ -51,13 +51,13 @@ struct AggregationOptions {
/// @brief constructor, using default values
////////////////////////////////////////////////////////////////////////////////
AggregationOptions() : method(AGGREGATION_METHOD_UNDEFINED) {}
CollectOptions() : method(COLLECT_METHOD_UNDEFINED) {}
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor, using JSON
////////////////////////////////////////////////////////////////////////////////
AggregationOptions(triagens::basics::Json const&);
CollectOptions(triagens::basics::Json const&);
////////////////////////////////////////////////////////////////////////////////
@ -76,17 +76,17 @@ struct AggregationOptions {
/// @brief get the aggregation method from a string
////////////////////////////////////////////////////////////////////////////////
static AggregationMethod methodFromString(std::string const&);
static CollectMethod methodFromString(std::string const&);
////////////////////////////////////////////////////////////////////////////////
/// @brief stringify the aggregation method
////////////////////////////////////////////////////////////////////////////////
static std::string methodToString(
AggregationOptions::AggregationMethod method);
CollectOptions::CollectMethod method);
AggregationMethod method;
CollectMethod method;
};
} // namespace triagens::aql

View File

@ -33,7 +33,7 @@ using EN = triagens::aql::ExecutionNode;
bool ConditionFinder::before(ExecutionNode* en) {
switch (en->getType()) {
case EN::ENUMERATE_LIST:
case EN::AGGREGATE:
case EN::COLLECT:
case EN::SCATTER:
case EN::DISTRIBUTE:
case EN::GATHER:

View File

@ -22,12 +22,12 @@
////////////////////////////////////////////////////////////////////////////////
#include "Aql/ExecutionEngine.h"
#include "Aql/AggregateBlock.h"
#include "Aql/AggregateNode.h"
#include "Aql/AggregationOptions.h"
#include "Aql/CollectOptions.h"
#include "Aql/BasicBlocks.h"
#include "Aql/CalculationBlock.h"
#include "Aql/ClusterBlocks.h"
#include "Aql/CollectBlock.h"
#include "Aql/CollectNode.h"
#include "Aql/EnumerateCollectionBlock.h"
#include "Aql/EnumerateListBlock.h"
#include "Aql/ExecutionBlock.h"
@ -87,22 +87,22 @@ static ExecutionBlock* CreateBlock(
case ExecutionNode::SORT: {
return new SortBlock(engine, static_cast<SortNode const*>(en));
}
case ExecutionNode::AGGREGATE: {
case ExecutionNode::COLLECT: {
auto aggregationMethod =
static_cast<AggregateNode const*>(en)->aggregationMethod();
static_cast<CollectNode const*>(en)->aggregationMethod();
if (aggregationMethod ==
AggregationOptions::AggregationMethod::AGGREGATION_METHOD_HASH) {
return new HashedAggregateBlock(engine,
static_cast<AggregateNode const*>(en));
} else if (aggregationMethod == AggregationOptions::AggregationMethod::
AGGREGATION_METHOD_SORTED) {
return new SortedAggregateBlock(engine,
static_cast<AggregateNode const*>(en));
CollectOptions::CollectMethod::COLLECT_METHOD_HASH) {
return new HashedCollectBlock(engine,
static_cast<CollectNode const*>(en));
} else if (aggregationMethod == CollectOptions::CollectMethod::
COLLECT_METHOD_SORTED) {
return new SortedCollectBlock(engine,
static_cast<CollectNode const*>(en));
}
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"cannot instantiate AggregateBlock with "
"cannot instantiate CollectBlock with "
"undetermined aggregation method");
}
case ExecutionNode::SUBQUERY: {

View File

@ -23,10 +23,10 @@
////////////////////////////////////////////////////////////////////////////////
#include "ExecutionNode.h"
#include "Aql/AggregateNode.h"
#include "Aql/Ast.h"
#include "Aql/ClusterNodes.h"
#include "Aql/Collection.h"
#include "Aql/CollectNode.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/IndexNode.h"
#include "Aql/ModificationNodes.h"
@ -64,7 +64,7 @@ std::unordered_map<int, std::string const> const ExecutionNode::TypeNames{
{static_cast<int>(SUBQUERY), "SubqueryNode"},
{static_cast<int>(FILTER), "FilterNode"},
{static_cast<int>(SORT), "SortNode"},
{static_cast<int>(AGGREGATE), "AggregateNode"},
{static_cast<int>(COLLECT), "CollectNode"},
{static_cast<int>(RETURN), "ReturnNode"},
{static_cast<int>(REMOVE), "RemoveNode"},
{static_cast<int>(INSERT), "InsertNode"},
@ -159,7 +159,7 @@ ExecutionNode* ExecutionNode::fromJsonFactory(
getSortElements(elements, plan, oneNode, "SortNode");
return new SortNode(plan, oneNode, elements, stable);
}
case AGGREGATE: {
case COLLECT: {
Variable* expressionVariable =
varFromJson(plan->getAst(), oneNode, "expressionVariable", Optional);
Variable* outVariable =
@ -186,9 +186,9 @@ ExecutionNode* ExecutionNode::fromJsonFactory(
size_t const len = jsonAggregates.size();
std::vector<std::pair<Variable const*, Variable const*>>
aggregateVariables;
collectVariables;
aggregateVariables.reserve(len);
collectVariables.reserve(len);
for (size_t i = 0; i < len; i++) {
triagens::basics::Json oneJsonAggregate =
jsonAggregates.at(static_cast<int>(i));
@ -197,16 +197,16 @@ ExecutionNode* ExecutionNode::fromJsonFactory(
Variable* inVar =
varFromJson(plan->getAst(), oneJsonAggregate, "inVariable");
aggregateVariables.emplace_back(std::make_pair(outVar, inVar));
collectVariables.emplace_back(std::make_pair(outVar, inVar));
}
bool count = JsonHelper::checkAndGetBooleanValue(oneNode.json(), "count");
bool isDistinctCommand = JsonHelper::checkAndGetBooleanValue(
oneNode.json(), "isDistinctCommand");
auto node = new AggregateNode(
auto node = new CollectNode(
plan, oneNode, expressionVariable, outVariable, keepVariables,
plan->getAst()->variables()->variables(false), aggregateVariables,
plan->getAst()->variables()->variables(false), collectVariables,
count, isDistinctCommand);
// specialize the node if required
@ -924,7 +924,7 @@ void ExecutionNode::RegisterPlan::after(ExecutionNode* en) {
break;
}
case ExecutionNode::AGGREGATE: {
case ExecutionNode::COLLECT: {
depth++;
nrRegsHere.emplace_back(0);
// create a copy of the last value here
@ -933,8 +933,8 @@ void ExecutionNode::RegisterPlan::after(ExecutionNode* en) {
RegisterId registerId = nrRegs.back();
nrRegs.emplace_back(registerId);
auto ep = static_cast<AggregateNode const*>(en);
for (auto const& p : ep->_aggregateVariables) {
auto ep = static_cast<CollectNode const*>(en);
for (auto const& p : ep->_collectVariables) {
// p is std::pair<Variable const*,Variable const*>
// and the first is the to be assigned output variable
// for which we need to create a register in the current

View File

@ -75,7 +75,7 @@ class ExecutionNode {
CALCULATION = 7,
SUBQUERY = 8,
SORT = 9,
AGGREGATE = 10,
COLLECT = 10,
SCATTER = 11,
GATHER = 12,
REMOTE = 13,

View File

@ -22,10 +22,10 @@
////////////////////////////////////////////////////////////////////////////////
#include "ExecutionPlan.h"
#include "Aql/AggregateNode.h"
#include "Aql/AggregationOptions.h"
#include "Aql/CollectOptions.h"
#include "Aql/Ast.h"
#include "Aql/AstNode.h"
#include "Aql/CollectNode.h"
#include "Aql/ExecutionNode.h"
#include "Aql/Expression.h"
#include "Aql/ModificationNodes.h"
@ -362,15 +362,15 @@ Variable const* ExecutionPlan::getOutVariable(ExecutionNode const* node) const {
return static_cast<CalculationNode const*>(node)->outVariable();
}
if (node->getType() == ExecutionNode::AGGREGATE) {
// AggregateNode has an outVariale() method, but we cannot use it.
// for AggregateNode, outVariable() will return the variable filled by INTO,
if (node->getType() == ExecutionNode::COLLECT) {
// CollectNode has an outVariale() method, but we cannot use it.
// for CollectNode, outVariable() will return the variable filled by INTO,
// but INTO is an optional feature
// so this will return the first result variable of the COLLECT
// this part of the code should only be called for anonymous COLLECT nodes,
// which only have one result variable
auto en = static_cast<AggregateNode const*>(node);
auto const& vars = en->aggregateVariables();
auto en = static_cast<CollectNode const*>(node);
auto const& vars = en->collectVariables();
TRI_ASSERT(vars.size() == 1);
auto v = vars[0].first;
@ -386,17 +386,17 @@ Variable const* ExecutionPlan::getOutVariable(ExecutionNode const* node) const {
/// @brief creates an anonymous COLLECT node (for a DISTINCT)
////////////////////////////////////////////////////////////////////////////////
AggregateNode* ExecutionPlan::createAnonymousCollect(
CollectNode* ExecutionPlan::createAnonymousCollect(
CalculationNode const* previous) {
// generate an out variable for the COLLECT
auto out = _ast->variables()->createTemporaryVariable();
TRI_ASSERT(out != nullptr);
std::vector<std::pair<Variable const*, Variable const*>> const
aggregateVariables{std::make_pair(out, previous->outVariable())};
groupVariables{std::make_pair(out, previous->outVariable())};
auto en = new AggregateNode(this, nextId(), AggregationOptions(),
aggregateVariables, nullptr, nullptr,
auto en = new CollectNode(this, nextId(), CollectOptions(),
groupVariables, nullptr, nullptr,
std::vector<Variable const*>(),
_ast->variables()->variables(false), false, true);
@ -479,9 +479,9 @@ ModificationOptions ExecutionPlan::createModificationOptions(
/// @brief create COLLECT options from an AST node
////////////////////////////////////////////////////////////////////////////////
AggregationOptions ExecutionPlan::createAggregationOptions(
CollectOptions ExecutionPlan::createCollectOptions(
AstNode const* node) {
AggregationOptions options;
CollectOptions options;
// parse the modification options we got
if (node != nullptr && node->type == NODE_TYPE_OBJECT) {
@ -499,7 +499,7 @@ AggregationOptions ExecutionPlan::createAggregationOptions(
if (strcmp(name, "method") == 0) {
if (value->isStringValue()) {
options.method =
AggregationOptions::methodFromString(value->getStringValue());
CollectOptions::methodFromString(value->getStringValue());
}
}
}
@ -874,13 +874,13 @@ ExecutionNode* ExecutionPlan::fromNodeCollect(ExecutionNode* previous,
TRI_ASSERT(n >= 2);
auto options = createAggregationOptions(node->getMember(0));
auto options = createCollectOptions(node->getMember(0));
auto list = node->getMember(1);
size_t const numVars = list->numMembers();
std::vector<std::pair<Variable const*, Variable const*>> aggregateVariables;
aggregateVariables.reserve(numVars);
std::vector<std::pair<Variable const*, Variable const*>> groupVariables;
groupVariables.reserve(numVars);
for (size_t i = 0; i < numVars; ++i) {
auto assigner = list->getMember(i);
@ -900,12 +900,12 @@ ExecutionNode* ExecutionPlan::fromNodeCollect(ExecutionNode* previous,
if (expression->type == NODE_TYPE_REFERENCE) {
// operand is a variable
auto e = static_cast<Variable*>(expression->getData());
aggregateVariables.emplace_back(std::make_pair(v, e));
groupVariables.emplace_back(std::make_pair(v, e));
} else {
// operand is some misc expression
auto calc = createTemporaryCalculation(expression, previous);
previous = calc;
aggregateVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
groupVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
}
}
@ -933,8 +933,8 @@ ExecutionNode* ExecutionPlan::fromNodeCollect(ExecutionNode* previous,
}
}
auto collectNode = new AggregateNode(
this, nextId(), options, aggregateVariables, nullptr, outVariable,
auto collectNode = new CollectNode(
this, nextId(), options, groupVariables, nullptr, outVariable,
keepVariables, _ast->variables()->variables(false), false, false);
auto en = registerNode(collectNode);
@ -951,13 +951,13 @@ ExecutionNode* ExecutionPlan::fromNodeCollectExpression(ExecutionNode* previous,
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_COLLECT_EXPRESSION);
TRI_ASSERT(node->numMembers() == 4);
auto options = createAggregationOptions(node->getMember(0));
auto options = createCollectOptions(node->getMember(0));
auto list = node->getMember(1);
size_t const numVars = list->numMembers();
std::vector<std::pair<Variable const*, Variable const*>> aggregateVariables;
aggregateVariables.reserve(numVars);
std::vector<std::pair<Variable const*, Variable const*>> groupVariables;
groupVariables.reserve(numVars);
for (size_t i = 0; i < numVars; ++i) {
auto assigner = list->getMember(i);
@ -976,12 +976,12 @@ ExecutionNode* ExecutionPlan::fromNodeCollectExpression(ExecutionNode* previous,
if (expression->type == NODE_TYPE_REFERENCE) {
// operand is a variable
auto e = static_cast<Variable*>(expression->getData());
aggregateVariables.emplace_back(std::make_pair(v, e));
groupVariables.emplace_back(std::make_pair(v, e));
} else {
// operand is some misc expression
auto calc = createTemporaryCalculation(expression, previous);
previous = calc;
aggregateVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
groupVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
}
}
@ -1004,8 +1004,8 @@ ExecutionNode* ExecutionPlan::fromNodeCollectExpression(ExecutionNode* previous,
auto v = node->getMember(2);
Variable* outVariable = static_cast<Variable*>(v->getData());
auto collectNode = new AggregateNode(
this, nextId(), options, aggregateVariables, expressionVariable,
auto collectNode = new CollectNode(
this, nextId(), options, groupVariables, expressionVariable,
outVariable, std::vector<Variable const*>(),
std::unordered_map<VariableId, std::string const>(), false, false);
@ -1025,13 +1025,13 @@ ExecutionNode* ExecutionPlan::fromNodeCollectCount(ExecutionNode* previous,
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_COLLECT_COUNT);
TRI_ASSERT(node->numMembers() == 3);
auto options = createAggregationOptions(node->getMember(0));
auto options = createCollectOptions(node->getMember(0));
auto list = node->getMember(1);
size_t const numVars = list->numMembers();
std::vector<std::pair<Variable const*, Variable const*>> aggregateVariables;
aggregateVariables.reserve(numVars);
std::vector<std::pair<Variable const*, Variable const*>> groupVariables;
groupVariables.reserve(numVars);
for (size_t i = 0; i < numVars; ++i) {
auto assigner = list->getMember(i);
@ -1050,12 +1050,12 @@ ExecutionNode* ExecutionPlan::fromNodeCollectCount(ExecutionNode* previous,
if (expression->type == NODE_TYPE_REFERENCE) {
// operand is a variable
auto e = static_cast<Variable*>(expression->getData());
aggregateVariables.emplace_back(std::make_pair(v, e));
groupVariables.emplace_back(std::make_pair(v, e));
} else {
// operand is some misc expression
auto calc = createTemporaryCalculation(expression, previous);
previous = calc;
aggregateVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
groupVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
}
}
@ -1067,13 +1067,93 @@ ExecutionNode* ExecutionPlan::fromNodeCollectCount(ExecutionNode* previous,
TRI_ASSERT(outVariable != nullptr);
auto en = registerNode(
new AggregateNode(this, nextId(), options, aggregateVariables, nullptr,
new CollectNode(this, nextId(), options, groupVariables, nullptr,
outVariable, std::vector<Variable const*>(),
_ast->variables()->variables(false), true, false));
return addDependency(previous, en);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST COLLECT node, AGGREGATE
////////////////////////////////////////////////////////////////////////////////
ExecutionNode* ExecutionPlan::fromNodeCollectAggregate(ExecutionNode* previous,
AstNode const* node) {
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_COLLECT_AGGREGATE);
TRI_ASSERT(node->numMembers() == 3);
auto options = createCollectOptions(node->getMember(0));
// parse group variables
auto list = node->getMember(1);
size_t numVars = list->numMembers();
std::vector<std::pair<Variable const*, Variable const*>> groupVariables;
groupVariables.reserve(numVars);
for (size_t i = 0; i < numVars; ++i) {
auto assigner = list->getMember(i);
if (assigner == nullptr) {
continue;
}
TRI_ASSERT(assigner->type == NODE_TYPE_ASSIGN);
auto out = assigner->getMember(0);
TRI_ASSERT(out != nullptr);
auto v = static_cast<Variable*>(out->getData());
TRI_ASSERT(v != nullptr);
auto expression = assigner->getMember(1);
if (expression->type == NODE_TYPE_REFERENCE) {
// operand is a variable
auto e = static_cast<Variable*>(expression->getData());
groupVariables.emplace_back(std::make_pair(v, e));
} else {
// operand is some misc expression
auto calc = createTemporaryCalculation(expression, previous);
previous = calc;
groupVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
}
}
list = node->getMember(2);
numVars = list->numMembers();
std::vector<std::pair<Variable const*, Variable const*>> aggregateVariables;
aggregateVariables.reserve(numVars);
for (size_t i = 0; i < numVars; ++i) {
auto assigner = list->getMember(i);
if (assigner == nullptr) {
continue;
}
TRI_ASSERT(assigner->type == NODE_TYPE_ASSIGN);
auto out = assigner->getMember(0);
TRI_ASSERT(out != nullptr);
auto v = static_cast<Variable*>(out->getData());
TRI_ASSERT(v != nullptr);
auto expression = assigner->getMember(1);
// operand is some misc expression
auto calc = createTemporaryCalculation(expression, previous);
previous = calc;
groupVariables.emplace_back(std::make_pair(v, getOutVariable(calc)));
}
auto collectNode = new CollectNode(
this, nextId(), options, groupVariables, nullptr,
nullptr, std::vector<Variable const*>(),
std::unordered_map<VariableId, std::string const>(), false, false);
auto en = registerNode(collectNode);
return addDependency(previous, en);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST LIMIT node
////////////////////////////////////////////////////////////////////////////////
@ -1472,6 +1552,11 @@ ExecutionNode* ExecutionPlan::fromNode(AstNode const* node) {
en = fromNodeCollectCount(en, member);
break;
}
case NODE_TYPE_COLLECT_AGGREGATE: {
en = fromNodeCollectAggregate(en, member);
break;
}
case NODE_TYPE_LIMIT: {
en = fromNodeLimit(en, member);

View File

@ -25,7 +25,7 @@
#define ARANGOD_AQL_EXECUTION_PLAN_H 1
#include "Basics/Common.h"
#include "Aql/AggregationOptions.h"
#include "Aql/CollectOptions.h"
#include "Aql/ExecutionNode.h"
#include "Aql/ModificationOptions.h"
#include "Aql/Query.h"
@ -35,10 +35,10 @@
namespace triagens {
namespace aql {
class AggregateNode;
class Ast;
struct AstNode;
class CalculationNode;
class CollectNode;
class ExecutionNode;
@ -338,7 +338,7 @@ class ExecutionPlan {
/// @brief creates an anonymous COLLECT node (for a DISTINCT)
////////////////////////////////////////////////////////////////////////////////
AggregateNode* createAnonymousCollect(CalculationNode const*);
CollectNode* createAnonymousCollect(CalculationNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create modification options from an AST node
@ -350,7 +350,7 @@ class ExecutionPlan {
/// @brief create COLLECT options from an AST node
////////////////////////////////////////////////////////////////////////////////
AggregationOptions createAggregationOptions(AstNode const*);
CollectOptions createCollectOptions(AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief adds "previous" as dependency to "plan", returns "plan"
@ -406,6 +406,12 @@ class ExecutionPlan {
////////////////////////////////////////////////////////////////////////////////
ExecutionNode* fromNodeCollectCount(ExecutionNode*, AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST COLLECT node, AGGREGATE
////////////////////////////////////////////////////////////////////////////////
ExecutionNode* fromNodeCollectAggregate(ExecutionNode*, AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST LIMIT node

View File

@ -392,7 +392,7 @@ void Optimizer::setupRules() {
true);
#endif
// determine the "right" type of AggregateNode and
// determine the "right" type of CollectNode and
// add a sort node for each COLLECT (may be removed later)
// this rule cannot be turned off (otherwise, the query result might be
// wrong!)

View File

@ -93,7 +93,7 @@ class Optimizer {
pass1 = 100,
// determine the "right" type of AggregateNode and
// determine the "right" type of CollectNode and
// add a sort node for each COLLECT (may be removed later)
specializeCollectRule_pass1 = 105,

View File

@ -23,9 +23,9 @@
////////////////////////////////////////////////////////////////////////////////
#include "OptimizerRules.h"
#include "Aql/AggregateNode.h"
#include "Aql/AggregationOptions.h"
#include "Aql/CollectOptions.h"
#include "Aql/ClusterNodes.h"
#include "Aql/CollectNode.h"
#include "Aql/ConditionFinder.h"
#include "Aql/ExecutionEngine.h"
#include "Aql/ExecutionNode.h"
@ -440,10 +440,10 @@ void triagens::aql::removeUnnecessaryFiltersRule(Optimizer* opt,
void triagens::aql::removeCollectIntoRule(Optimizer* opt, ExecutionPlan* plan,
Optimizer::Rule const* rule) {
bool modified = false;
std::vector<ExecutionNode*> nodes(plan->findNodesOfType(EN::AGGREGATE, true));
std::vector<ExecutionNode*> nodes(plan->findNodesOfType(EN::COLLECT, true));
for (auto const& n : nodes) {
auto collectNode = static_cast<AggregateNode*>(n);
auto collectNode = static_cast<CollectNode*>(n);
TRI_ASSERT(collectNode != nullptr);
auto outVariable = collectNode->outVariable();
@ -774,13 +774,13 @@ void triagens::aql::removeSortRandRule(Optimizer* opt, ExecutionPlan* plan,
switch (current->getType()) {
case EN::SORT:
case EN::AGGREGATE:
case EN::COLLECT:
case EN::FILTER:
case EN::SUBQUERY:
case EN::ENUMERATE_LIST:
case EN::TRAVERSAL:
case EN::INDEX: {
// if we found another SortNode, an AggregateNode, FilterNode, a
// if we found another SortNode, an CollectNode, FilterNode, a
// SubqueryNode,
// an EnumerateListNode, a TraversalNode or an IndexNode
// this means we cannot apply our optimization
@ -965,7 +965,7 @@ void triagens::aql::moveCalculationsDownRule(Optimizer* opt,
} else if (currentType == EN::INDEX ||
currentType == EN::ENUMERATE_COLLECTION ||
currentType == EN::ENUMERATE_LIST ||
currentType == EN::TRAVERSAL || currentType == EN::AGGREGATE ||
currentType == EN::TRAVERSAL || currentType == EN::COLLECT ||
currentType == EN::NORESULTS) {
// we will not push further down than such nodes
shouldMove = false;
@ -1108,29 +1108,29 @@ void triagens::aql::fuseCalculationsRule(Optimizer* opt, ExecutionPlan* plan,
}
////////////////////////////////////////////////////////////////////////////////
/// @brief determine the "right" type of AggregateNode and
/// @brief determine the "right" type of CollectNode and
/// add a sort node for each COLLECT (note: the sort may be removed later)
/// this rule cannot be turned off (otherwise, the query result might be wrong!)
////////////////////////////////////////////////////////////////////////////////
void triagens::aql::specializeCollectRule(Optimizer* opt, ExecutionPlan* plan,
Optimizer::Rule const* rule) {
std::vector<ExecutionNode*> nodes(plan->findNodesOfType(EN::AGGREGATE, true));
std::vector<ExecutionNode*> nodes(plan->findNodesOfType(EN::COLLECT, true));
bool modified = false;
for (auto const& n : nodes) {
auto collectNode = static_cast<AggregateNode*>(n);
auto collectNode = static_cast<CollectNode*>(n);
if (collectNode->isSpecialized()) {
// already specialized this node
continue;
}
auto const& aggregateVariables = collectNode->aggregateVariables();
auto const& collectVariables = collectNode->collectVariables();
// test if we can use an alternative version of COLLECT with a hash table
bool const canUseHashAggregation =
(!aggregateVariables.empty() &&
(!collectVariables.empty() &&
(!collectNode->hasOutVariable() || collectNode->count()) &&
collectNode->getOptions().canUseHashMethod());
@ -1140,21 +1140,21 @@ void triagens::aql::specializeCollectRule(Optimizer* opt, ExecutionPlan* plan,
// use the cloned COLLECT node
auto newCollectNode =
static_cast<AggregateNode*>(newPlan->getNodeById(collectNode->id()));
static_cast<CollectNode*>(newPlan->getNodeById(collectNode->id()));
TRI_ASSERT(newCollectNode != nullptr);
// specialize the AggregateNode so it will become a HashAggregateBlock
// specialize the CollectNode so it will become a HashedCollectBlock
// later
// additionally, add a SortNode BEHIND the AggregateNode (to sort the
// additionally, add a SortNode BEHIND the CollectNode (to sort the
// final result)
newCollectNode->aggregationMethod(
AggregationOptions::AggregationMethod::AGGREGATION_METHOD_HASH);
CollectOptions::CollectMethod::COLLECT_METHOD_HASH);
newCollectNode->specialized();
if (!collectNode->isDistinctCommand()) {
// add the post-SORT
std::vector<std::pair<Variable const*, bool>> sortElements;
for (auto const& v : newCollectNode->aggregateVariables()) {
for (auto const& v : newCollectNode->collectVariables()) {
sortElements.emplace_back(std::make_pair(v.first, true));
}
@ -1187,15 +1187,15 @@ void triagens::aql::specializeCollectRule(Optimizer* opt, ExecutionPlan* plan,
// finally, adjust the original plan and create a sorted version of COLLECT
// specialize the AggregateNode so it will become a SortedAggregateBlock
// specialize the CollectNode so it will become a SortedCollectBlock
// later
collectNode->aggregationMethod(
AggregationOptions::AggregationMethod::AGGREGATION_METHOD_SORTED);
CollectOptions::CollectMethod::COLLECT_METHOD_SORTED);
// insert a SortNode IN FRONT OF the AggregateNode
if (!aggregateVariables.empty()) {
// insert a SortNode IN FRONT OF the CollectNode
if (!collectVariables.empty()) {
std::vector<std::pair<Variable const*, bool>> sortElements;
for (auto const& v : aggregateVariables) {
for (auto const& v : collectVariables) {
sortElements.emplace_back(std::make_pair(v.second, true));
}
@ -1412,9 +1412,9 @@ class triagens::aql::RedundantCalculationsReplacer final
break;
}
case EN::AGGREGATE: {
auto node = static_cast<AggregateNode*>(en);
for (auto& variable : node->_aggregateVariables) {
case EN::COLLECT: {
auto node = static_cast<CollectNode*>(en);
for (auto& variable : node->_collectVariables) {
variable.second = Variable::replace(variable.second, _replacements);
}
break;
@ -1541,8 +1541,8 @@ void triagens::aql::removeRedundantCalculationsRule(
}
}
if (current->getType() == EN::AGGREGATE) {
if (static_cast<AggregateNode*>(current)->hasOutVariable()) {
if (current->getType() == EN::COLLECT) {
if (static_cast<CollectNode*>(current)->hasOutVariable()) {
// COLLECT ... INTO is evil (tm): it needs to keep all already defined
// variables
// we need to abort optimization here
@ -1891,7 +1891,7 @@ struct SortToIndexNode final : public WalkerWorker<ExecutionNode> {
}
case EN::SINGLETON:
case EN::AGGREGATE:
case EN::COLLECT:
case EN::INSERT:
case EN::REMOVE:
case EN::REPLACE:
@ -2568,7 +2568,7 @@ void triagens::aql::distributeFilternCalcToClusterRule(
continue;
}
case EN::AGGREGATE:
case EN::COLLECT:
case EN::SUBQUERY:
case EN::RETURN:
case EN::NORESULTS:
@ -2663,7 +2663,7 @@ void triagens::aql::distributeSortToClusterRule(Optimizer* opt,
switch (inspectNode->getType()) {
case EN::ENUMERATE_LIST:
case EN::SINGLETON:
case EN::AGGREGATE:
case EN::COLLECT:
case EN::INSERT:
case EN::REMOVE:
case EN::REPLACE:
@ -2937,7 +2937,7 @@ class RemoveToEnumCollFinder final : public WalkerWorker<ExecutionNode> {
case EN::SINGLETON:
case EN::ENUMERATE_LIST:
case EN::SUBQUERY:
case EN::AGGREGATE:
case EN::COLLECT:
case EN::INSERT:
case EN::REPLACE:
case EN::UPDATE:

View File

@ -103,7 +103,7 @@ void moveCalculationsDownRule(Optimizer*, ExecutionPlan*,
void fuseCalculationsRule(Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief determine the "right" type of AggregateNode and
/// @brief determine the "right" type of CollectNode and
/// add a sort node for each COLLECT (may be removed later)
/// this rule cannot be turned off (otherwise, the query result might be wrong!)
////////////////////////////////////////////////////////////////////////////////

View File

@ -227,7 +227,7 @@ bool TraversalConditionFinder::before(ExecutionNode* en) {
switch (en->getType()) {
case EN::ENUMERATE_LIST:
case EN::AGGREGATE:
case EN::COLLECT:
case EN::SCATTER:
case EN::DISTRIBUTE:
case EN::GATHER:

File diff suppressed because it is too large Load Diff

View File

@ -58,59 +58,60 @@ extern int Aqldebug;
T_IN = 267,
T_WITH = 268,
T_INTO = 269,
T_GRAPH = 270,
T_DISTINCT = 271,
T_REMOVE = 272,
T_INSERT = 273,
T_UPDATE = 274,
T_REPLACE = 275,
T_UPSERT = 276,
T_NULL = 277,
T_TRUE = 278,
T_FALSE = 279,
T_STRING = 280,
T_QUOTED_STRING = 281,
T_INTEGER = 282,
T_DOUBLE = 283,
T_PARAMETER = 284,
T_ASSIGN = 285,
T_NOT = 286,
T_AND = 287,
T_OR = 288,
T_EQ = 289,
T_NE = 290,
T_LT = 291,
T_GT = 292,
T_LE = 293,
T_GE = 294,
T_PLUS = 295,
T_MINUS = 296,
T_TIMES = 297,
T_DIV = 298,
T_MOD = 299,
T_QUESTION = 300,
T_COLON = 301,
T_SCOPE = 302,
T_RANGE = 303,
T_COMMA = 304,
T_OPEN = 305,
T_CLOSE = 306,
T_OBJECT_OPEN = 307,
T_OBJECT_CLOSE = 308,
T_ARRAY_OPEN = 309,
T_ARRAY_CLOSE = 310,
T_OUTBOUND = 311,
T_INBOUND = 312,
T_ANY = 313,
T_ALL = 314,
T_NONE = 315,
T_NIN = 316,
UMINUS = 317,
UPLUS = 318,
FUNCCALL = 319,
REFERENCE = 320,
INDEXED = 321,
EXPANSION = 322
T_AGGREGATE = 270,
T_GRAPH = 271,
T_DISTINCT = 272,
T_REMOVE = 273,
T_INSERT = 274,
T_UPDATE = 275,
T_REPLACE = 276,
T_UPSERT = 277,
T_NULL = 278,
T_TRUE = 279,
T_FALSE = 280,
T_STRING = 281,
T_QUOTED_STRING = 282,
T_INTEGER = 283,
T_DOUBLE = 284,
T_PARAMETER = 285,
T_ASSIGN = 286,
T_NOT = 287,
T_AND = 288,
T_OR = 289,
T_EQ = 290,
T_NE = 291,
T_LT = 292,
T_GT = 293,
T_LE = 294,
T_GE = 295,
T_PLUS = 296,
T_MINUS = 297,
T_TIMES = 298,
T_DIV = 299,
T_MOD = 300,
T_QUESTION = 301,
T_COLON = 302,
T_SCOPE = 303,
T_RANGE = 304,
T_COMMA = 305,
T_OPEN = 306,
T_CLOSE = 307,
T_OBJECT_OPEN = 308,
T_OBJECT_CLOSE = 309,
T_ARRAY_OPEN = 310,
T_ARRAY_CLOSE = 311,
T_OUTBOUND = 312,
T_INBOUND = 313,
T_ANY = 314,
T_ALL = 315,
T_NONE = 316,
T_NIN = 317,
UMINUS = 318,
UPLUS = 319,
FUNCCALL = 320,
REFERENCE = 321,
INDEXED = 322,
EXPANSION = 323
};
#endif
@ -129,7 +130,7 @@ union YYSTYPE
bool boolval;
int64_t intval;
#line 133 "arangod/Aql/grammar.hpp" /* yacc.c:1909 */
#line 134 "arangod/Aql/grammar.hpp" /* yacc.c:1909 */
};
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1

View File

@ -72,6 +72,7 @@ void Aqlerror (YYLTYPE* locp,
%token T_IN "IN keyword"
%token T_WITH "WITH keyword"
%token T_INTO "INTO keyword"
%token T_AGGREGATE "AGGREGATE keyword"
%token T_GRAPH "GRAPH keyword"
%token T_DISTINCT "DISTINCT modifier"
@ -125,10 +126,10 @@ void Aqlerror (YYLTYPE* locp,
%token T_END 0 "end of query string"
%token T_OUTBOUND "outbound direction"
%token T_INBOUND "inbound direction"
%token T_ANY "any direction"
%token T_OUTBOUND "outbound modifier"
%token T_INBOUND "inbound modifier"
%token T_ANY "any modifier"
%token T_ALL "all modifier"
%token T_NONE "none modifier"
@ -167,6 +168,7 @@ void Aqlerror (YYLTYPE* locp,
%type <node> collect_element;
%type <node> collect_variable_list;
%type <node> keep;
%type <node> aggregate;
%type <strval> optional_into;
%type <strval> count_into;
%type <node> expression;
@ -347,6 +349,7 @@ collect_variable_list:
collect_statement:
T_COLLECT count_into options {
/* COLLECT WITH COUNT INTO var OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
@ -363,6 +366,7 @@ collect_statement:
parser->ast()->addOperation(node);
}
| collect_variable_list count_into options {
/* COLLECT var = expr WITH COUNT INTO var OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
@ -389,7 +393,94 @@ collect_statement:
auto node = parser->ast()->createNodeCollectCount($1, $2.value, $2.length, $3);
parser->ast()->addOperation(node);
}
| T_COLLECT aggregate options {
/* AGGREGATE var = expr OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
bool reRegisterVariables = (scopes->type() != triagens::aql::AQL_SCOPE_MAIN);
if (reRegisterVariables) {
// end the active scopes
scopes->endNested();
// start a new scope
scopes->start(triagens::aql::AQL_SCOPE_COLLECT);
size_t const n = $2->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = $2->getMember(i);
if (member != nullptr) {
TRI_ASSERT(member->type == NODE_TYPE_ASSIGN);
auto v = static_cast<Variable*>(member->getMember(0)->getData());
scopes->addVariable(v);
}
}
}
auto node = parser->ast()->createNodeCollectAggregate(parser->ast()->createNodeArray(), $2, $3);
parser->ast()->addOperation(node);
}
| collect_variable_list aggregate options {
/* COLLECT var = expr AGGREGATE var = expr OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
bool reRegisterVariables = (scopes->type() != triagens::aql::AQL_SCOPE_MAIN);
if (reRegisterVariables) {
// end the active scopes
scopes->endNested();
// start a new scope
scopes->start(triagens::aql::AQL_SCOPE_COLLECT);
// register all used variables
size_t n = $1->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = $1->getMember(i);
if (member != nullptr) {
TRI_ASSERT(member->type == NODE_TYPE_ASSIGN);
auto v = static_cast<Variable*>(member->getMember(0)->getData());
scopes->addVariable(v);
}
}
n = $2->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = $2->getMember(i);
if (member != nullptr) {
TRI_ASSERT(member->type == NODE_TYPE_ASSIGN);
auto func = member->getMember(1);
bool isValid = true;
if (func->type != NODE_TYPE_FCALL) {
// aggregate expression must be a function call
isValid = false;
}
else {
auto f = static_cast<triagens::aql::Function*>(func->getData());
if (f->externalName != "MIN" && f->externalName != "MAX" && f->externalName != "LENGTH") {
// aggregate expression must be a call to MIN|MAX|LENGTH
isValid = false;
}
}
if (! isValid) {
parser->registerParseError(TRI_ERROR_QUERY_PARSE, "aggregate expression must be a function call", yylloc.first_line, yylloc.first_column);
}
auto v = static_cast<Variable*>(member->getMember(0)->getData());
scopes->addVariable(v);
}
}
}
auto node = parser->ast()->createNodeCollectAggregate($1, $2, $3);
parser->ast()->addOperation(node);
}
| collect_variable_list optional_into options {
/* COLLECT var = expr INTO var OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
@ -416,7 +507,36 @@ collect_statement:
auto node = parser->ast()->createNodeCollect($1, $2.value, $2.length, nullptr, $3);
parser->ast()->addOperation(node);
}
| collect_variable_list T_INTO variable_name T_ASSIGN expression options {
/* COLLECT var = expr INTO var = expr OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
bool reRegisterVariables = (scopes->type() != triagens::aql::AQL_SCOPE_MAIN);
if (reRegisterVariables) {
// end the active scopes
scopes->endNested();
// start a new scope
scopes->start(triagens::aql::AQL_SCOPE_COLLECT);
size_t const n = $1->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = $1->getMember(i);
if (member != nullptr) {
TRI_ASSERT(member->type == NODE_TYPE_ASSIGN);
auto v = static_cast<Variable*>(member->getMember(0)->getData());
scopes->addVariable(v);
}
}
}
auto node = parser->ast()->createNodeCollectExpression($1, $3.value, $3.length, $5, $6);
parser->ast()->addOperation(node);
}
| collect_variable_list optional_into keep options {
/* COLLECT var = expr INTO var KEEP ... OPTIONS ... */
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
@ -448,33 +568,6 @@ collect_statement:
auto node = parser->ast()->createNodeCollect($1, $2.value, $2.length, $3, $4);
parser->ast()->addOperation(node);
}
| collect_variable_list T_INTO variable_name T_ASSIGN expression options {
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
bool reRegisterVariables = (scopes->type() != triagens::aql::AQL_SCOPE_MAIN);
if (reRegisterVariables) {
// end the active scopes
scopes->endNested();
// start a new scope
scopes->start(triagens::aql::AQL_SCOPE_COLLECT);
size_t const n = $1->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = $1->getMember(i);
if (member != nullptr) {
TRI_ASSERT(member->type == NODE_TYPE_ASSIGN);
auto v = static_cast<Variable*>(member->getMember(0)->getData());
scopes->addVariable(v);
}
}
}
auto node = parser->ast()->createNodeCollectExpression($1, $3.value, $3.length, $5, $6);
parser->ast()->addOperation(node);
}
;
collect_list:
@ -547,6 +640,16 @@ keep:
}
;
aggregate:
T_AGGREGATE {
auto node = parser->ast()->createNodeArray();
parser->pushStack(node);
} collect_list {
auto list = static_cast<AstNode*>(parser->popStack());
$$ = list;
}
;
sort_statement:
T_SORT {
auto node = parser->ast()->createNodeArray();

File diff suppressed because it is too large Load Diff

View File

@ -93,6 +93,10 @@ namespace triagens {
return T_DISTINCT;
}
(?i:AGGREGATE) {
return T_AGGREGATE;
}
(?i:ASC) {
return T_ASC;
}

View File

@ -60,9 +60,6 @@ add_executable(
Actions/RestActionHandler.cpp
ApplicationServer/ApplicationFeature.cpp
ApplicationServer/ApplicationServer.cpp
Aql/AggregateBlock.cpp
Aql/AggregateNode.cpp
Aql/AggregationOptions.cpp
Aql/AqlItemBlock.cpp
Aql/AqlItemBlockManager.cpp
Aql/AqlValue.cpp
@ -77,6 +74,9 @@ add_executable(
Aql/Collection.cpp
Aql/Collections.cpp
Aql/CollectionScanner.cpp
Aql/CollectBlock.cpp
Aql/CollectNode.cpp
Aql/CollectOptions.cpp
Aql/Condition.cpp
Aql/ConditionFinder.cpp
Aql/EnumerateCollectionBlock.cpp

View File

@ -17,9 +17,6 @@ arangod_libarangod_a_SOURCES = \
arangod/Actions/RestActionHandler.cpp \
arangod/ApplicationServer/ApplicationFeature.cpp \
arangod/ApplicationServer/ApplicationServer.cpp \
arangod/Aql/AggregateBlock.cpp \
arangod/Aql/AggregateNode.cpp \
arangod/Aql/AggregationOptions.cpp \
arangod/Aql/AqlItemBlock.cpp \
arangod/Aql/AqlItemBlockManager.cpp \
arangod/Aql/AqlValue.cpp \
@ -34,6 +31,9 @@ arangod_libarangod_a_SOURCES = \
arangod/Aql/Collection.cpp \
arangod/Aql/Collections.cpp \
arangod/Aql/CollectionScanner.cpp \
arangod/Aql/CollectBlock.cpp \
arangod/Aql/CollectNode.cpp \
arangod/Aql/CollectOptions.cpp \
arangod/Aql/Condition.cpp \
arangod/Aql/ConditionFinder.cpp \
arangod/Aql/EnumerateCollectionBlock.cpp \

View File

@ -795,6 +795,7 @@ function processQuery (query, explain) {
case "FilterNode":
return keyword("FILTER") + " " + variableName(node.inVariable);
case "AggregateNode":
case "CollectNode":
return keyword("COLLECT") + " " + node.aggregates.map(function(node) {
return variableName(node.outVariable) + " = " + variableName(node.inVariable);
}).join(", ") +

View File

@ -794,6 +794,7 @@ function processQuery (query, explain) {
case "FilterNode":
return keyword("FILTER") + " " + variableName(node.inVariable);
case "AggregateNode":
case "CollectNode":
return keyword("COLLECT") + " " + node.aggregates.map(function(node) {
return variableName(node.outVariable) + " = " + variableName(node.inVariable);
}).join(", ") +

View File

@ -282,7 +282,7 @@ function explainSuite () {
prev = node.id;
node = nodes[n++];
assertEqual("AggregateNode", node.type);
assertEqual("CollectNode", node.type);
assertEqual([ prev ], node.dependencies);
assertEqual(1, node.aggregates.length);
assertEqual("a", node.aggregates[0].inVariable.name);

View File

@ -258,7 +258,7 @@ function explainSuite () {
assertTrue(node.stable);
node = nodes[7];
assertEqual("AggregateNode", node.type);
assertEqual("CollectNode", node.type);
assertEqual([ 9 ], node.dependencies);
assertEqual(7, node.id);
assertEqual(1, node.aggregates.length);

View File

@ -126,7 +126,7 @@ function optimizerCollectMethodsTestSuite () {
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
if (node.type === "CollectNode") {
++aggregateNodes;
assertEqual("hash", node.aggregationOptions.method);
}
@ -166,7 +166,7 @@ function optimizerCollectMethodsTestSuite () {
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
if (node.type === "CollectNode") {
++aggregateNodes;
assertEqual("hash", node.aggregationOptions.method);
}
@ -204,7 +204,7 @@ function optimizerCollectMethodsTestSuite () {
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
if (node.type === "CollectNode") {
++aggregateNodes;
assertEqual("sorted", node.aggregationOptions.method);
}
@ -241,7 +241,7 @@ function optimizerCollectMethodsTestSuite () {
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
if (node.type === "CollectNode") {
++aggregateNodes;
assertEqual("hash", node.aggregationOptions.method);
}
@ -282,7 +282,7 @@ function optimizerCollectMethodsTestSuite () {
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
if (node.type === "CollectNode") {
++aggregateNodes;
assertEqual("sorted", node.aggregationOptions.method);
}
@ -317,7 +317,7 @@ function optimizerCollectMethodsTestSuite () {
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
if (node.type === "CollectNode") {
++aggregateNodes;
assertEqual("sorted", node.aggregationOptions.method);
}

View File

@ -117,9 +117,9 @@ function optimizerRuleTestSuite () {
testPlans : function () {
var plans = [
[ "FOR i IN 1..10 COLLECT a = i INTO group RETURN a", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "SortNode", "AggregateNode", "ReturnNode" ] ],
[ "FOR i IN 1..10 FOR j IN 1..10 COLLECT a = i, b = j INTO group RETURN a", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "EnumerateListNode", "SortNode", "AggregateNode", "ReturnNode" ] ],
[ "FOR i IN 1..10 FOR j IN 1..10 COLLECT a = i, b = j INTO group RETURN { a: a, b : b }", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "EnumerateListNode", "SortNode", "AggregateNode", "CalculationNode", "ReturnNode" ] ]
[ "FOR i IN 1..10 COLLECT a = i INTO group RETURN a", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "SortNode", "CollectNode", "ReturnNode" ] ],
[ "FOR i IN 1..10 FOR j IN 1..10 COLLECT a = i, b = j INTO group RETURN a", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "EnumerateListNode", "SortNode", "CollectNode", "ReturnNode" ] ],
[ "FOR i IN 1..10 FOR j IN 1..10 COLLECT a = i, b = j INTO group RETURN { a: a, b : b }", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "EnumerateListNode", "SortNode", "CollectNode", "CalculationNode", "ReturnNode" ] ]
];
plans.forEach(function(plan) {

View File

@ -133,7 +133,7 @@ function optimizerRuleTestSuite () {
[ "FOR i IN [ { a: 1 }, { a: 2 }, { a: 3 } ] SORT i.a DESC SORT i.a DESC RETURN i", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "SortNode", "ReturnNode" ] ],
[ "FOR i IN [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 3, b: 1 } ] SORT i.a ASC, i.b DESC SORT i.a DESC, i.b ASC RETURN i", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "CalculationNode", "SortNode", "ReturnNode" ] ],
[ "FOR i IN [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 3, b: 1 } ] SORT i.a ASC, i.b DESC FILTER i.a == 1 SORT i.a DESC, i.b ASC RETURN i", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "FilterNode", "CalculationNode", "CalculationNode", "SortNode", "ReturnNode" ] ],
[ "FOR i IN [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 3, b: 1 } ] SORT i.a ASC COLLECT x = i.a RETURN x", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "SortNode", "AggregateNode", "ReturnNode" ] ],
[ "FOR i IN [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 3, b: 1 } ] SORT i.a ASC COLLECT x = i.a RETURN x", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "SortNode", "CollectNode", "ReturnNode" ] ],
[ "FOR i IN [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 3, b: 1 } ] SORT i.a, i.b SORT i.a SORT i.a RETURN i", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "CalculationNode", "SortNode", "ReturnNode" ] ],
[ "FOR i IN [ { a: 1, b: 1 }, { a: 2, b: 1 }, { a: 3, b: 1 } ] SORT i.a, i.b SORT i.a DESC SORT i.a ASC RETURN i", [ "SingletonNode", "CalculationNode", "EnumerateListNode", "CalculationNode", "CalculationNode", "SortNode", "ReturnNode" ] ]
];