mirror of https://gitee.com/bigwinds/arangodb
implemented FILTER, LIMIT and multi-star features
This commit is contained in:
parent
64089eea75
commit
7313b19431
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue