mirror of https://gitee.com/bigwinds/arangodb
added tests
This commit is contained in:
parent
3e99e76b4f
commit
d6f3436cc9
|
@ -169,6 +169,34 @@ void Ast::addOperation (AstNode* node) {
|
|||
|
||||
_root->addMember(node);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief find the bottom-most expansion subnodes (if any)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode const* Ast::findExpansionSubNode (AstNode const* current) const {
|
||||
while (true) {
|
||||
TRI_ASSERT(current->type == NODE_TYPE_EXPANSION);
|
||||
|
||||
if (current->getMember(1)->type != NODE_TYPE_EXPANSION) {
|
||||
return current;
|
||||
}
|
||||
current = current->getMember(1);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST passhthru node
|
||||
/// note: this type of node is only used during parsing and optimized away later
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::createNodePassthru (AstNode const* what) {
|
||||
AstNode* node = createNode(NODE_TYPE_PASSTHRU);
|
||||
|
||||
node->addMember(what);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST example node
|
||||
|
@ -776,33 +804,61 @@ AstNode* Ast::createNodeIndexedAccess (AstNode const* accessed,
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST expansion node, without filter
|
||||
/// @brief create an AST array limit node (offset, count)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::createNodeExpansion (AstNode const* iterator,
|
||||
AstNode const* expanded,
|
||||
bool multiExpand) {
|
||||
AstNode* node = createNode(NODE_TYPE_EXPANSION);
|
||||
node->setBoolValue(multiExpand);
|
||||
AstNode* Ast::createNodeArrayLimit (AstNode const* offset,
|
||||
AstNode const* count) {
|
||||
AstNode* node = createNode(NODE_TYPE_ARRAY_LIMIT);
|
||||
|
||||
node->addMember(iterator);
|
||||
node->addMember(expanded);
|
||||
if (offset == nullptr) {
|
||||
offset = createNodeValueInt(0);
|
||||
}
|
||||
node->addMember(offset);
|
||||
node->addMember(count);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST expansion node, with a filter
|
||||
/// @brief create an AST expansion node, with or without a filter
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::createNodeExpansion (AstNode const* iterator,
|
||||
AstNode* Ast::createNodeExpansion (int64_t levels,
|
||||
AstNode const* iterator,
|
||||
AstNode const* expanded,
|
||||
AstNode const* filter) {
|
||||
AstNode const* filter,
|
||||
AstNode const* limit,
|
||||
AstNode const* projection) {
|
||||
AstNode* node = createNode(NODE_TYPE_EXPANSION);
|
||||
node->setIntValue(levels);
|
||||
|
||||
node->addMember(iterator);
|
||||
node->addMember(expanded);
|
||||
node->addMember(filter);
|
||||
|
||||
if (filter == nullptr) {
|
||||
node->addMember(createNodeNop());
|
||||
}
|
||||
else {
|
||||
node->addMember(filter);
|
||||
}
|
||||
|
||||
if (limit == nullptr) {
|
||||
node->addMember(createNodeNop());
|
||||
}
|
||||
else {
|
||||
TRI_ASSERT(limit->type == NODE_TYPE_ARRAY_LIMIT);
|
||||
node->addMember(limit);
|
||||
}
|
||||
|
||||
if (projection == nullptr) {
|
||||
node->addMember(createNodeNop());
|
||||
}
|
||||
else {
|
||||
node->addMember(projection);
|
||||
}
|
||||
|
||||
TRI_ASSERT(node->numMembers() == 5);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -1153,6 +1209,7 @@ AstNode* Ast::replaceVariables (AstNode* node,
|
|||
|
||||
if (variable != nullptr) {
|
||||
auto it = replacements.find(variable->id);
|
||||
|
||||
if (it != replacements.end()) {
|
||||
// overwrite the node in place
|
||||
node->setData((*it).second);
|
||||
|
@ -1296,6 +1353,12 @@ void Ast::validateAndOptimize () {
|
|||
return this->optimizeTernaryOperator(node);
|
||||
}
|
||||
|
||||
// passthru node
|
||||
if (node->type == NODE_TYPE_PASSTHRU) {
|
||||
// optimize away passthru node. this type of node is only used during parsing
|
||||
return node->getMember(0);
|
||||
}
|
||||
|
||||
// call to built-in function
|
||||
if (node->type == NODE_TYPE_FCALL) {
|
||||
auto func = static_cast<Function*>(node->getData());
|
||||
|
|
|
@ -214,6 +214,19 @@ namespace triagens {
|
|||
|
||||
void addOperation (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief find the bottom-most expansion subnodes (if any)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode const* findExpansionSubNode (AstNode const*) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST passthru node
|
||||
/// note: this type of node is only used during parsing and optimized away later
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* createNodePassthru (AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST example node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -446,18 +459,20 @@ namespace triagens {
|
|||
AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST expansion node, without filter
|
||||
/// @brief create an AST array limit node (offset, count)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* createNodeExpansion (AstNode const*,
|
||||
AstNode* createNodeArrayLimit (AstNode const*,
|
||||
AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST expansion node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* createNodeExpansion (int64_t,
|
||||
AstNode const*,
|
||||
AstNode const*,
|
||||
AstNode const*,
|
||||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST expansion node, with a filter
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* createNodeExpansion (AstNode const*,
|
||||
AstNode const*,
|
||||
AstNode const*);
|
||||
|
||||
|
|
|
@ -146,7 +146,9 @@ std::unordered_map<int, std::string const> const AstNode::TypeNames{
|
|||
{ static_cast<int>(NODE_TYPE_COLLECT_COUNT), "collect count" },
|
||||
{ static_cast<int>(NODE_TYPE_COLLECT_EXPRESSION), "collect expression" },
|
||||
{ static_cast<int>(NODE_TYPE_CALCULATED_OBJECT_ELEMENT),"calculated object element" },
|
||||
{ static_cast<int>(NODE_TYPE_EXAMPLE), "example" }
|
||||
{ static_cast<int>(NODE_TYPE_EXAMPLE), "example" },
|
||||
{ static_cast<int>(NODE_TYPE_PASSTHRU), "passthru" },
|
||||
{ static_cast<int>(NODE_TYPE_ARRAY_LIMIT), "array limit" }
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -515,6 +517,10 @@ AstNode::AstNode (Ast* ast,
|
|||
setStringValue(query->registerString(JsonHelper::getStringValue(json.json(), "name", ""), false));
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_EXPANSION: {
|
||||
setIntValue(JsonHelper::checkAndGetNumericValue<int64_t>(json.json(), "levels"));
|
||||
break;
|
||||
}
|
||||
case NODE_TYPE_OBJECT:
|
||||
case NODE_TYPE_ROOT:
|
||||
case NODE_TYPE_FOR:
|
||||
|
@ -555,13 +561,14 @@ AstNode::AstNode (Ast* ast,
|
|||
case NODE_TYPE_SUBQUERY:
|
||||
case NODE_TYPE_BOUND_ATTRIBUTE_ACCESS:
|
||||
case NODE_TYPE_INDEXED_ACCESS:
|
||||
case NODE_TYPE_EXPANSION:
|
||||
case NODE_TYPE_ITERATOR:
|
||||
case NODE_TYPE_ARRAY:
|
||||
case NODE_TYPE_RANGE:
|
||||
case NODE_TYPE_NOP:
|
||||
case NODE_TYPE_CALCULATED_OBJECT_ELEMENT:
|
||||
case NODE_TYPE_EXAMPLE:
|
||||
case NODE_TYPE_PASSTHRU:
|
||||
case NODE_TYPE_ARRAY_LIMIT:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -732,7 +739,7 @@ TRI_json_t* AstNode::toJsonValue (TRI_memory_zone_t* zone) const {
|
|||
}
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto member = getMember(i);
|
||||
auto member = getMemberUnchecked(i);
|
||||
|
||||
if (member != nullptr) {
|
||||
TRI_json_t* j = member->toJsonValue(zone);
|
||||
|
@ -750,7 +757,7 @@ TRI_json_t* AstNode::toJsonValue (TRI_memory_zone_t* zone) const {
|
|||
TRI_json_t* object = TRI_CreateObjectJson(zone, n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto member = getMember(i);
|
||||
auto member = getMemberUnchecked(i);
|
||||
|
||||
if (member != nullptr) {
|
||||
TRI_json_t* j = member->getMember(0)->toJsonValue(zone);
|
||||
|
@ -862,9 +869,13 @@ TRI_json_t* AstNode::toJson (TRI_memory_zone_t* zone,
|
|||
TRI_Insert3ObjectJson(zone, node, "name", TRI_CreateStringCopyJson(zone, variable->name.c_str(), variable->name.size()));
|
||||
TRI_Insert3ObjectJson(zone, node, "id", TRI_CreateNumberJson(zone, static_cast<double>(variable->id)));
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_EXPANSION) {
|
||||
TRI_Insert3ObjectJson(zone, node, "levels", TRI_CreateNumberJson(zone, static_cast<double>(getIntValue(true))));
|
||||
}
|
||||
|
||||
// dump sub-nodes
|
||||
size_t const n = members._length;
|
||||
size_t const n = TRI_LengthVectorPointer(&members);
|
||||
|
||||
if (n > 0) {
|
||||
TRI_json_t* subNodes = TRI_CreateArrayJson(zone, n);
|
||||
|
@ -876,8 +887,8 @@ TRI_json_t* AstNode::toJson (TRI_memory_zone_t* zone,
|
|||
|
||||
try {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
AstNode* member = getMember(i);
|
||||
if (member != nullptr && member->type != NODE_TYPE_NOP) {
|
||||
AstNode* member = getMemberUnchecked(i);
|
||||
if (member != nullptr) {
|
||||
member->toJson(subNodes, zone, verbose);
|
||||
}
|
||||
}
|
||||
|
@ -1266,7 +1277,8 @@ bool AstNode::isSimple () const {
|
|||
type == NODE_TYPE_OPERATOR_BINARY_IN ||
|
||||
type == NODE_TYPE_OPERATOR_BINARY_NIN ||
|
||||
type == NODE_TYPE_RANGE ||
|
||||
type == NODE_TYPE_INDEXED_ACCESS) {
|
||||
type == NODE_TYPE_INDEXED_ACCESS ||
|
||||
type == NODE_TYPE_PASSTHRU) {
|
||||
// a logical operator is simple if its operands are simple
|
||||
// a comparison operator is simple if both bounds are simple
|
||||
// a range is simple if both bounds are simple
|
||||
|
@ -1687,6 +1699,16 @@ void AstNode::stringify (triagens::basics::StringBuffer* buffer,
|
|||
buffer->appendChar(')');
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_ARRAY_LIMIT) {
|
||||
// not used by V8
|
||||
buffer->appendText("_LIMIT(");
|
||||
getMember(0)->stringify(buffer, verbose, failIfLong);
|
||||
buffer->appendChar(',');
|
||||
getMember(1)->stringify(buffer, verbose, failIfLong);
|
||||
buffer->appendChar(')');
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_EXPANSION) {
|
||||
// not used by V8
|
||||
|
@ -1694,10 +1716,27 @@ void AstNode::stringify (triagens::basics::StringBuffer* buffer,
|
|||
getMember(0)->stringify(buffer, verbose, failIfLong);
|
||||
buffer->appendChar(',');
|
||||
getMember(1)->stringify(buffer, verbose, failIfLong);
|
||||
if (numMembers() > 2) {
|
||||
buffer->appendChar(',');
|
||||
getMember(2)->stringify(buffer, verbose, failIfLong);
|
||||
// filter
|
||||
buffer->appendChar(',');
|
||||
|
||||
auto filterNode = getMember(2);
|
||||
if (filterNode != nullptr) {
|
||||
buffer->appendText(" FILTER ");
|
||||
filterNode->getMember(0)->stringify(buffer, verbose, failIfLong);
|
||||
}
|
||||
auto limitNode = getMember(3);
|
||||
if (limitNode != nullptr) {
|
||||
buffer->appendText(" LIMIT ");
|
||||
limitNode->getMember(0)->stringify(buffer, verbose, failIfLong);
|
||||
buffer->appendChar(',');
|
||||
limitNode->getMember(1)->stringify(buffer, verbose, failIfLong);
|
||||
}
|
||||
auto returnNode = getMember(4);
|
||||
if (returnNode != nullptr) {
|
||||
buffer->appendText(" RETURN ");
|
||||
returnNode->getMember(0)->stringify(buffer, verbose, failIfLong);
|
||||
}
|
||||
|
||||
buffer->appendChar(')');
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -171,7 +171,9 @@ namespace triagens {
|
|||
NODE_TYPE_COLLECT_EXPRESSION = 52,
|
||||
NODE_TYPE_CALCULATED_OBJECT_ELEMENT = 53,
|
||||
NODE_TYPE_UPSERT = 54,
|
||||
NODE_TYPE_EXAMPLE = 55
|
||||
NODE_TYPE_EXAMPLE = 55,
|
||||
NODE_TYPE_PASSTHRU = 56,
|
||||
NODE_TYPE_ARRAY_LIMIT = 57
|
||||
};
|
||||
|
||||
static_assert(NODE_TYPE_VALUE < NODE_TYPE_ARRAY, "incorrect node types");
|
||||
|
@ -509,7 +511,7 @@ namespace triagens {
|
|||
/// @brief return the number of members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline size_t numMembers () const {
|
||||
inline size_t numMembers () const throw() {
|
||||
return members._length;
|
||||
}
|
||||
|
||||
|
|
|
@ -262,6 +262,12 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
|
|||
|
||||
size_t const Executor::DefaultLiteralSizeThreshold = 32;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief maxmium number of array members created from range accesses
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int64_t const Executor::MaxRangeAccessArraySize = 1024 * 1024 * 32;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors / destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -305,7 +311,7 @@ V8Expression* Executor::generateExpression (AstNode const* node) {
|
|||
|
||||
// std::cout << "Executor::generateExpression: " << std::string(_buffer->c_str(), _buffer->length()) << "\n";
|
||||
v8::Handle<v8::Object> constantValues = v8::Object::New(isolate);
|
||||
for (auto& it : _constantRegisters) {
|
||||
for (auto const& it : _constantRegisters) {
|
||||
std::string name = "r";
|
||||
name.append(std::to_string(it.second));
|
||||
|
||||
|
@ -657,11 +663,56 @@ void Executor::generateCodeArray (AstNode const* node) {
|
|||
_buffer->appendChar(',');
|
||||
}
|
||||
|
||||
generateCodeNode(node->getMember(i));
|
||||
generateCodeNode(node->getMemberUnchecked(i));
|
||||
}
|
||||
_buffer->appendChar(']');
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate JavaScript code for an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Executor::generateCodeForcedArray (AstNode const* node,
|
||||
int64_t levels) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
|
||||
if (levels > 1) {
|
||||
_buffer->appendText("_AQL.AQL_FLATTEN(");
|
||||
}
|
||||
|
||||
bool castToArray = true;
|
||||
if (node->type == NODE_TYPE_ARRAY) {
|
||||
// value is an array already
|
||||
castToArray = false;
|
||||
}
|
||||
else if (node->type == NODE_TYPE_EXPANSION &&
|
||||
node->getMember(0)->type == NODE_TYPE_ARRAY) {
|
||||
// value is an expansion over an array
|
||||
castToArray = false;
|
||||
}
|
||||
else if (node->type == NODE_TYPE_ITERATOR &&
|
||||
node->getMember(1)->type == NODE_TYPE_ARRAY) {
|
||||
castToArray = false;
|
||||
}
|
||||
|
||||
if (castToArray) {
|
||||
// force the value to be an array
|
||||
_buffer->appendText("_AQL.AQL_TO_ARRAY(");
|
||||
generateCodeNode(node);
|
||||
_buffer->appendChar(')');
|
||||
}
|
||||
else {
|
||||
// value already is an array
|
||||
generateCodeNode(node);
|
||||
}
|
||||
|
||||
if (levels > 1) {
|
||||
_buffer->appendChar(',');
|
||||
_buffer->appendInteger(levels - 1);
|
||||
_buffer->appendChar(')');
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate JavaScript code for an object
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -689,7 +740,7 @@ void Executor::generateCodeDynamicObject (AstNode const* node) {
|
|||
|
||||
_buffer->appendText("(function() { var o={};");
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto member = node->getMember(i);
|
||||
auto member = node->getMemberUnchecked(i);
|
||||
|
||||
if (member->type == NODE_TYPE_OBJECT_ELEMENT) {
|
||||
_buffer->appendText("o[", 2);
|
||||
|
@ -963,47 +1014,66 @@ void Executor::generateCodeUserFunctionCall (AstNode const* node) {
|
|||
|
||||
void Executor::generateCodeExpansion (AstNode const* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->numMembers() >= 2);
|
||||
|
||||
TRI_ASSERT(node->numMembers() == 5);
|
||||
|
||||
auto levels = node->getIntValue(true);
|
||||
|
||||
auto iterator = node->getMember(0);
|
||||
auto variable = static_cast<Variable*>(iterator->getMember(0)->getData());
|
||||
|
||||
_buffer->appendText("(function () { return _AQL.AQL_TO_LIST(");
|
||||
if (node->getBoolValue()) {
|
||||
_buffer->appendText("_AQL.COMPACT(");
|
||||
generateCodeNode(node->getMember(0));
|
||||
_buffer->appendText(")");
|
||||
}
|
||||
else {
|
||||
generateCodeNode(node->getMember(0));
|
||||
// start LIMIT
|
||||
auto limitNode = node->getMember(3);
|
||||
|
||||
if (limitNode->type != NODE_TYPE_NOP) {
|
||||
_buffer->appendText("_AQL.AQL_SLICE(");
|
||||
}
|
||||
|
||||
if (node->numMembers() > 2) {
|
||||
// use a filter expression
|
||||
_buffer->appendText(").filter(function (v) { ");
|
||||
generateCodeForcedArray(node->getMember(0), levels);
|
||||
|
||||
// FILTER
|
||||
auto filterNode = node->getMember(2);
|
||||
|
||||
if (filterNode->type != NODE_TYPE_NOP) {
|
||||
_buffer->appendText(".filter(function (v) { ");
|
||||
_buffer->appendText("vars[\"");
|
||||
_buffer->appendText(variable->name);
|
||||
_buffer->appendText("\"]=v; ");
|
||||
_buffer->appendText("return _AQL.AQL_TO_BOOL(");
|
||||
generateCodeNode(node->getMember(2));
|
||||
_buffer->appendText("); }");
|
||||
generateCodeNode(filterNode);
|
||||
_buffer->appendText("); })");
|
||||
}
|
||||
|
||||
_buffer->appendText(").map(function (v) { ");
|
||||
// finish LIMIT
|
||||
if (limitNode->type != NODE_TYPE_NOP) {
|
||||
_buffer->appendChar(',');
|
||||
generateCodeNode(limitNode->getMember(0));
|
||||
_buffer->appendChar(',');
|
||||
generateCodeNode(limitNode->getMember(1));
|
||||
_buffer->appendChar(')');
|
||||
}
|
||||
|
||||
// RETURN
|
||||
_buffer->appendText(".map(function (v) { ");
|
||||
_buffer->appendText("vars[\"");
|
||||
_buffer->appendText(variable->name);
|
||||
_buffer->appendText("\"]=v; ");
|
||||
|
||||
_buffer->appendText("return ");
|
||||
generateCodeNode(node->getMember(1));
|
||||
_buffer->appendText("; }); })()");
|
||||
if (node->getMember(4)->type != NODE_TYPE_NOP) {
|
||||
generateCodeNode(node->getMember(4));
|
||||
}
|
||||
else {
|
||||
generateCodeNode(node->getMember(1));
|
||||
}
|
||||
_buffer->appendText("; })");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate JavaScript code for an expansion iterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Executor::generateCodeExpandIterator (AstNode const* node) {
|
||||
void Executor::generateCodeExpansionIterator (AstNode const* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
||||
|
@ -1064,25 +1134,12 @@ void Executor::generateCodeIndexedAccess (AstNode const* node) {
|
|||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
||||
auto index = node->getMember(1);
|
||||
if (index->type == NODE_TYPE_RANGE) {
|
||||
// range access
|
||||
_buffer->appendText("_AQL.GET_RANGE(");
|
||||
generateCodeNode(node->getMember(0));
|
||||
_buffer->appendChar(',');
|
||||
generateCodeNode(index->getMember(0));
|
||||
_buffer->appendChar(',');
|
||||
generateCodeNode(index->getMember(1));
|
||||
_buffer->appendChar(')');
|
||||
}
|
||||
else {
|
||||
// indexed access
|
||||
_buffer->appendText("_AQL.GET_INDEX(");
|
||||
generateCodeNode(node->getMember(0));
|
||||
_buffer->appendChar(',');
|
||||
generateCodeNode(index);
|
||||
_buffer->appendChar(')');
|
||||
}
|
||||
// indexed access
|
||||
_buffer->appendText("_AQL.GET_INDEX(");
|
||||
generateCodeNode(node->getMember(0));
|
||||
_buffer->appendChar(',');
|
||||
generateCodeNode(node->getMember(1));
|
||||
_buffer->appendChar(')');
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1148,13 +1205,13 @@ void Executor::generateCodeNode (AstNode const* node) {
|
|||
case NODE_TYPE_FCALL_USER:
|
||||
generateCodeUserFunctionCall(node);
|
||||
break;
|
||||
|
||||
|
||||
case NODE_TYPE_EXPANSION:
|
||||
generateCodeExpansion(node);
|
||||
break;
|
||||
|
||||
case NODE_TYPE_ITERATOR:
|
||||
generateCodeExpandIterator(node);
|
||||
generateCodeExpansionIterator(node);
|
||||
break;
|
||||
|
||||
case NODE_TYPE_RANGE:
|
||||
|
@ -1175,11 +1232,13 @@ void Executor::generateCodeNode (AstNode const* node) {
|
|||
|
||||
case NODE_TYPE_VARIABLE:
|
||||
case NODE_TYPE_PARAMETER:
|
||||
case NODE_TYPE_PASSTHRU:
|
||||
case NODE_TYPE_ARRAY_LIMIT:
|
||||
// we're not expecting these types here
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unexpected node type in code generator");
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unexpected node type in generateCodeNode");
|
||||
|
||||
default:
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "node type not implemented");
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED, "node type not implemented in generateCodeNode");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1195,7 +1254,7 @@ triagens::basics::StringBuffer* Executor::initializeBuffer () {
|
|||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
_buffer->reserve(256);
|
||||
_buffer->reserve(512);
|
||||
}
|
||||
else {
|
||||
_buffer->clear();
|
||||
|
|
|
@ -150,6 +150,13 @@ namespace triagens {
|
|||
|
||||
void generateCodeArray (AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate JavaScript code for a forced array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void generateCodeForcedArray (AstNode const*,
|
||||
int64_t);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate JavaScript code for an object
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -228,7 +235,7 @@ namespace triagens {
|
|||
/// @brief generate JavaScript code for an expansion iterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void generateCodeExpandIterator (AstNode const*);
|
||||
void generateCodeExpansionIterator (AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate JavaScript code for a range (i.e. 1..10)
|
||||
|
@ -308,12 +315,24 @@ namespace triagens {
|
|||
|
||||
static std::unordered_map<std::string, Function const> const FunctionNames;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public static variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief minimum number of array members / object attributes for considering
|
||||
/// an array / object literal "big" and pulling it out of the expression
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static size_t const DefaultLiteralSizeThreshold;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief maxmium number of array members created from range accesses
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int64_t const MaxRangeAccessArraySize;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -281,78 +281,6 @@ void Expression::invalidate () {
|
|||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extracts a range from an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AqlValue Expression::extractRange (AqlValue const& result,
|
||||
AqlValue const& indexResult,
|
||||
TRI_document_collection_t const* collection,
|
||||
triagens::arango::AqlTransaction* trx) const {
|
||||
TRI_ASSERT(result.isArray());
|
||||
|
||||
size_t const length = result.arraySize();
|
||||
|
||||
if (length == 0) {
|
||||
// cannot extract anything from an empty array
|
||||
return AqlValue(new Json(Json::Array));
|
||||
}
|
||||
|
||||
int64_t low = indexResult.getRange()->_low;
|
||||
int64_t high = indexResult.getRange()->_high;
|
||||
|
||||
// negative positions are translated into their positive counterparts
|
||||
if (low < 0) {
|
||||
low = static_cast<int64_t>(length) + low;
|
||||
}
|
||||
if (high < 0) {
|
||||
high = static_cast<int64_t>(length) + high;
|
||||
}
|
||||
|
||||
std::unique_ptr<Json> array(new Json(Json::Array));
|
||||
|
||||
if (low <= high) {
|
||||
// forward iteration
|
||||
++high;
|
||||
if (low < high) {
|
||||
if (low < 0) {
|
||||
low = 0;
|
||||
}
|
||||
if (high >= static_cast<int64_t>(length)) {
|
||||
high = static_cast<int64_t>(length);
|
||||
}
|
||||
if (low < high) {
|
||||
array->reserve(static_cast<size_t>(high - low));
|
||||
for (int64_t position = low; position < high; ++position) {
|
||||
auto j = result.extractArrayMember(trx, collection, position, true);
|
||||
array->add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// backward iteration
|
||||
--high;
|
||||
if (low >= static_cast<int64_t>(length)) {
|
||||
low = static_cast<int64_t>(length) - 1;
|
||||
}
|
||||
if (high < -1) {
|
||||
high = -1;
|
||||
}
|
||||
|
||||
if (low - high > 0) {
|
||||
array->reserve(static_cast<size_t>(low - high));
|
||||
// array->reserve(static_cast<size_t>(high - low + 1));
|
||||
for (int64_t position = low; position > high; --position) {
|
||||
auto j = result.extractArrayMember(trx, collection, position, true);
|
||||
array->add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AqlValue(array.release());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief find a value in an AQL list node
|
||||
/// this performs either a binary search (if the node is sorted) or a
|
||||
|
@ -592,19 +520,6 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
|||
// no number found.
|
||||
}
|
||||
}
|
||||
else if (indexResult.isRange()) {
|
||||
try {
|
||||
auto range = extractRange(result, indexResult, myCollection, trx);
|
||||
indexResult.destroy();
|
||||
result.destroy();
|
||||
return range;
|
||||
}
|
||||
catch (...) {
|
||||
indexResult.destroy();
|
||||
result.destroy();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
// fall-through to returning null
|
||||
}
|
||||
else if (result.isObject()) {
|
||||
|
|
|
@ -296,15 +296,6 @@ namespace triagens {
|
|||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extracts a range from an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AqlValue extractRange (AqlValue const&,
|
||||
AqlValue const&,
|
||||
TRI_document_collection_t const*,
|
||||
triagens::arango::AqlTransaction*) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief find a value in an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1611,6 +1611,7 @@ int triagens::aql::removeRedundantCalculationsRule (Optimizer* opt,
|
|||
auto target = outvars[0];
|
||||
while (target != nullptr) {
|
||||
auto it = replacements.find(target->id);
|
||||
|
||||
if (it != replacements.end()) {
|
||||
target = (*it).second;
|
||||
}
|
||||
|
@ -1656,7 +1657,6 @@ int triagens::aql::removeRedundantCalculationsRule (Optimizer* opt,
|
|||
|
||||
if (! replacements.empty()) {
|
||||
// finally replace the variables
|
||||
|
||||
RedundantCalculationsReplacer finder(replacements);
|
||||
plan->root()->walk(&finder);
|
||||
plan->findVarUsage();
|
||||
|
|
|
@ -268,7 +268,9 @@ Variable const* Scopes::getCurrentVariable () const {
|
|||
if (_currentVariables.empty()) {
|
||||
THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_VARIABLE_NAME_UNKNOWN, Variable::NAME_CURRENT);
|
||||
}
|
||||
return _currentVariables.back();
|
||||
auto result = _currentVariables.back();
|
||||
TRI_ASSERT(result != nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -140,10 +140,10 @@ void Aqlerror (YYLTYPE* locp,
|
|||
%left T_PLUS T_MINUS
|
||||
%left T_TIMES T_DIV T_MOD
|
||||
%right UMINUS UPLUS T_NOT
|
||||
%left EXPANSION
|
||||
%left FUNCCALL
|
||||
%left REFERENCE
|
||||
%left INDEXED
|
||||
%left EXPANSION
|
||||
%left T_SCOPE
|
||||
|
||||
/* define token return types */
|
||||
|
@ -180,6 +180,10 @@ void Aqlerror (YYLTYPE* locp,
|
|||
%type <node> object_elements_list;
|
||||
%type <node> object_element;
|
||||
%type <strval> object_element_name;
|
||||
%type <intval> array_filter_operator;
|
||||
%type <node> optional_array_filter;
|
||||
%type <node> optional_array_limit;
|
||||
%type <node> optional_array_return;
|
||||
%type <node> reference;
|
||||
%type <node> simple_value;
|
||||
%type <node> value_literal;
|
||||
|
@ -971,6 +975,45 @@ object_element:
|
|||
}
|
||||
;
|
||||
|
||||
array_filter_operator:
|
||||
T_TIMES {
|
||||
$$ = 1;
|
||||
}
|
||||
| array_filter_operator T_TIMES {
|
||||
$$ = $1 + 1;
|
||||
}
|
||||
;
|
||||
|
||||
optional_array_filter:
|
||||
/* empty */ {
|
||||
$$ = nullptr;
|
||||
}
|
||||
| T_FILTER expression {
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
optional_array_limit:
|
||||
/* empty */ {
|
||||
$$ = nullptr;
|
||||
}
|
||||
| T_LIMIT expression {
|
||||
$$ = parser->ast()->createNodeArrayLimit(nullptr, $2);
|
||||
}
|
||||
| T_LIMIT expression T_COMMA expression {
|
||||
$$ = parser->ast()->createNodeArrayLimit($2, $4);
|
||||
}
|
||||
;
|
||||
|
||||
optional_array_return:
|
||||
/* empty */ {
|
||||
$$ = nullptr;
|
||||
}
|
||||
| T_RETURN expression {
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
reference:
|
||||
T_STRING {
|
||||
// variable or collection
|
||||
|
@ -1028,7 +1071,14 @@ reference:
|
|||
}
|
||||
}
|
||||
| T_OPEN expression T_CLOSE {
|
||||
$$ = $2;
|
||||
if ($2->type == NODE_TYPE_EXPANSION) {
|
||||
// create a dummy passthru node that reduces and evaluates the expansion first
|
||||
// and the expansion on top of the stack won't be chained with any other expansions
|
||||
$$ = parser->ast()->createNodePassthru($2);
|
||||
}
|
||||
else {
|
||||
$$ = $2;
|
||||
}
|
||||
}
|
||||
| T_OPEN {
|
||||
if (parser->isModificationQuery()) {
|
||||
|
@ -1050,8 +1100,11 @@ reference:
|
|||
// named variable access, e.g. variable.reference
|
||||
if ($1->type == NODE_TYPE_EXPANSION) {
|
||||
// if left operand is an expansion already...
|
||||
// patch the existing expansion
|
||||
$1->changeMember(1, parser->ast()->createNodeAttributeAccess($1->getMember(1), $3));
|
||||
// dive into the expansion's right-hand child nodes for further expansion and
|
||||
// patch the bottom-most one
|
||||
auto current = const_cast<AstNode*>(parser->ast()->findExpansionSubNode($1));
|
||||
TRI_ASSERT(current->type == NODE_TYPE_EXPANSION);
|
||||
current->changeMember(1, parser->ast()->createNodeAttributeAccess(current->getMember(1), $3));
|
||||
$$ = $1;
|
||||
}
|
||||
else {
|
||||
|
@ -1063,7 +1116,9 @@ reference:
|
|||
if ($1->type == NODE_TYPE_EXPANSION) {
|
||||
// if left operand is an expansion already...
|
||||
// patch the existing expansion
|
||||
$1->changeMember(1, parser->ast()->createNodeBoundAttributeAccess($1->getMember(1), $3));
|
||||
auto current = const_cast<AstNode*>(parser->ast()->findExpansionSubNode($1));
|
||||
TRI_ASSERT(current->type == NODE_TYPE_EXPANSION);
|
||||
current->changeMember(1, parser->ast()->createNodeBoundAttributeAccess(current->getMember(1), $3));
|
||||
$$ = $1;
|
||||
}
|
||||
else {
|
||||
|
@ -1075,48 +1130,62 @@ reference:
|
|||
if ($1->type == NODE_TYPE_EXPANSION) {
|
||||
// if left operand is an expansion already...
|
||||
// patch the existing expansion
|
||||
$1->changeMember(1, parser->ast()->createNodeIndexedAccess($1->getMember(1), $3));
|
||||
auto current = const_cast<AstNode*>(parser->ast()->findExpansionSubNode($1));
|
||||
TRI_ASSERT(current->type == NODE_TYPE_EXPANSION);
|
||||
current->changeMember(1, parser->ast()->createNodeIndexedAccess(current->getMember(1), $3));
|
||||
$$ = $1;
|
||||
}
|
||||
else {
|
||||
$$ = parser->ast()->createNodeIndexedAccess($1, $3);
|
||||
}
|
||||
}
|
||||
| reference T_ARRAY_OPEN T_TIMES T_ARRAY_CLOSE %prec EXPANSION {
|
||||
// variable expansion, e.g. variable[*]
|
||||
| reference T_ARRAY_OPEN array_filter_operator {
|
||||
// variable expansion, e.g. variable[*], with optional FILTER, LIMIT and RETURN clauses
|
||||
if ($3 > 1 && $1->type == NODE_TYPE_EXPANSION) {
|
||||
// create a dummy passthru node that reduces and evaluates the expansion first
|
||||
// and the expansion on top of the stack won't be chained with any other expansions
|
||||
$1 = parser->ast()->createNodePassthru($1);
|
||||
}
|
||||
|
||||
// create a temporary iterator variable
|
||||
std::string const nextName = parser->ast()->variables()->nextName() + "_";
|
||||
char const* iteratorName = nextName.c_str();
|
||||
auto iterator = parser->ast()->createNodeIterator(iteratorName, $1);
|
||||
$$ = parser->ast()->createNodeExpansion(iterator, parser->ast()->createNodeReference(iteratorName), false);
|
||||
}
|
||||
| reference T_ARRAY_OPEN T_TIMES T_TIMES T_ARRAY_CLOSE %prec EXPANSION {
|
||||
// variable expansion, e.g. variable[**]
|
||||
// create a temporary iterator variable
|
||||
std::string const nextName = parser->ast()->variables()->nextName() + "_";
|
||||
char const* iteratorName = nextName.c_str();
|
||||
auto iterator = parser->ast()->createNodeIterator(iteratorName, $1);
|
||||
$$ = parser->ast()->createNodeExpansion(iterator, parser->ast()->createNodeReference(iteratorName), true);
|
||||
}
|
||||
| reference T_ARRAY_OPEN T_FILTER {
|
||||
// variable expansion, e.g. variable[*]
|
||||
// create a temporary iterator variable
|
||||
std::string const nextName = parser->ast()->variables()->nextName() + "_";
|
||||
char const* iteratorName = nextName.c_str();
|
||||
auto iterator = parser->ast()->createNodeIterator(iteratorName, $1);
|
||||
parser->pushStack(iterator);
|
||||
|
||||
if ($1->type == NODE_TYPE_EXPANSION) {
|
||||
auto iterator = parser->ast()->createNodeIterator(iteratorName, $1->getMember(1));
|
||||
parser->pushStack(iterator);
|
||||
}
|
||||
else {
|
||||
auto iterator = parser->ast()->createNodeIterator(iteratorName, $1);
|
||||
parser->pushStack(iterator);
|
||||
}
|
||||
|
||||
auto scopes = parser->ast()->scopes();
|
||||
scopes->stackCurrentVariable(scopes->getVariable(iteratorName));
|
||||
} expression T_ARRAY_CLOSE %prec EXPANSION {
|
||||
} optional_array_filter optional_array_limit optional_array_return T_ARRAY_CLOSE %prec EXPANSION {
|
||||
auto scopes = parser->ast()->scopes();
|
||||
scopes->unstackCurrentVariable();
|
||||
|
||||
auto iterator = static_cast<AstNode*>(parser->popStack());
|
||||
auto iterator = static_cast<AstNode const*>(parser->popStack());
|
||||
auto variableNode = iterator->getMember(0);
|
||||
TRI_ASSERT(variableNode->type == NODE_TYPE_VARIABLE);
|
||||
auto variable = static_cast<Variable const*>(variableNode->getData());
|
||||
$$ = parser->ast()->createNodeExpansion(iterator, parser->ast()->createNodeReference(variable->name.c_str()), $5);
|
||||
|
||||
if ($1->type == NODE_TYPE_EXPANSION) {
|
||||
auto expand = parser->ast()->createNodeExpansion($3, iterator, parser->ast()->createNodeReference(variable->name.c_str()), $5, $6, $7);
|
||||
$1->changeMember(1, expand);
|
||||
$$ = $1;
|
||||
/*
|
||||
auto current = const_cast<AstNode*>(parser->ast()->findExpansionSubNode($1));
|
||||
TRI_ASSERT(current->type == NODE_TYPE_EXPANSION);
|
||||
auto expand = parser->ast()->createNodeExpansion($3, iterator, parser->ast()->createNodeReference(variable->name.c_str()), $5, $6, $7);
|
||||
current->changeMember(1, expand);
|
||||
$$ = $1;
|
||||
*/
|
||||
}
|
||||
else {
|
||||
$$ = parser->ast()->createNodeExpansion($3, iterator, parser->ast()->createNodeReference(variable->name.c_str()), $5, $6, $7);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
@ -986,85 +986,6 @@ function GET_INDEX (value, index) {
|
|||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief get an indexed value from an array or document (e.g. users[3])
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function GET_RANGE (value, low, high) {
|
||||
'use strict';
|
||||
|
||||
if (TYPEWEIGHT(value) !== TYPEWEIGHT_ARRAY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
low = parseInt(low, 10);
|
||||
high = parseInt(high, 10);
|
||||
|
||||
var result = [ ], position;
|
||||
|
||||
if (low < 0) {
|
||||
low = value.length + low;
|
||||
}
|
||||
if (high < 0) {
|
||||
high = value.length + high;
|
||||
}
|
||||
|
||||
if (low <= high) {
|
||||
++high;
|
||||
if (low < high) {
|
||||
if (low < 0) {
|
||||
low = 0;
|
||||
}
|
||||
if (high >= value.length) {
|
||||
high = value.length;
|
||||
}
|
||||
if (low < high) {
|
||||
for (position = low; position < high; ++position) {
|
||||
result.push(value[position]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
--high;
|
||||
if (low >= value.length) {
|
||||
low = value.length - 1;
|
||||
}
|
||||
if (high < -1) {
|
||||
high = -1;
|
||||
}
|
||||
if (low - high > 0) {
|
||||
for (position = low; position > high; --position) {
|
||||
result.push(value[position]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function COMPACT (value) {
|
||||
'use strict';
|
||||
|
||||
if (TYPEWEIGHT(value) !== TYPEWEIGHT_ARRAY) {
|
||||
return [ ];
|
||||
}
|
||||
|
||||
var result = [ ];
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
var v = value[i];
|
||||
if (TYPEWEIGHT(v) === TYPEWEIGHT_ARRAY) {
|
||||
for (var j = 0; j < v.length; ++j) {
|
||||
result.push(v[j]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.push(v);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief normalize a value for comparison, sorting etc.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -3187,7 +3108,7 @@ function AQL_SHIFT (list) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract a slice from a list
|
||||
/// @brief extract a slice from an array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function AQL_SLICE (value, from, to) {
|
||||
|
@ -8435,7 +8356,6 @@ function AQL_GRAPH_DIAMETER (graphName, options) {
|
|||
exports.FCALL_USER = FCALL_USER;
|
||||
exports.KEYS = KEYS;
|
||||
exports.GET_INDEX = GET_INDEX;
|
||||
exports.GET_RANGE = GET_RANGE;
|
||||
exports.DOCUMENT_MEMBER = DOCUMENT_MEMBER;
|
||||
exports.GET_DOCUMENTS = GET_DOCUMENTS;
|
||||
exports.TERNARY_OPERATOR = TERNARY_OPERATOR;
|
||||
|
@ -8458,7 +8378,6 @@ exports.ARITHMETIC_MINUS = ARITHMETIC_MINUS;
|
|||
exports.ARITHMETIC_TIMES = ARITHMETIC_TIMES;
|
||||
exports.ARITHMETIC_DIVIDE = ARITHMETIC_DIVIDE;
|
||||
exports.ARITHMETIC_MODULUS = ARITHMETIC_MODULUS;
|
||||
exports.COMPACT = COMPACT;
|
||||
|
||||
exports.AQL_DOCUMENT = AQL_DOCUMENT;
|
||||
exports.AQL_COLLECTIONS = AQL_COLLECTIONS;
|
||||
|
|
|
@ -35,14 +35,59 @@ var jsunity = require("jsunity");
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function arrayAccessTestSuite () {
|
||||
var values = [
|
||||
var persons = [
|
||||
{ name: "sir alfred", age: 60, loves: [ "lettuce", "flowers" ] },
|
||||
{ person: { name: "gadgetto", age: 50, loves: "gadgets" } },
|
||||
{ name: "everybody", loves: "sunshine" },
|
||||
{ name: "judge", loves: [ "order", "policing", "weapons" ] },
|
||||
"someone"
|
||||
"something"
|
||||
];
|
||||
|
||||
var continents = [
|
||||
{
|
||||
name: "Europe", countries: [
|
||||
{ id: "UK", capital: "London" },
|
||||
{ id: "FR", capital: "Paris" },
|
||||
{ id: "IT", capital: "Rome" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Asia", countries: [
|
||||
{ id: "JP", capital: "Tokyo" },
|
||||
{ id: "CN", capital: "Beijing" },
|
||||
{ id: "KR", capital: "Seoul" },
|
||||
{ id: "IN", capital: "Delhi" }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var tags = [
|
||||
{
|
||||
values: [
|
||||
{ name: "javascript", count: 2, payload: [ { name: "foo" }, { name: "bar" } ] },
|
||||
{ name: "cpp", count: 4, payload: [ { name: "baz" }, { name: "qux" } ] },
|
||||
{ name: "php", count: 2, payload: [ ] },
|
||||
{ name: "ruby", count: 5, payload: [ { name: "test" }, { name: 23 } ] },
|
||||
{ name: "python", count: 3, payload: [ { name: "one" }, { name: "two" }, { name: "three" }, { name: "four" } ] }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var arrayOfArrays = [
|
||||
[
|
||||
[ "one", "two", "three" ],
|
||||
[ "four", "five" ],
|
||||
[ "six" ]
|
||||
],
|
||||
[
|
||||
[ "seven", "eight" ]
|
||||
],
|
||||
[
|
||||
[ "nine", "ten" ],
|
||||
[ "eleven", "twelve" ]
|
||||
]
|
||||
];
|
||||
|
||||
return {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -154,296 +199,512 @@ function arrayAccessTestSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSubqueryResult : function () {
|
||||
for (var i = 0; i < values.length; ++i) {
|
||||
var result = AQL_EXECUTE("RETURN (FOR value IN @values RETURN value)[" + i + "]", { values: values }).json;
|
||||
assertEqual([ values[i] ], result);
|
||||
for (var i = 0; i < persons.length; ++i) {
|
||||
var result = AQL_EXECUTE("RETURN (FOR value IN @persons RETURN value)[" + i + "]", { persons: persons }).json;
|
||||
assertEqual([ persons[i] ], result);
|
||||
}
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test range result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSubqueryResultRangeForward1 : function () {
|
||||
for (var to = 0; to < 10; ++to) {
|
||||
var result = AQL_EXECUTE("RETURN (FOR value IN @values RETURN value)[0.." + to + "]", { values: values }).json;
|
||||
var expected = [ ];
|
||||
for (var i = 0; i < Math.min(to + 1, values.length); ++i) {
|
||||
expected.push(values[i]);
|
||||
}
|
||||
assertEqual([ expected ], result);
|
||||
}
|
||||
testStarExtractScalar : function () {
|
||||
var expected = [ "Europe", "Asia" ];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].name", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test range result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSubqueryResultRangeForward2 : function () {
|
||||
for (var from = 0; from < 10; ++from) {
|
||||
var result = AQL_EXECUTE("RETURN (FOR value IN @values RETURN value)[" + from + "..99]", { values: values }).json;
|
||||
var expected = [ ];
|
||||
for (var i = Math.min(from, values.length); i < values.length; ++i) {
|
||||
expected.push(values[i]);
|
||||
}
|
||||
assertEqual([ expected ], result);
|
||||
}
|
||||
testStarExtractScalarUndefinedAttribute : function () {
|
||||
var expected = [ null, null ];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].foobar", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test range result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSubqueryResultRangeForwardMisc : function () {
|
||||
var test = function (from, to, v) {
|
||||
var result = AQL_EXECUTE("RETURN (FOR value IN @values RETURN value)[" + from + ".." + to + "]", { values: values }).json;
|
||||
var expected = v.map(function(v) { return values[v]; });
|
||||
assertEqual([ expected ], result);
|
||||
};
|
||||
|
||||
test(0, 0, [ 0 ]);
|
||||
test(0, 1, [ 0, 1 ]);
|
||||
test(0, 2, [ 0, 1, 2 ]);
|
||||
test(0, 3, [ 0, 1, 2, 3 ]);
|
||||
test(0, 4, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, 5, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, 6, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, 99, [ 0, 1, 2, 3, 4 ]);
|
||||
|
||||
test(1, 1, [ 1 ]);
|
||||
test(1, 2, [ 1, 2 ]);
|
||||
test(1, 3, [ 1, 2, 3 ]);
|
||||
test(1, 4, [ 1, 2, 3, 4 ]);
|
||||
test(1, 5, [ 1, 2, 3, 4 ]);
|
||||
|
||||
test(2, 2, [ 2 ]);
|
||||
test(2, 3, [ 2, 3 ]);
|
||||
|
||||
test(4, 4, [ 4 ]);
|
||||
test(4, 5, [ 4 ]);
|
||||
|
||||
test(5, 5, [ ]);
|
||||
test(5, 6, [ ]);
|
||||
test(1000, 1000, [ ]);
|
||||
test(1000000, 1000000, [ ]);
|
||||
|
||||
test(0, -1, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, -2, [ 0, 1, 2, 3 ]);
|
||||
test(0, -3, [ 0, 1, 2 ]);
|
||||
test(0, -4, [ 0, 1 ]);
|
||||
test(0, -5, [ 0 ]);
|
||||
test(0, -6, [ 0 ]);
|
||||
test(0, -7, [ 0 ]);
|
||||
|
||||
test(1, 0, [ 1, 0 ]);
|
||||
test(1, -1, [ 1, 2, 3, 4 ]);
|
||||
test(1, -2, [ 1, 2, 3 ]);
|
||||
test(1, -3, [ 1, 2 ]);
|
||||
test(1, -4, [ 1 ]);
|
||||
test(1, -5, [ 1, 0 ]);
|
||||
test(1, -6, [ 1, 0 ]);
|
||||
test(1, -7, [ 1, 0 ]);
|
||||
|
||||
test(2, 0, [ 2, 1, 0 ]);
|
||||
test(2, 1, [ 2, 1 ]);
|
||||
test(2, -1, [ 2, 3, 4 ]);
|
||||
test(2, -2, [ 2, 3 ]);
|
||||
test(2, -3, [ 2 ]);
|
||||
test(2, -4, [ 2, 1 ]);
|
||||
test(2, -5, [ 2, 1, 0 ]);
|
||||
test(2, -6, [ 2, 1, 0 ]);
|
||||
test(2, -7, [ 2, 1, 0 ]);
|
||||
|
||||
test(3, 0, [ 3, 2, 1, 0 ]);
|
||||
test(3, 1, [ 3, 2, 1 ]);
|
||||
test(3, 2, [ 3, 2 ]);
|
||||
test(3, -1, [ 3, 4 ]);
|
||||
test(3, -2, [ 3 ]);
|
||||
test(3, -3, [ 3, 2 ]);
|
||||
test(3, -4, [ 3, 2, 1 ]);
|
||||
test(3, -5, [ 3, 2, 1, 0 ]);
|
||||
test(3, -6, [ 3, 2, 1, 0 ]);
|
||||
|
||||
test(4, 0, [ 4, 3, 2, 1, 0 ]);
|
||||
test(4, 1, [ 4, 3, 2, 1 ]);
|
||||
test(4, 2, [ 4, 3, 2 ]);
|
||||
test(4, 3, [ 4, 3 ]);
|
||||
test(4, -1, [ 4 ]);
|
||||
test(4, -2, [ 4, 3 ]);
|
||||
test(4, -3, [ 4, 3, 2 ]);
|
||||
test(4, -4, [ 4, 3, 2, 1 ]);
|
||||
test(4, -5, [ 4, 3, 2, 1, 0 ]);
|
||||
test(4, -6, [ 4, 3, 2, 1, 0 ]);
|
||||
|
||||
test(5, 0, [ 4, 3, 2, 1, 0 ]);
|
||||
test(5, 1, [ 4, 3, 2, 1 ]);
|
||||
test(5, 2, [ 4, 3, 2 ]);
|
||||
test(5, 3, [ 4, 3 ]);
|
||||
test(5, -1, [ 4 ]);
|
||||
test(5, -2, [ 4, 3 ]);
|
||||
test(5, -3, [ 4, 3, 2 ]);
|
||||
test(5, -4, [ 4, 3, 2, 1 ]);
|
||||
test(5, -5, [ 4, 3, 2, 1, 0 ]);
|
||||
test(5, -6, [ 4, 3, 2, 1, 0 ]);
|
||||
|
||||
test(6, 6, [ ]);
|
||||
test(6, 7, [ ]);
|
||||
test(7, 6, [ ]);
|
||||
test(10, 10, [ ]);
|
||||
test(100, 100, [ ]);
|
||||
test(100, 1000, [ ]);
|
||||
test(1000, 100, [ ]);
|
||||
|
||||
test(-6, -6, [ ]);
|
||||
test(-6, -7, [ ]);
|
||||
test(-7, -6, [ ]);
|
||||
test(-10, -10, [ ]);
|
||||
test(-100, -100, [ ]);
|
||||
test(-100, -1000, [ ]);
|
||||
test(-1000, -100, [ ]);
|
||||
testStarExtractScalarWithFilter : function () {
|
||||
var expected = [ "Europe" ];
|
||||
var result = AQL_EXECUTE("RETURN @value[* FILTER CURRENT.name == 'Europe'].name", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test range result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testV8SubqueryResultRangeForward1 : function () {
|
||||
for (var to = 0; to < 10; ++to) {
|
||||
var result = AQL_EXECUTE("RETURN NOOPT(V8(FOR value IN @values RETURN value))[0.." + to + "]", { values: values }).json;
|
||||
var expected = [ ];
|
||||
for (var i = 0; i < Math.min(to + 1, values.length); ++i) {
|
||||
expected.push(values[i]);
|
||||
}
|
||||
assertEqual([ expected ], result);
|
||||
}
|
||||
testStarExtractScalarWithNonMatchingFilter : function () {
|
||||
var expected = [ ];
|
||||
var result = AQL_EXECUTE("RETURN @value[* FILTER CURRENT.name == 'foobar'].name", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractArray : function () {
|
||||
var expected = [
|
||||
[
|
||||
{ id: "UK", capital: "London" },
|
||||
{ id: "FR", capital: "Paris" },
|
||||
{ id: "IT", capital: "Rome" }
|
||||
],
|
||||
[
|
||||
{ id: "JP", capital: "Tokyo" },
|
||||
{ id: "CN", capital: "Beijing" },
|
||||
{ id: "KR", capital: "Seoul" },
|
||||
{ id: "IN", capital: "Delhi" }
|
||||
]
|
||||
];
|
||||
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test range result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testV8SubqueryResultRangeForward2 : function () {
|
||||
for (var from = 0; from < 10; ++from) {
|
||||
var result = AQL_EXECUTE("RETURN NOOPT(V8(FOR value IN @values RETURN value))[" + from + "..99]", { values: values }).json;
|
||||
var expected = [ ];
|
||||
for (var i = Math.min(from, values.length); i < values.length; ++i) {
|
||||
expected.push(values[i]);
|
||||
}
|
||||
assertEqual([ expected ], result);
|
||||
}
|
||||
testStarExtractArrayWithFilter : function () {
|
||||
var expected = [ continents[0].countries ];
|
||||
var result = AQL_EXECUTE("RETURN @value[* FILTER CURRENT.name == 'Europe'].countries", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test range result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
testStarExtractSubArrayIndexed1 : function () {
|
||||
var expected = [
|
||||
{ id: "UK", capital: "London" },
|
||||
{ id: "JP", capital: "Tokyo" }
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[0]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testV8SubqueryResultRangeForwardMisc : function () {
|
||||
var test = function (from, to, v) {
|
||||
var result = AQL_EXECUTE("RETURN NOOPT(V8(FOR value IN @values RETURN value))[" + from + ".." + to + "]", { values: values }).json;
|
||||
var expected = v.map(function(v) { return values[v]; });
|
||||
assertEqual([ expected ], result);
|
||||
};
|
||||
testStarExtractSubArrayIndexed2 : function () {
|
||||
var expected = [
|
||||
{ id: "IT", capital: "Rome" },
|
||||
{ id: "IN", capital: "Delhi" }
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[-1]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(0, 0, [ 0 ]);
|
||||
test(0, 1, [ 0, 1 ]);
|
||||
test(0, 2, [ 0, 1, 2 ]);
|
||||
test(0, 3, [ 0, 1, 2, 3 ]);
|
||||
test(0, 4, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, 5, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, 6, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, 99, [ 0, 1, 2, 3, 4 ]);
|
||||
testStarExtractSubArrayIndexed3 : function () {
|
||||
var expected = [
|
||||
null,
|
||||
null
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[99]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(1, 1, [ 1 ]);
|
||||
test(1, 2, [ 1, 2 ]);
|
||||
test(1, 3, [ 1, 2, 3 ]);
|
||||
test(1, 4, [ 1, 2, 3, 4 ]);
|
||||
test(1, 5, [ 1, 2, 3, 4 ]);
|
||||
testStarExtractSubArrayIndexedTotal1 : function () {
|
||||
var expected = [
|
||||
{ id: "UK", capital: "London" },
|
||||
{ id: "FR", capital: "Paris" },
|
||||
{ id: "IT", capital: "Rome" }
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN (@value[*].countries)[0]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(2, 2, [ 2 ]);
|
||||
test(2, 3, [ 2, 3 ]);
|
||||
testStarExtractSubArrayIndexedTotal2 : function () {
|
||||
var expected = [
|
||||
{ id: "JP", capital: "Tokyo" },
|
||||
{ id: "CN", capital: "Beijing" },
|
||||
{ id: "KR", capital: "Seoul" },
|
||||
{ id: "IN", capital: "Delhi" }
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN (@value[*].countries)[-1]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(4, 4, [ 4 ]);
|
||||
test(4, 5, [ 4 ]);
|
||||
testStarExtractSubAttribute1 : function () {
|
||||
var expected = [
|
||||
[ "UK", "FR", "IT" ],
|
||||
[ "JP", "CN", "KR", "IN" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].id", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(5, 5, [ ]);
|
||||
test(5, 6, [ ]);
|
||||
test(1000, 1000, [ ]);
|
||||
test(1000000, 1000000, [ ]);
|
||||
|
||||
test(0, -1, [ 0, 1, 2, 3, 4 ]);
|
||||
test(0, -2, [ 0, 1, 2, 3 ]);
|
||||
test(0, -3, [ 0, 1, 2 ]);
|
||||
test(0, -4, [ 0, 1 ]);
|
||||
test(0, -5, [ 0 ]);
|
||||
test(0, -6, [ 0 ]);
|
||||
test(0, -7, [ 0 ]);
|
||||
testStarExtractSubAttribute2 : function () {
|
||||
var expected = [
|
||||
[ "London", "Paris", "Rome" ],
|
||||
[ "Tokyo", "Beijing", "Seoul", "Delhi" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].capital", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(1, 0, [ 1, 0 ]);
|
||||
test(1, -1, [ 1, 2, 3, 4 ]);
|
||||
test(1, -2, [ 1, 2, 3 ]);
|
||||
test(1, -3, [ 1, 2 ]);
|
||||
test(1, -4, [ 1 ]);
|
||||
test(1, -5, [ 1, 0 ]);
|
||||
test(1, -6, [ 1, 0 ]);
|
||||
test(1, -7, [ 1, 0 ]);
|
||||
testStarExtractSubAttributeExtraOperator1 : function () {
|
||||
var expected = [
|
||||
[ "UK", "FR", "IT" ],
|
||||
[ "JP", "CN", "KR", "IN" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].id[*]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(2, 0, [ 2, 1, 0 ]);
|
||||
test(2, 1, [ 2, 1 ]);
|
||||
test(2, -1, [ 2, 3, 4 ]);
|
||||
test(2, -2, [ 2, 3 ]);
|
||||
test(2, -3, [ 2 ]);
|
||||
test(2, -4, [ 2, 1 ]);
|
||||
test(2, -5, [ 2, 1, 0 ]);
|
||||
test(2, -6, [ 2, 1, 0 ]);
|
||||
test(2, -7, [ 2, 1, 0 ]);
|
||||
testStarExtractSubAttributeExtraOperator2 : function () {
|
||||
var expected = [
|
||||
[ "London", "Paris", "Rome" ],
|
||||
[ "Tokyo", "Beijing", "Seoul", "Delhi" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].capital[*]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(3, 0, [ 3, 2, 1, 0 ]);
|
||||
test(3, 1, [ 3, 2, 1 ]);
|
||||
test(3, 2, [ 3, 2 ]);
|
||||
test(3, -1, [ 3, 4 ]);
|
||||
test(3, -2, [ 3 ]);
|
||||
test(3, -3, [ 3, 2 ]);
|
||||
test(3, -4, [ 3, 2, 1 ]);
|
||||
test(3, -5, [ 3, 2, 1, 0 ]);
|
||||
test(3, -6, [ 3, 2, 1, 0 ]);
|
||||
testStarExtractSubAttribute1Combine : function () {
|
||||
var expected = [
|
||||
"UK", "FR", "IT", "JP", "CN", "KR", "IN"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[**].id", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(4, 0, [ 4, 3, 2, 1, 0 ]);
|
||||
test(4, 1, [ 4, 3, 2, 1 ]);
|
||||
test(4, 2, [ 4, 3, 2 ]);
|
||||
test(4, 3, [ 4, 3 ]);
|
||||
test(4, -1, [ 4 ]);
|
||||
test(4, -2, [ 4, 3 ]);
|
||||
test(4, -3, [ 4, 3, 2 ]);
|
||||
test(4, -4, [ 4, 3, 2, 1 ]);
|
||||
test(4, -5, [ 4, 3, 2, 1, 0 ]);
|
||||
test(4, -6, [ 4, 3, 2, 1, 0 ]);
|
||||
testStarExtractSubAttribute2Combine : function () {
|
||||
var expected = [
|
||||
"London", "Paris", "Rome", "Tokyo", "Beijing", "Seoul", "Delhi"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[**].capital", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(5, 0, [ 4, 3, 2, 1, 0 ]);
|
||||
test(5, 1, [ 4, 3, 2, 1 ]);
|
||||
test(5, 2, [ 4, 3, 2 ]);
|
||||
test(5, 3, [ 4, 3 ]);
|
||||
test(5, -1, [ 4 ]);
|
||||
test(5, -2, [ 4, 3 ]);
|
||||
test(5, -3, [ 4, 3, 2 ]);
|
||||
test(5, -4, [ 4, 3, 2, 1 ]);
|
||||
test(5, -5, [ 4, 3, 2, 1, 0 ]);
|
||||
test(5, -6, [ 4, 3, 2, 1, 0 ]);
|
||||
|
||||
test(6, 6, [ ]);
|
||||
test(6, 7, [ ]);
|
||||
test(7, 6, [ ]);
|
||||
test(10, 10, [ ]);
|
||||
test(100, 100, [ ]);
|
||||
test(100, 1000, [ ]);
|
||||
test(1000, 100, [ ]);
|
||||
testStarExtractSubAttribute3Combine : function () {
|
||||
var expected = [
|
||||
"London", "Paris", "Rome", "Tokyo", "Beijing", "Seoul", "Delhi"
|
||||
];
|
||||
// more than 2 asterisks won't change the result
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[****].capital", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
test(-6, -6, [ ]);
|
||||
test(-6, -7, [ ]);
|
||||
test(-7, -6, [ ]);
|
||||
test(-10, -10, [ ]);
|
||||
test(-100, -100, [ ]);
|
||||
test(-100, -1000, [ ]);
|
||||
test(-1000, -100, [ ]);
|
||||
testStarExtractSubAttributeNonExisting : function () {
|
||||
var expected = [
|
||||
[ null, null, null ],
|
||||
[ null, null, null, null ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].id.foobar", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractSubAttributeIndexed1 : function () {
|
||||
var expected = [
|
||||
[ null, null, null ],
|
||||
[ null, null, null, null ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].id[0]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractSubAttributeIndexed2 : function () {
|
||||
var expected = [
|
||||
[ null, null, null ],
|
||||
[ null, null, null, null ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].countries[*].id[1]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractSubAttributeIndexedTotal1 : function () {
|
||||
var expected = [
|
||||
"UK", "FR", "IT"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN (@value[*].countries[*].id)[0]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractSubAttributeIndexedTotal2 : function () {
|
||||
var expected = [
|
||||
"JP", "CN", "KR", "IN"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN (@value[*].countries[*].id)[1]", { value : continents }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered1 : function () {
|
||||
var expected = [
|
||||
"javascript", "php"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.count == 2].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered2 : function () {
|
||||
var expected = [
|
||||
"cpp", "ruby", "python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.count != 2].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered3 : function () {
|
||||
var expected = [
|
||||
"ruby"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.count != 2 && CURRENT.name == 'ruby'].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered4 : function () {
|
||||
var expected = [
|
||||
3
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER LENGTH(CURRENT.payload) == 4].count", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered5 : function () {
|
||||
var expected = [
|
||||
"php", "ruby", "python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.name IN [ 'php', 'python', 'ruby' ]].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered6 : function () {
|
||||
var expected = [
|
||||
[ "javascript", "php" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].values[* FILTER CURRENT.count == 2].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered7 : function () {
|
||||
var expected = [
|
||||
[ "cpp", "ruby", "python" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].values[* FILTER CURRENT.count != 2].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered8 : function () {
|
||||
var expected = [
|
||||
[ "ruby" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].values[* FILTER CURRENT.count != 2 && CURRENT.name == 'ruby'].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered9 : function () {
|
||||
var expected = [
|
||||
[ 3 ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].values[* FILTER LENGTH(CURRENT.payload) == 4].count", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractFiltered10 : function () {
|
||||
var expected = [
|
||||
[ "php", "ruby", "python" ]
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[*].values[* FILTER CURRENT.name IN [ 'php', 'python', 'ruby' ]].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractProject1 : function () {
|
||||
var expected = [
|
||||
2, 2, 0, 2, 4
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* RETURN LENGTH(CURRENT.payload)][**]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractProject2 : function () {
|
||||
var expected = [
|
||||
"x-javascript", "x-cpp", "x-php", "x-ruby", "x-python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[* RETURN CONCAT('x-', CURRENT)][**]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractProjectFilter : function () {
|
||||
var expected = [
|
||||
"x-cpp", "x-ruby", "x-python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.count >= 3].name[* RETURN CONCAT('x-', CURRENT)][**]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimitEmpty1 : function () {
|
||||
var expected = [
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 0]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimitEmpty2 : function () {
|
||||
var expected = [
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* LIMIT 0].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimitEmpty3 : function () {
|
||||
var expected = [
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* LIMIT -10, 1].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit0 : function () {
|
||||
var expected = [
|
||||
"javascript"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* LIMIT 1].name", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit1 : function () {
|
||||
var expected = [
|
||||
"javascript"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 1]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit2 : function () {
|
||||
var expected = [
|
||||
"javascript"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 0, 1]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit3 : function () {
|
||||
var expected = [
|
||||
"javascript", "cpp", "php", "ruby", "python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 0, 10]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit4 : function () {
|
||||
var expected = [
|
||||
"javascript", "cpp", "php", "ruby"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 0, 4]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit5 : function () {
|
||||
var expected = [
|
||||
"javascript", "cpp"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 0, 2]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit6 : function () {
|
||||
var expected = [
|
||||
"cpp", "php", "ruby", "python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 1, 4]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit7 : function () {
|
||||
var expected = [
|
||||
"ruby", "python"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[*].name[** LIMIT 3, 4]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimit8 : function () {
|
||||
var expected = [
|
||||
"javascript", "cpp"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.name IN [ 'javascript', 'php', 'cpp' ] LIMIT 0, 2].name[**]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimitProject1 : function () {
|
||||
var expected = [
|
||||
"javascript-x", "cpp-x"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[** LIMIT 0, 2 RETURN CONCAT(CURRENT.name, '-x')]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testStarExtractLimitProject2 : function () {
|
||||
var expected = [
|
||||
"javascript", "cpp"
|
||||
];
|
||||
var result = AQL_EXECUTE("RETURN @value[0].values[* FILTER CURRENT.name IN [ 'javascript', 'php', 'cpp' ] LIMIT 0, 2 RETURN CURRENT.name]", { value : tags }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapse1 : function () {
|
||||
var expected = arrayOfArrays;
|
||||
var result = AQL_EXECUTE("RETURN @value[*]", { value : arrayOfArrays }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapse2 : function () {
|
||||
var expected = [ [ "one", "two", "three" ], [ "four", "five" ], [ "six" ], [ "seven", "eight" ], [ "nine", "ten" ], [ "eleven", "twelve" ] ];
|
||||
var result = AQL_EXECUTE("RETURN @value[**]", { value : arrayOfArrays }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapse3 : function () {
|
||||
var expected = [ "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" ];
|
||||
var result = AQL_EXECUTE("RETURN @value[***]", { value : arrayOfArrays }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseUnmodified : function () {
|
||||
var data = [ [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ] ], [ 8 ] ];
|
||||
var expected = data;
|
||||
var result = AQL_EXECUTE("RETURN @value", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseMixed1 : function () {
|
||||
var data = [ [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ] ], [ 8 ] ];
|
||||
var expected = data;
|
||||
var result = AQL_EXECUTE("RETURN @value[*]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseMixed2 : function () {
|
||||
var data = [ [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ] ], [ 8 ] ];
|
||||
var expected = [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ], 8 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[**]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseMixed3 : function () {
|
||||
var data = [ [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ] ], [ 8 ] ];
|
||||
var expected = [ [ 1, 2 ], 3, 4, 5, 6, 7, 8 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[***]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseMixed4 : function () {
|
||||
var data = [ [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ] ], [ 8 ] ];
|
||||
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[****]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseMixed5 : function () {
|
||||
var data = [ [ [ [ 1, 2 ] ], [ 3, 4 ], 5, [ 6, 7 ] ], [ 8 ] ];
|
||||
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[*****]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseFilter : function () {
|
||||
var data = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
var expected = [ 1, 3, 5, 7 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[**** FILTER CURRENT % 2 != 0]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseProject : function () {
|
||||
var data = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
var expected = [ 2, 4, 6, 8, 10, 12, 14, 16 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[**** RETURN CURRENT * 2]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseFilterProject : function () {
|
||||
var data = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
var expected = [ 4, 8, 12, 16 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[**** FILTER CURRENT % 2 == 0 RETURN CURRENT * 2]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
},
|
||||
|
||||
testCollapseFilterProjectLimit : function () {
|
||||
var data = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||||
var expected = [ 4, 8, 12 ];
|
||||
var result = AQL_EXECUTE("RETURN @value[**** FILTER CURRENT % 2 == 0 LIMIT 3 RETURN CURRENT * 2]", { value : data }).json;
|
||||
assertEqual([ expected ], result);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -103,7 +103,7 @@ function optimizerRuleTestSuite () {
|
|||
"whether the execution of '" + query +
|
||||
"' this plan gave the wrong results: " + JSON.stringify(allresults.plans[j]) +
|
||||
" Should be: '" + JSON.stringify(allresults.results[0]) +
|
||||
"' but Is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
"' but is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ function optimizerRuleTestSuite () {
|
|||
"whether the execution of '" + query +
|
||||
"' this plan gave the wrong results: " + JSON.stringify(allresults.plans[j]) +
|
||||
" Should be: '" + JSON.stringify(allresults.results[0]) +
|
||||
"' but Is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
"' but is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -173,7 +173,7 @@ function optimizerRuleTestSuite () {
|
|||
"whether the execution of '" + query +
|
||||
"' this plan gave the wrong results: " + JSON.stringify(allresults.plans[j]) +
|
||||
" Should be: '" + JSON.stringify(allresults.results[0]) +
|
||||
"' but Is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
"' but is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -215,7 +215,7 @@ function optimizerRuleTestSuite () {
|
|||
"whether the execution of '" + query[0] +
|
||||
"' this plan gave the wrong results: " + JSON.stringify(allresults.plans[j]) +
|
||||
" Should be: '" + JSON.stringify(allresults.results[0]) +
|
||||
"' but Is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
"' but is: " + JSON.stringify(allresults.results[j]) + "'"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -163,7 +163,7 @@ function ahuacatlQueryVariablesTestSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testListExpansion6 : function () {
|
||||
var query = "FOR a IN " + JSON.stringify(airports) + " RETURN a.continent.countries[*].airports[*][0].name";
|
||||
var query = "FOR a IN " + JSON.stringify(airports) + " RETURN a.continent.countries[*].airports[0].name";
|
||||
var expected = [["CGN", "LHR", "CDG"], ["LGA", "YTO", "BOG"], ["NRT"]];
|
||||
|
||||
var actual = getQueryResults(query);
|
||||
|
@ -175,7 +175,7 @@ function ahuacatlQueryVariablesTestSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testListExpansion7 : function () {
|
||||
var query = "FOR a IN " + JSON.stringify(airports) + " RETURN a.continent.countries[*].airports[*][1].name";
|
||||
var query = "FOR a IN " + JSON.stringify(airports) + " RETURN a.continent.countries[*].airports[1].name";
|
||||
var expected = [["DTM", "LGW", "ORY"], ["JFK", "YVR", null], ["HND"]];
|
||||
|
||||
var actual = getQueryResults(query);
|
||||
|
|
Loading…
Reference in New Issue