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) {
|
if (static_cast<TraversalContext*>(data)->hasSeenWriteNode) {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_ACCESS_AFTER_MODIFICATION);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_ACCESS_AFTER_MODIFICATION);
|
||||||
}
|
}
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
// example
|
// example
|
||||||
|
|
|
@ -1050,7 +1050,7 @@ void Executor::generateCodeExpansion (AstNode const* node) {
|
||||||
generateCodeNode(limitNode->getMember(0));
|
generateCodeNode(limitNode->getMember(0));
|
||||||
_buffer->appendChar(',');
|
_buffer->appendChar(',');
|
||||||
generateCodeNode(limitNode->getMember(1));
|
generateCodeNode(limitNode->getMember(1));
|
||||||
_buffer->appendChar(')');
|
_buffer->appendText(",true)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// RETURN
|
// RETURN
|
||||||
|
|
|
@ -833,40 +833,166 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
||||||
|
|
||||||
else if (node->type == NODE_TYPE_EXPANSION) {
|
else if (node->type == NODE_TYPE_EXPANSION) {
|
||||||
TRI_ASSERT(node->numMembers() == 5);
|
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 iterator = node->getMember(0);
|
||||||
auto variable = static_cast<Variable*>(iterator->getMember(0)->getData());
|
auto variable = static_cast<Variable*>(iterator->getMember(0)->getData());
|
||||||
// TODO: implement flatten!
|
|
||||||
auto levels = node->getIntValue(true);
|
auto levels = node->getIntValue(true);
|
||||||
|
|
||||||
TRI_document_collection_t const* myCollection = nullptr;
|
AqlValue value;
|
||||||
AqlValue value = executeSimpleExpression(node->getMember(0), &myCollection, trx, argv, startPos, vars, regs, false);
|
|
||||||
|
|
||||||
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();
|
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();
|
size_t const n = value.arraySize();
|
||||||
std::unique_ptr<Json> array(new Json(Json::Array, n));
|
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) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
// TODO: check why we must copy the array member. will crash without copying!
|
// 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);
|
auto arrayItem = value.extractArrayMember(trx, myCollection, i, true);
|
||||||
|
|
||||||
setVariable(variable, arrayItem.json());
|
setVariable(variable, arrayItem.json());
|
||||||
|
|
||||||
TRI_document_collection_t const* subCollection = nullptr;
|
bool takeItem = true;
|
||||||
AqlValue sub = executeSimpleExpression(node->getMember(projectionNode), &subCollection, trx, argv, startPos, vars, regs, true);
|
|
||||||
array->add(sub.toJson(trx, subCollection, 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);
|
clearVariable(variable);
|
||||||
|
|
||||||
arrayItem.destroy();
|
arrayItem.destroy();
|
||||||
|
|
||||||
|
if (takeItem && count > 0) {
|
||||||
|
// number of items to pick was restricted
|
||||||
|
if (--count == 0) {
|
||||||
|
// done
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value.destroy();
|
value.destroy();
|
||||||
|
|
|
@ -3111,7 +3111,7 @@ function AQL_SHIFT (list) {
|
||||||
/// @brief extract a slice from an array
|
/// @brief extract a slice from an array
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function AQL_SLICE (value, from, to) {
|
function AQL_SLICE (value, from, to, nonNegative) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
if (TYPEWEIGHT(value) !== TYPEWEIGHT_ARRAY) {
|
if (TYPEWEIGHT(value) !== TYPEWEIGHT_ARRAY) {
|
||||||
|
@ -3122,6 +3122,10 @@ function AQL_SLICE (value, from, to) {
|
||||||
from = AQL_TO_NUMBER(from);
|
from = AQL_TO_NUMBER(from);
|
||||||
to = AQL_TO_NUMBER(to);
|
to = AQL_TO_NUMBER(to);
|
||||||
|
|
||||||
|
if (nonNegative && (from < 0 || to < 0)) {
|
||||||
|
return [ ];
|
||||||
|
}
|
||||||
|
|
||||||
if (TYPEWEIGHT(to) === TYPEWEIGHT_NULL) {
|
if (TYPEWEIGHT(to) === TYPEWEIGHT_NULL) {
|
||||||
to = undefined;
|
to = undefined;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue