1
0
Fork 0

implemented FILTER, LIMIT and multi-star features

This commit is contained in:
Jan Steemann 2015-06-20 14:21:58 +02:00
parent 64089eea75
commit 7313b19431
4 changed files with 146 additions and 15 deletions

View File

@ -1406,6 +1406,7 @@ void Ast::validateAndOptimize () {
if (static_cast<TraversalContext*>(data)->hasSeenWriteNode) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_ACCESS_AFTER_MODIFICATION);
}
return node;
}
// example

View File

@ -1050,7 +1050,7 @@ void Executor::generateCodeExpansion (AstNode const* node) {
generateCodeNode(limitNode->getMember(0));
_buffer->appendChar(',');
generateCodeNode(limitNode->getMember(1));
_buffer->appendChar(')');
_buffer->appendText(",true)");
}
// RETURN

View File

@ -833,40 +833,166 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
else if (node->type == NODE_TYPE_EXPANSION) {
TRI_ASSERT(node->numMembers() == 5);
// LIMIT
int64_t offset = 0;
int64_t count = INT64_MAX;
auto limitNode = node->getMember(3);
if (limitNode->type != NODE_TYPE_NOP) {
TRI_document_collection_t const* subCollection = nullptr;
AqlValue sub = executeSimpleExpression(limitNode->getMember(0), &subCollection, trx, argv, startPos, vars, regs, false);
offset = sub.toInt64();
sub.destroy();
subCollection = nullptr;
sub = executeSimpleExpression(limitNode->getMember(1), &subCollection, trx, argv, startPos, vars, regs, false);
count = sub.toInt64();
sub.destroy();
}
if (offset < 0 || count <= 0) {
// no items to return... can already stop here
return AqlValue(new triagens::basics::Json(triagens::basics::Json::Array));
}
// FILTER
AstNode const* filterNode = node->getMember(2);
if (filterNode->type == NODE_TYPE_NOP) {
filterNode = nullptr;
}
else if (filterNode->isConstant()) {
if (filterNode->isTrue()) {
// filter expression is always true
filterNode = nullptr;
}
else {
// filter expression is always false
return AqlValue(new triagens::basics::Json(triagens::basics::Json::Array));
}
}
auto iterator = node->getMember(0);
auto variable = static_cast<Variable*>(iterator->getMember(0)->getData());
// TODO: implement flatten!
auto levels = node->getIntValue(true);
TRI_document_collection_t const* myCollection = nullptr;
AqlValue value = executeSimpleExpression(node->getMember(0), &myCollection, trx, argv, startPos, vars, regs, false);
AqlValue value;
if (! value.isArray()) {
if (levels > 1) {
// flatten value...
// generate a new temporary for the flattened array
std::unique_ptr<Json> flattened(new Json(Json::Array));
TRI_document_collection_t const* myCollection = nullptr;
value = executeSimpleExpression(node->getMember(0), &myCollection, trx, argv, startPos, vars, regs, false);
if (! value.isArray()) {
// must cast value to array first
FunctionParameters parameters{ std::make_pair(value, myCollection) };
auto res = Functions::ToArray(_ast->query(), trx, parameters);
// destroy old value and swap with function call result
value.destroy();
value = res;
}
std::function<void(TRI_json_t const*, int64_t)> flatten = [&] (TRI_json_t const* json, int64_t level) {
if (! TRI_IsArrayJson(json)) {
return;
}
size_t const n = TRI_LengthArrayJson(json);
for (size_t i = 0; i < n; ++i) {
auto item = static_cast<TRI_json_t const*>(TRI_AtVector(&json->_value._objects, i));
bool const isArray = TRI_IsArrayJson(item);
if (! isArray || level == levels) {
flattened->add(TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, item));
}
else if (isArray && level < levels) {
flatten(item, level + 1);
}
}
};
auto subJson = value.toJson(trx, myCollection, false);
flatten(subJson.json(), 1);
value.destroy();
return AqlValue(new triagens::basics::Json(triagens::basics::Json::Array));
value = AqlValue(flattened.release());
}
else {
TRI_document_collection_t const* myCollection = nullptr;
value = executeSimpleExpression(node->getMember(0), &myCollection, trx, argv, startPos, vars, regs, false);
if (! value.isArray()) {
// must cast value to array first
FunctionParameters parameters{ std::make_pair(value, myCollection) };
auto res = Functions::ToArray(_ast->query(), trx, parameters);
// destroy old value and swap with function call result
value.destroy();
value = res;
}
}
// RETURN
// the default is to return array member unmodified
AstNode const* projectionNode = node->getMember(1);
if (node->getMember(4)->type != NODE_TYPE_NOP) {
// return projection
projectionNode = node->getMember(4);
}
size_t const n = value.arraySize();
std::unique_ptr<Json> array(new Json(Json::Array, n));
size_t projectionNode = 1;
if (node->getMember(4)->type != NODE_TYPE_NOP) {
projectionNode = 4;
}
for (size_t i = 0; i < n; ++i) {
// TODO: check why we must copy the array member. will crash without copying!
TRI_document_collection_t const* myCollection = nullptr;
auto arrayItem = value.extractArrayMember(trx, myCollection, i, true);
setVariable(variable, arrayItem.json());
TRI_document_collection_t const* subCollection = nullptr;
AqlValue sub = executeSimpleExpression(node->getMember(projectionNode), &subCollection, trx, argv, startPos, vars, regs, true);
array->add(sub.toJson(trx, subCollection, true));
bool takeItem = true;
if (filterNode != nullptr) {
// have a filter
TRI_document_collection_t const* subCollection = nullptr;
AqlValue sub = executeSimpleExpression(filterNode, &subCollection, trx, argv, startPos, vars, regs, false);
takeItem = sub.isTrue();
sub.destroy();
}
if (takeItem && offset > 0) {
// there is an offset in place
--offset;
takeItem = false;
}
if (takeItem) {
TRI_document_collection_t const* subCollection = nullptr;
AqlValue sub = executeSimpleExpression(projectionNode, &subCollection, trx, argv, startPos, vars, regs, true);
array->add(sub.toJson(trx, subCollection, true));
}
clearVariable(variable);
arrayItem.destroy();
if (takeItem && count > 0) {
// number of items to pick was restricted
if (--count == 0) {
// done
break;
}
}
}
value.destroy();

View File

@ -3111,7 +3111,7 @@ function AQL_SHIFT (list) {
/// @brief extract a slice from an array
////////////////////////////////////////////////////////////////////////////////
function AQL_SLICE (value, from, to) {
function AQL_SLICE (value, from, to, nonNegative) {
'use strict';
if (TYPEWEIGHT(value) !== TYPEWEIGHT_ARRAY) {
@ -3122,6 +3122,10 @@ function AQL_SLICE (value, from, to) {
from = AQL_TO_NUMBER(from);
to = AQL_TO_NUMBER(to);
if (nonNegative && (from < 0 || to < 0)) {
return [ ];
}
if (TYPEWEIGHT(to) === TYPEWEIGHT_NULL) {
to = undefined;
}