1
0
Fork 0

Bug fix/internal issue #627 (#10233)

* Implemented Array IN operators

* First implementation for array comparsion operators for SEARCH clause

* WIP

* WIP

* simplified filter building

* Adding Unit tests

* Added tests for IN/NIN

* Debuggin Array comparsion operators. Fixed ViewNode redundand register planning

* Fixed log id duplicate

* Fixed jslin

* Fixed Mac build

* Code cleanup

* WIP

* WIP

* Code cleanup

* fixed applying boost to all/empty filters

* Removed redundancy in filter generation

* cleanup
This commit is contained in:
Dronplane 2019-10-15 20:41:06 +03:00 committed by Andrey Abramov
parent a7fd2e68f7
commit cfce3d8df8
13 changed files with 5686 additions and 68 deletions

View File

@ -858,7 +858,7 @@ void IResearchViewNode::planNodeRegisters(
std::vector<aql::RegisterId>& nrRegsHere, std::vector<aql::RegisterId>& nrRegs,
std::unordered_map<aql::VariableId, aql::VarInfo>& varInfo,
unsigned int& totalNrRegs, unsigned int depth) const {
nrRegsHere.emplace_back(isLateMaterialized()? 0 : 1);
nrRegsHere.emplace_back(0);
// create a copy of the last value here
// this is requried because back returns a reference and emplace/push_back

View File

@ -28,10 +28,6 @@
using namespace arangodb::aql;
int64_t const Quantifier::NONE = 1;
int64_t const Quantifier::ALL = 2;
int64_t const Quantifier::ANY = 3;
/// @brief converts a quantifier string into an int equivalent
int64_t Quantifier::FromString(std::string const& value) {
if (value == "all") {

View File

@ -34,9 +34,9 @@ namespace aql {
struct AstNode;
struct Quantifier {
static int64_t const NONE;
static int64_t const ALL;
static int64_t const ANY;
static int64_t constexpr NONE = 1;
static int64_t constexpr ALL = 2;
static int64_t constexpr ANY = 3;
/// @brief converts a quantifier string into an int equivalent
static int64_t FromString(std::string const& value);

View File

@ -253,7 +253,7 @@ class ScopedAqlValue : private irs::util::noncopyable {
}
ScopedAqlValue(ScopedAqlValue&& rhs) noexcept
: _value(rhs._value), _node(rhs._node), _type(rhs._type) {
: _value(rhs._value), _node(rhs._node), _type(rhs._type), _executed(rhs._executed) {
rhs._node = &INVALID_NODE;
rhs._type = SCOPED_VALUE_TYPE_INVALID;
rhs._destroy = false;

View File

@ -40,6 +40,7 @@
#include "Aql/Ast.h"
#include "Aql/Function.h"
#include "Aql/Range.h"
#include "Aql/Quantifier.h"
#include "Basics/StringUtils.h"
#include "IResearch/AqlHelper.h"
#include "IResearch/ExpressionFilter.h"
@ -142,8 +143,7 @@ arangodb::iresearch::IResearchLinkMeta::Analyzer extractAnalyzerFromArg(
irs::boolean_filter const* filter, arangodb::aql::AstNode const* analyzerArg,
QueryContext const& ctx, size_t argIdx, irs::string_ref const& functionName) {
static const arangodb::iresearch::IResearchLinkMeta::Analyzer invalid( // invalid analyzer
nullptr, ""
);
nullptr, "");
if (!analyzerArg) {
auto message = "'"s + std::string(functionName.c_str(), functionName.size()) + "' AQL function: " + std::to_string(argIdx) + " argument is invalid analyzer";
@ -206,8 +206,7 @@ arangodb::iresearch::IResearchLinkMeta::Analyzer extractAnalyzerFromArg(
analyzer = analyzerFeature.get(analyzerId, ctx.trx->vocbase(), *sysVocbase);
shortName = arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
analyzerId, ctx.trx->vocbase(), *sysVocbase, false // args
);
analyzerId, ctx.trx->vocbase(), *sysVocbase, false); // args
}
} else {
analyzer = analyzerFeature.get(analyzerId); // verbatim
@ -221,17 +220,9 @@ arangodb::iresearch::IResearchLinkMeta::Analyzer extractAnalyzerFromArg(
return result;
}
arangodb::Result byTerm(irs::by_term* filter, arangodb::aql::AstNode const& attribute,
ScopedAqlValue const& value, QueryContext const& ctx,
FilterContext const& filterCtx) {
std::string name;
if (filter && !arangodb::iresearch::nameFromAttributeAccess(name, attribute, ctx)) {
auto message = "Failed to generate field name from node " + arangodb::aql::AstNode::toString(&attribute);
LOG_TOPIC("d4b6e", WARN, arangodb::iresearch::TOPIC) << message;
return {TRI_ERROR_BAD_PARAMETER, message};
}
arangodb::Result byTerm(irs::by_term* filter, std::string name,
ScopedAqlValue const& value, QueryContext const& ctx,
FilterContext const& filterCtx) {
switch (value.type()) {
case arangodb::iresearch::SCOPED_VALUE_TYPE_NULL:
if (filter) {
@ -294,6 +285,18 @@ arangodb::Result byTerm(irs::by_term* filter, arangodb::aql::AstNode const& attr
}
}
arangodb::Result byTerm(irs::by_term* filter, arangodb::aql::AstNode const& attribute,
ScopedAqlValue const& value, QueryContext const& ctx,
FilterContext const& filterCtx) {
std::string name;
if (filter && !arangodb::iresearch::nameFromAttributeAccess(name, attribute, ctx)) {
auto message = "Failed to generate field name from node " + arangodb::aql::AstNode::toString(&attribute);
LOG_TOPIC("d4b6e", WARN, arangodb::iresearch::TOPIC) << message;
return {TRI_ERROR_BAD_PARAMETER, message};
}
return byTerm(filter, std::move(name), value, ctx, filterCtx);
}
arangodb::Result byTerm(irs::by_term* filter, arangodb::iresearch::NormalizedCmpNode const& node,
QueryContext const& ctx, FilterContext const& filterCtx) {
TRI_ASSERT(node.attribute && node.attribute->isDeterministic());
@ -496,34 +499,8 @@ arangodb::Result byRange(irs::boolean_filter* filter, arangodb::aql::AstNode con
}
template <irs::Bound Bound>
arangodb::Result byRange(irs::boolean_filter* filter,
arangodb::iresearch::NormalizedCmpNode const& node, bool const incl,
QueryContext const& ctx, FilterContext const& filterCtx) {
TRI_ASSERT(node.attribute && node.attribute->isDeterministic());
TRI_ASSERT(node.value && node.value->isDeterministic());
ScopedAqlValue value(*node.value);
if (!value.isConstant()) {
if (!filter) {
// can't evaluate non constant filter before the execution
return {};
}
if (!value.execute(ctx)) {
// could not execute expression
return {TRI_ERROR_BAD_PARAMETER, "can not execute expression"};
}
}
std::string name;
if (filter && !nameFromAttributeAccess(name, *node.attribute, ctx)) {
auto message = "Failed to generate field name from node " + arangodb::aql::AstNode::toString(node.attribute);
LOG_TOPIC("1a218", WARN, arangodb::iresearch::TOPIC) << message;
return {TRI_ERROR_BAD_PARAMETER, message};
}
arangodb::Result byRange(irs::boolean_filter* filter, std::string name, const ScopedAqlValue& value,
bool const incl, QueryContext const& ctx, FilterContext const& filterCtx) {
switch (value.type()) {
case arangodb::iresearch::SCOPED_VALUE_TYPE_NULL: {
if (filter) {
@ -601,6 +578,34 @@ arangodb::Result byRange(irs::boolean_filter* filter,
}
}
template <irs::Bound Bound>
arangodb::Result byRange(irs::boolean_filter* filter,
arangodb::iresearch::NormalizedCmpNode const& node, bool const incl,
QueryContext const& ctx, FilterContext const& filterCtx) {
TRI_ASSERT(node.attribute && node.attribute->isDeterministic());
TRI_ASSERT(node.value && node.value->isDeterministic());
std::string name;
if (filter && !nameFromAttributeAccess(name, *node.attribute, ctx)) {
auto message = "Failed to generate field name from node " + arangodb::aql::AstNode::toString(node.attribute);
LOG_TOPIC("1a218", WARN, arangodb::iresearch::TOPIC) << message;
return {TRI_ERROR_BAD_PARAMETER, message};
}
auto value = ScopedAqlValue(*node.value);
if (!value.isConstant()) {
if (!filter) {
// can't evaluate non constant filter before the execution
return {};
}
if (!value.execute(ctx)) {
// could not execute expression
return { TRI_ERROR_BAD_PARAMETER, "can not execute expression" };
}
}
return byRange<Bound>(filter, name, value, incl, ctx, filterCtx);
}
arangodb::Result fromExpression(irs::boolean_filter* filter, QueryContext const& ctx,
FilterContext const& filterCtx,
std::shared_ptr<arangodb::aql::AstNode>&& node) {
@ -739,6 +744,398 @@ arangodb::Result fromRange(irs::boolean_filter* filter, QueryContext const& /*ct
return {};
}
std::pair<arangodb::Result, arangodb::aql::AstNodeType> buildBinaryArrayComparsionPreFilter(
irs::boolean_filter* &filter, arangodb::aql::AstNodeType arrayComparsion,
const arangodb::aql::AstNode* qualifierNode, size_t arraySize) {
TRI_ASSERT(qualifierNode);
auto qualifierType = qualifierNode->getIntValue(true);
arangodb::aql::AstNodeType expansionNodeType = arangodb::aql::NODE_TYPE_ROOT;
if (0 == arraySize) {
expansionNodeType = arangodb::aql::NODE_TYPE_ROOT; // no subfilters expansion needed
switch (qualifierType) {
case arangodb::aql::Quantifier::ANY:
if (filter) {
filter->add<irs::empty>();
}
break;
case arangodb::aql::Quantifier::ALL:
case arangodb::aql::Quantifier::NONE:
if (filter) {
filter->add<irs::all>();
}
break;
default:
TRI_ASSERT(false); // new qualifier added ?
return std::make_pair(
arangodb::Result(TRI_ERROR_NOT_IMPLEMENTED, "Unknown qualifier in Array comparison operator"),
arangodb::aql::AstNodeType::NODE_TYPE_ROOT);
}
} else {
// NONE is inverted ALL so do conversion
if (arangodb::aql::Quantifier::NONE == qualifierType) {
qualifierType = arangodb::aql::Quantifier::ALL;
switch (arrayComparsion) {
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN:
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NE:
arrayComparsion = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_IN;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_IN:
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ:
arrayComparsion = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GE:
arrayComparsion = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LT;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GT:
arrayComparsion = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LE;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LE:
arrayComparsion = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GT;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LT:
arrayComparsion = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GE;
break;
default:
TRI_ASSERT(false); // new array comparsion operator?
return std::make_pair(
arangodb::Result(TRI_ERROR_NOT_IMPLEMENTED, "Unknown Array NONE comparison operator"),
arangodb::aql::AstNodeType::NODE_TYPE_ROOT);
}
}
switch (qualifierType) {
case arangodb::aql::Quantifier::ALL:
// calculate node type for expanding operation
// As soon as array is left argument but for filter we place document to the left
// we reverse comparsion operation
switch (arrayComparsion) {
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_IN:
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::And>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN:
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NE:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Not>().filter<irs::Or>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LT:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::And>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LE:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::And>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GT:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::And>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LT;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GE:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::And>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LE;
break;
default:
TRI_ASSERT(false); // new array comparsion operator?
return std::make_pair(
arangodb::Result(TRI_ERROR_NOT_IMPLEMENTED, "Unknown Array ALL/NONE comparison operator"),
arangodb::aql::AstNodeType::NODE_TYPE_ROOT);
}
break;
case arangodb::aql::Quantifier::ANY: {
switch (arrayComparsion) {
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_IN:
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Or>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN:
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NE:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Not>().filter<irs::And>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GT:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Or>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LT;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GE:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Or>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LE;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LT:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Or>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT;
break;
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LE:
if (filter) {
filter = static_cast<irs::boolean_filter*>(&filter->add<irs::Or>());
}
expansionNodeType = arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE;
break;
default:
TRI_ASSERT(false); // new array comparsion operator?
return std::make_pair(
arangodb::Result(TRI_ERROR_NOT_IMPLEMENTED, "Unknown Array ANY comparison operator"),
arangodb::aql::AstNodeType::NODE_TYPE_ROOT);
}
break;
}
default:
TRI_ASSERT(false); // new qualifier added ?
return std::make_pair(
arangodb::Result(TRI_ERROR_NOT_IMPLEMENTED, "Unknown qualifier in Array comparison operator"),
arangodb::aql::AstNodeType::NODE_TYPE_ROOT);
}
}
return std::make_pair(TRI_ERROR_NO_ERROR, expansionNodeType);
}
class ByTermSubFilterFactory {
public:
static arangodb::Result byNodeSubFilter(irs::boolean_filter* filter,
arangodb::iresearch::NormalizedCmpNode const& node,
QueryContext const& ctx, FilterContext const& filterCtx) {
TRI_ASSERT(arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ == node.cmp);
iresearch::by_term* termFilter = nullptr;
if (filter) {
termFilter = &filter->add<irs::by_term>();
}
return byTerm(termFilter, node, ctx, filterCtx);
}
static arangodb::Result byValueSubFilter(irs::boolean_filter* filter, std::string fieldName, const ScopedAqlValue& value,
arangodb::aql::AstNodeType arrayExpansionNodeType,
QueryContext const& ctx, FilterContext const& filterCtx) {
TRI_ASSERT(arangodb::aql::NODE_TYPE_OPERATOR_BINARY_EQ == arrayExpansionNodeType);
iresearch::by_term* termFilter = nullptr;
if (filter) {
termFilter = &filter->add<irs::by_term>();
}
return byTerm(termFilter, std::move(fieldName), value, ctx, filterCtx);
}
};
class ByRangeSubFilterFactory {
public:
static arangodb::Result byNodeSubFilter(irs::boolean_filter* filter,
arangodb::iresearch::NormalizedCmpNode const& node,
QueryContext const& ctx, FilterContext const& filterCtx) {
bool incl, min;
std::tie(min, incl) = calcMinInclude(node.cmp);
return min ? byRange<irs::Bound::MIN>(filter, node, incl, ctx, filterCtx)
: byRange<irs::Bound::MAX>(filter, node, incl, ctx, filterCtx);
}
static arangodb::Result byValueSubFilter(irs::boolean_filter* filter, std::string fieldName, const ScopedAqlValue& value,
arangodb::aql::AstNodeType arrayExpansionNodeType,
QueryContext const& ctx, FilterContext const& filterCtx) {
bool incl, min;
std::tie(min, incl) = calcMinInclude(arrayExpansionNodeType);
return min ? byRange<irs::Bound::MIN>(filter, fieldName, value, incl, ctx, filterCtx)
: byRange<irs::Bound::MAX>(filter, fieldName, value, incl, ctx, filterCtx);
}
private:
static std::pair<bool, bool> calcMinInclude(arangodb::aql::AstNodeType arrayExpansionNodeType) {
TRI_ASSERT(arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LT == arrayExpansionNodeType ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LE == arrayExpansionNodeType ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT == arrayExpansionNodeType ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE == arrayExpansionNodeType);
return std::pair<bool, bool>(
// min
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT == arrayExpansionNodeType ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE == arrayExpansionNodeType,
// incl
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE == arrayExpansionNodeType ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LE == arrayExpansionNodeType);
}
};
template<typename SubFilterFactory>
arangodb::Result fromArrayComparsion(irs::boolean_filter*& filter, QueryContext const& ctx,
FilterContext const& filterCtx, arangodb::aql::AstNode const& node) {
TRI_ASSERT(arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LT == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LE == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GT == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GE == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NE == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_IN == node.type ||
arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN == node.type);
if (node.numMembers() != 3) {
auto rv = logMalformedNode(node.type);
return rv.reset(rv.errorNumber(), "error in Array comparsion operator " + rv.errorMessage());
}
auto const* valueNode = node.getMemberUnchecked(0);
TRI_ASSERT(valueNode);
auto const* attributeNode = node.getMemberUnchecked(1);
TRI_ASSERT(attributeNode);
auto const* qualifierNode = node.getMemberUnchecked(2);
TRI_ASSERT(qualifierNode);
if (qualifierNode->type != arangodb::aql::NODE_TYPE_QUANTIFIER) {
return { TRI_ERROR_BAD_PARAMETER, "wrong qualifier node type for Array comparison operator" };
}
if (arangodb::aql::NODE_TYPE_ARRAY == valueNode->type) {
if (!attributeNode->isDeterministic()) {
// not supported by IResearch, but could be handled by ArangoDB
return fromExpression(filter, ctx, filterCtx, node);
}
size_t const n = valueNode->numMembers();
if (!arangodb::iresearch::checkAttributeAccess(attributeNode, *ctx.ref)) {
// no attribute access specified in attribute node, try to
// find it in value node
bool attributeAccessFound = false;
for (size_t i = 0; i < n; ++i) {
attributeAccessFound |=
(nullptr != arangodb::iresearch::checkAttributeAccess(valueNode->getMemberUnchecked(i),
*ctx.ref));
}
if (!attributeAccessFound) {
return fromExpression(filter, ctx, filterCtx, node);
}
}
arangodb::Result buildRes;
arangodb::aql::AstNodeType arrayExpansionNodeType;
std::tie(buildRes, arrayExpansionNodeType) = buildBinaryArrayComparsionPreFilter(filter, node.type, qualifierNode, n);
if (!buildRes.ok()) {
return buildRes;
}
if (filter) {
filter->boost(filterCtx.boost);
}
if (arangodb::aql::NODE_TYPE_ROOT == arrayExpansionNodeType) {
// nothing to do more
return {};
}
FilterContext const subFilterCtx{
filterCtx.analyzer,
irs::no_boost() }; // reset boost
// Expand array interval as several binaryInterval nodes ('array' feature is ensured by pre-filter)
arangodb::iresearch::NormalizedCmpNode normalized;
arangodb::aql::AstNode toNormalize(arrayExpansionNodeType);
toNormalize.reserve(2);
for (size_t i = 0; i < n; ++i) {
auto const* member = valueNode->getMemberUnchecked(i);
TRI_ASSERT(member);
// edit in place for now; TODO change so we can replace instead
TEMPORARILY_UNLOCK_NODE(&toNormalize);
toNormalize.clearMembers();
toNormalize.addMember(attributeNode);
toNormalize.addMember(member);
toNormalize.flags = member->flags;
if (!arangodb::iresearch::normalizeCmpNode(toNormalize, *ctx.ref, normalized)) {
if (!filter) {
// can't evaluate non constant filter before the execution
return {};
}
// use std::shared_ptr since AstNode is not copyable/moveable
auto exprNode = std::make_shared<arangodb::aql::AstNode>(arrayExpansionNodeType);
exprNode->reserve(2);
exprNode->addMember(attributeNode);
exprNode->addMember(member);
// not supported by IResearch, but could be handled by ArangoDB
auto rv = fromExpression(filter, ctx, subFilterCtx, std::move(exprNode));
if (rv.fail()) {
return rv.reset(rv.errorNumber(), "while getting array: " + rv.errorMessage());
}
} else {
auto rv = SubFilterFactory::byNodeSubFilter(filter, normalized, ctx, subFilterCtx);
if (rv.fail()) {
return rv.reset(rv.errorNumber(), "while getting array: " + rv.errorMessage());
}
}
}
return {};
}
if (!node.isDeterministic() ||
!arangodb::iresearch::checkAttributeAccess(attributeNode, *ctx.ref) ||
arangodb::iresearch::findReference(*valueNode, *ctx.ref)) {
return fromExpression(filter, ctx, filterCtx, node);
}
if (!filter) {
// can't evaluate non constant filter before the execution
return {};
}
ScopedAqlValue value(*valueNode);
if (!value.execute(ctx)) {
// can't execute expression
auto message = "Unable to extract value from Array comparison operator";
LOG_TOPIC("f7a13", WARN, arangodb::iresearch::TOPIC) << message;
return {TRI_ERROR_BAD_PARAMETER, message};
}
switch (value.type()) {
case arangodb::iresearch::SCOPED_VALUE_TYPE_ARRAY: {
size_t const n = value.size();
arangodb::Result buildRes;
arangodb::aql::AstNodeType arrayExpansionNodeType;
std::tie(buildRes, arrayExpansionNodeType) = buildBinaryArrayComparsionPreFilter(filter, node.type, qualifierNode, n);
if (!buildRes.ok()) {
return buildRes;
}
filter->boost(filterCtx.boost);
if (arangodb::aql::NODE_TYPE_ROOT == arrayExpansionNodeType) {
// nothing to do more
return {};
}
FilterContext const subFilterCtx{
filterCtx.analyzer,
irs::no_boost() // reset boost
};
std::string fieldName;
if (filter && !nameFromAttributeAccess(fieldName, *attributeNode, ctx)) {
auto message = "Failed to generate field name from node " + arangodb::aql::AstNode::toString(attributeNode);
LOG_TOPIC("ff299", WARN, arangodb::iresearch::TOPIC) << message;
return { TRI_ERROR_BAD_PARAMETER, message };
}
for (size_t i = 0; i < n; ++i) {
auto rv = SubFilterFactory::byValueSubFilter(filter, fieldName, value.at(i), arrayExpansionNodeType, ctx, subFilterCtx);
if (rv.fail()) {
return rv.reset(rv.errorNumber(), "failed to create filter because: " + rv.errorMessage());
}
}
return {};
}
default:
break;
}
// wrong value node type
return {TRI_ERROR_BAD_PARAMETER, "wrong value node type for Array comparison operator"};
}
arangodb::Result fromInArray(irs::boolean_filter* filter, QueryContext const& ctx,
FilterContext const& filterCtx, arangodb::aql::AstNode const& node) {
TRI_ASSERT(arangodb::aql::NODE_TYPE_OPERATOR_BINARY_IN == node.type ||
@ -1181,8 +1578,7 @@ arangodb::Result fromFuncAnalyzer(irs::boolean_filter* filter, QueryContext cons
analyzer = analyzerFeature.get(analyzerIdValue, ctx.trx->vocbase(), *sysVocbase);
shortName = arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
analyzerIdValue, ctx.trx->vocbase(), *sysVocbase, false // args
);
analyzerIdValue, ctx.trx->vocbase(), *sysVocbase, false); // args
}
} else {
analyzer = analyzerFeature.get(analyzerIdValue); // verbatim
@ -1198,7 +1594,7 @@ arangodb::Result fromFuncAnalyzer(irs::boolean_filter* filter, QueryContext cons
FilterContext const subFilterContext(analyzerValue, filterCtx.boost); // override analyzer
auto rv = ::filter(filter, ctx, subFilterContext, *expressionArg);
if( rv.fail() ){
if (rv.fail()) {
return arangodb::Result{rv.errorNumber(), "failed to get filter for analyzer: " + analyzer->name() + " : " + rv.errorMessage()};
}
return rv;
@ -1266,7 +1662,7 @@ arangodb::Result fromFuncBoost(irs::boolean_filter* filter, QueryContext const&
filterCtx.boost * static_cast<float_t>(boostValue)};
auto rv = ::filter(filter, ctx, subFilterContext, *expressionArg);
if(rv.fail()){
if (rv.fail()) {
return arangodb::Result{rv.errorNumber(), "error in sub-filter context: " + rv.errorMessage()};
}
return {};
@ -1903,19 +2299,19 @@ arangodb::Result fromFuncInRange(irs::boolean_filter* filter, QueryContext const
// 4th argument defines inclusion of lower boundary
bool lhsInclude = false;
auto inc1 = getInclusion(args.getMemberUnchecked(3), lhsInclude, "4th");
if (inc1.fail()){
if (inc1.fail()) {
return inc1;
}
// 5th argument defines inclusion of upper boundary
bool rhsInclude = false;
auto inc2 = getInclusion(args.getMemberUnchecked(4), rhsInclude, "5th");
if (inc2.fail()){
if (inc2.fail()) {
return inc2;
}
auto rv = ::byRange(filter, *field, *lhsArg, lhsInclude, *rhsArg, rhsInclude, ctx, filterCtx);
if(rv.fail()){
if (rv.fail()) {
return {rv.errorNumber(), "error in byRange: " + rv.errorMessage()};
}
return {};
@ -2061,6 +2457,17 @@ arangodb::Result filter(irs::boolean_filter* filter, QueryContext const& queryCt
return fromGroup<irs::And>(filter, queryCtx, filterCtx, node);
case arangodb::aql::NODE_TYPE_OPERATOR_NARY_OR: // n-ary or
return fromGroup<irs::Or>(filter, queryCtx, filterCtx, node);
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_IN: // compare ARRAY in
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN: // compare ARRAY not in
// for iresearch filters IN and EQ queries will be actually the same
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ: // compare ARRAY ==
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_NE: // compare ARRAY !=
return fromArrayComparsion<ByTermSubFilterFactory>(filter, queryCtx, filterCtx, node);
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LT: // compare ARRAY <
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_LE: // compare ARRAY <=
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GT: // compare ARRAY >
case arangodb::aql::NODE_TYPE_OPERATOR_BINARY_ARRAY_GE: // compare ARRAY >=
return fromArrayComparsion<ByRangeSubFilterFactory>(filter, queryCtx, filterCtx, node);
default:
return fromExpression(filter, queryCtx, filterCtx, node);
}

View File

@ -137,6 +137,8 @@ set(ARANGODB_TESTS_SOURCES
IResearch/IResearchComparer-test.cpp
IResearch/IResearchDocument-test.cpp
IResearch/IResearchFeature-test.cpp
IResearch/IResearchFilterArrayIn-test.cpp
IResearch/IResearchFilterArrayInterval-test.cpp
IResearch/IResearchFilterBoolean-test.cpp
IResearch/IResearchFilterCompare-test.cpp
IResearch/IResearchFilterFunction-test.cpp

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,711 @@
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Andrei Lobov
////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include "analysis/analyzers.hpp"
#include "analysis/token_attributes.hpp"
#include "analysis/token_streams.hpp"
#include "search/all_filter.hpp"
#include "search/boolean_filter.hpp"
#include "search/column_existence_filter.hpp"
#include "search/granular_range_filter.hpp"
#include "search/phrase_filter.hpp"
#include "search/prefix_filter.hpp"
#include "search/range_filter.hpp"
#include "search/term_filter.hpp"
#include <velocypack/Parser.h>
#include "IResearch/ExpressionContextMock.h"
#include "IResearch/common.h"
#include "Mocks/LogLevels.h"
#include "Mocks/Servers.h"
#include "Mocks/StorageEngineMock.h"
#include "Aql/AqlFunctionFeature.h"
#include "Aql/Ast.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/ExpressionContext.h"
#include "Aql/Query.h"
#include "Cluster/ClusterFeature.h"
#include "GeneralServer/AuthenticationFeature.h"
#include "IResearch/AqlHelper.h"
#include "IResearch/ExpressionFilter.h"
#include "IResearch/IResearchAnalyzerFeature.h"
#include "IResearch/IResearchCommon.h"
#include "IResearch/IResearchFeature.h"
#include "IResearch/IResearchFilterFactory.h"
#include "IResearch/IResearchLinkMeta.h"
#include "IResearch/IResearchViewMeta.h"
#include "Logger/LogTopic.h"
#include "Logger/Logger.h"
#include "RestServer/AqlFeature.h"
#include "RestServer/DatabaseFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "RestServer/TraverserEngineRegistryFeature.h"
#include "RestServer/ViewTypesFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/Methods/Collections.h"
#if USE_ENTERPRISE
#include "Enterprise/Ldap/LdapFeature.h"
#endif
static const VPackBuilder systemDatabaseBuilder = dbArgsBuilder();
static const VPackSlice systemDatabaseArgs = systemDatabaseBuilder.slice();
// -----------------------------------------------------------------------------
// --SECTION-- setup / tear-down
// -----------------------------------------------------------------------------
class IResearchFilterArrayIntervalTest
: public ::testing::Test,
public arangodb::tests::LogSuppressor<arangodb::Logger::AUTHENTICATION, arangodb::LogLevel::ERR> {
protected:
arangodb::tests::mocks::MockAqlServer server;
private:
TRI_vocbase_t* _vocbase;
protected:
IResearchFilterArrayIntervalTest() {
arangodb::tests::init();
auto& functions = server.getFeature<arangodb::aql::AqlFunctionFeature>();
// register fake non-deterministic function in order to suppress optimizations
functions.add(arangodb::aql::Function{
"_NONDETERM_", ".",
arangodb::aql::Function::makeFlags(
// fake non-deterministic
arangodb::aql::Function::Flags::CanRunOnDBServer),
[](arangodb::aql::ExpressionContext*, arangodb::transaction::Methods*,
arangodb::aql::VPackFunctionParameters const& params) {
TRI_ASSERT(!params.empty());
return params[0];
}});
// register fake non-deterministic function in order to suppress optimizations
functions.add(arangodb::aql::Function{
"_FORWARD_", ".",
arangodb::aql::Function::makeFlags(
// fake deterministic
arangodb::aql::Function::Flags::Deterministic, arangodb::aql::Function::Flags::Cacheable,
arangodb::aql::Function::Flags::CanRunOnDBServer),
[](arangodb::aql::ExpressionContext*, arangodb::transaction::Methods*,
arangodb::aql::VPackFunctionParameters const& params) {
TRI_ASSERT(!params.empty());
return params[0];
}});
auto& analyzers = server.getFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
auto& dbFeature = server.getFeature<arangodb::DatabaseFeature>();
dbFeature.createDatabase(testDBInfo(server.server()), _vocbase); // required for IResearchAnalyzerFeature::emplace(...)
arangodb::methods::Collections::createSystem(*_vocbase, arangodb::tests::AnalyzerCollectionName,
false);
analyzers.emplace(
result, "testVocbase::test_analyzer", "TestAnalyzer",
arangodb::velocypack::Parser::fromJson("{ \"args\": \"abc\"}")->slice()); // cache analyzer
}
TRI_vocbase_t& vocbase() { return *_vocbase; }
}; // IResearchFilterSetup
namespace {
// Auxilary check lambdas. Need them to check by_range part of expected filter
auto checkLess = [](irs::boolean_filter::const_iterator& filter,
irs::bytes_ref const& term,
irs::string_ref const& field) {
ASSERT_EQ(irs::by_range::type(), filter->type());
auto& actual = dynamic_cast<irs::by_range const&>(*filter);
irs::by_range expected;
expected.field(field);
expected.term<irs::Bound::MIN>(term);
expected.include<irs::Bound::MIN>(false);
EXPECT_EQ(expected, actual);
};
auto checkLessEqual = [](irs::boolean_filter::const_iterator& filter,
irs::bytes_ref const& term,
irs::string_ref const& field) {
ASSERT_EQ(irs::by_range::type(), filter->type());
auto& actual = dynamic_cast<irs::by_range const&>(*filter);
irs::by_range expected;
expected.field(field);
expected.term<irs::Bound::MIN>(term);
expected.include<irs::Bound::MIN>(true);
EXPECT_EQ(expected, actual);
};
auto checkGreaterEqual = [](irs::boolean_filter::const_iterator& filter,
irs::bytes_ref const& term,
irs::string_ref const& field) {
ASSERT_EQ(irs::by_range::type(), filter->type());
auto& actual = dynamic_cast<irs::by_range const&>(*filter);
irs::by_range expected;
expected.field(field);
expected.term<irs::Bound::MAX>(term);
expected.include<irs::Bound::MAX>(true);
EXPECT_EQ(expected, actual);
};
auto checkGreater = [](irs::boolean_filter::const_iterator& filter,
irs::bytes_ref const& term,
irs::string_ref const& field) {
ASSERT_EQ(irs::by_range::type(), filter->type());
auto& actual = dynamic_cast<irs::by_range const&>(*filter);
irs::by_range expected;
expected.field(field);
expected.term<irs::Bound::MAX>(term);
expected.include<irs::Bound::MAX>(false);
EXPECT_EQ(expected, actual);
};
// Auxilary check lambdas. Need them to check root part of expected filter
auto checkAny = [](irs::Or& actual, iresearch::boost_t boost) {
EXPECT_EQ(1, actual.size());
EXPECT_EQ(irs::Or::type(), actual.begin()->type());
auto& root = dynamic_cast<const irs::Or&>(*actual.begin());
EXPECT_EQ(3, root.size());
EXPECT_EQ(boost, root.boost());
return root.begin();
};
auto checkAll = [](irs::Or& actual, iresearch::boost_t boost) {
EXPECT_EQ(1, actual.size());
EXPECT_EQ(irs::And::type(), actual.begin()->type());
auto& root = dynamic_cast<const irs::And&>(*actual.begin());
EXPECT_EQ(3, root.size());
EXPECT_EQ(boost, root.boost());
return root.begin();
};
auto checkNone = [](irs::Or& actual, iresearch::boost_t boost) {
// none for now is like All but with inverted interval check
EXPECT_EQ(1, actual.size());
EXPECT_EQ(irs::And::type(), actual.begin()->type());
auto& root = dynamic_cast<const irs::And&>(*actual.begin());
EXPECT_EQ(3, root.size());
EXPECT_EQ(boost, root.boost());
return root.begin();
};
std::vector<std::pair<std::string,
std::pair<std::function<irs::boolean_filter::const_iterator(irs::Or&, iresearch::boost_t)>,
std::function<void(irs::boolean_filter::const_iterator&,
irs::bytes_ref const&,
irs::string_ref const&)>>>>
intervalOperations = {
{"ANY >", {checkAny, checkGreater}}, {"ANY >=", {checkAny, checkGreaterEqual}},
{"ANY <", {checkAny, checkLess}}, {"ANY <=", {checkAny, checkLessEqual}},
{"ALL >", {checkAll, checkGreater}}, {"ALL >=", {checkAll, checkGreaterEqual}},
{"ALL <", {checkAll, checkLess}}, {"ALL <=", {checkAll, checkLessEqual}},
{"NONE >", {checkNone, checkLessEqual}}, {"NONE >=", {checkNone, checkLess}},
{"NONE <", {checkNone, checkGreaterEqual}}, {"NONE <=", {checkNone, checkGreater}}
};
std::string buildQueryString(const irs::string_ref queryPrefix,
const irs::string_ref operation,
const irs::string_ref querySuffix) {
return std::string(queryPrefix).append(" ")
.append(operation).append(" ").append(querySuffix);
}
} // namespace
TEST_F(IResearchFilterArrayIntervalTest, Interval) {
// simple attribute
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER ['1','2','3']",
operation.first, "d.a RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator, irs::ref_cast<irs::byte_type>(irs::string_ref("1")), mangleStringIdentity("a"));
++subFiltersIterator;
operation.second.second(subFiltersIterator, irs::ref_cast<irs::byte_type>(irs::string_ref("2")), mangleStringIdentity("a"));
++subFiltersIterator;
operation.second.second(subFiltersIterator, irs::ref_cast<irs::byte_type>(irs::string_ref("3")), mangleStringIdentity("a"));
++subFiltersIterator;
}
}
// complex attribute name with offset, boost, analyzer
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER BOOST(ANALYZER(['1','2','3']",
operation.first, "d.a['b']['c'][412].e.f, 'test_analyzer'), 2.5) RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual);
auto subFiltersIterator = operation.second.first(actual, 2.5);
operation.second.second(subFiltersIterator, irs::ref_cast<irs::byte_type>(irs::string_ref("1")), mangleString("a.b.c[412].e.f", "test_analyzer"));
++subFiltersIterator;
operation.second.second(subFiltersIterator, irs::ref_cast<irs::byte_type>(irs::string_ref("2")), mangleString("a.b.c[412].e.f", "test_analyzer"));
++subFiltersIterator;
operation.second.second(subFiltersIterator, irs::ref_cast<irs::byte_type>(irs::string_ref("3")), mangleString("a.b.c[412].e.f", "test_analyzer"));
++subFiltersIterator;
}
}
// heterogeneous array values, analyzer, boost
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER ANALYZER(BOOST(['1',null,true]",
operation.first, "d.quick.brown.fox, 1.5), 'test_analyzer') RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual);
auto subFiltersIterator = operation.second.first(actual, 1.5);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleString("quick.brown.fox", "test_analyzer"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::null_token_stream::value_null(),
mangleNull("quick.brown.fox"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::boolean_token_stream::value_true(),
mangleBool("quick.brown.fox"));
++subFiltersIterator;
}
}
// heterogeneous non string values, analyzer, boost
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER ANALYZER(BOOST([2, null,false]",
operation.first, "d.quick.brown.fox, 1.5), 'test_analyzer') RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual);
auto subFiltersIterator = operation.second.first(actual, 1.5);
irs::numeric_token_stream stream;
stream.reset(2.);
ASSERT_EQ(irs::by_granular_range::type(), subFiltersIterator->type());
{
auto& by_range_actual = dynamic_cast<irs::by_granular_range const&>(*subFiltersIterator);
irs::by_granular_range expected;
expected.field(mangleNumeric("quick.brown.fox"));
// granular range handled separately (it is used only for numerics, just check it here once)
if (operation.first == "ANY >" || operation.first == "ALL >" || operation.first == "NONE <=") {
expected.insert<irs::Bound::MAX>(stream);
expected.include<irs::Bound::MAX>(false);
} else if (operation.first == "ANY >=" || operation.first == "ALL >=" || operation.first == "NONE <") {
expected.insert<irs::Bound::MAX>(stream);
expected.include<irs::Bound::MAX>(true);
} else if (operation.first == "ANY <" || operation.first == "ALL <" || operation.first == "NONE >=") {
expected.insert<irs::Bound::MIN>(stream);
expected.include<irs::Bound::MIN>(false);
} else if (operation.first == "ANY <=" || operation.first == "ALL <=" || operation.first == "NONE >") {
expected.insert<irs::Bound::MIN>(stream);
expected.include<irs::Bound::MIN>(true);
} else {
ASSERT_TRUE(false); // new array comparsion operator added? Need to update checks here!
}
EXPECT_EQ(expected, by_range_actual);
}
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::null_token_stream::value_null(),
mangleNull("quick.brown.fox"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::boolean_token_stream::value_false(),
mangleBool("quick.brown.fox"));
++subFiltersIterator;
}
}
// dynamic complex attribute name
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN "
"collection FILTER "
" ['1','2','3']",
operation.first,
"d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] "
" RETURN d");
ExpressionContextMock ctx;
ctx.vars.emplace("a", arangodb::aql::AqlValue(arangodb::aql::AqlValue{"a"}));
ctx.vars.emplace("c", arangodb::aql::AqlValue(arangodb::aql::AqlValue{"c"}));
ctx.vars.emplace("offsetInt", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintInt{4})));
ctx.vars.emplace("offsetDbl", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintDouble{5.6})));
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, &ctx);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("2")),
mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("3")),
mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"));
++subFiltersIterator;
}
}
// invalid dynamic attribute name (null value)
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("LET a=null LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN "
"collection FILTER "
" ['1','2','3']",
operation.first,
"d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] "
" RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
ExpressionContextMock ctx;
ctx.vars.emplace("a", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintNull{})); // invalid value type
ctx.vars.emplace("c", arangodb::aql::AqlValue(arangodb::aql::AqlValue{ "c" }));
ctx.vars.emplace("offsetInt", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintInt{ 4 })));
ctx.vars.emplace("offsetDbl", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintDouble{ 5.6 })));
assertFilterExecutionFail(vocbase(), queryString, &ctx);
}
}
// invalid dynamic attribute name (missing value)
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN "
"collection FILTER "
" ['1','2','3']",
operation.first,
"d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] "
" RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
ExpressionContextMock ctx;
ctx.vars.emplace("a", arangodb::aql::AqlValue(arangodb::aql::AqlValue{"a"}));
ctx.vars.emplace("c", arangodb::aql::AqlValue(arangodb::aql::AqlValue{ "c" }));
ctx.vars.emplace("offsetDbl", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintDouble{ 5.6 })));
assertFilterExecutionFail(vocbase(), queryString, &ctx);
}
}
// invalid dynamic attribute name (bool value)
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("LET a=false LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN "
"collection FILTER "
" ['1','2','3']",
operation.first,
"d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] "
" RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
ExpressionContextMock ctx;
ctx.vars.emplace("a", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintBool{false}));
ctx.vars.emplace("c", arangodb::aql::AqlValue(arangodb::aql::AqlValue{ "c" }));
ctx.vars.emplace("offsetInt", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintInt{ 4 })));
ctx.vars.emplace("offsetDbl", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintDouble{ 5.6 })));
assertFilterExecutionFail(vocbase(), queryString, &ctx);
}
}
// reference in array
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("LET c=2 FOR d IN collection FILTER ['1', c, '3']",
operation.first,
"d.a.b.c.e.f RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
arangodb::aql::Variable var("c", 0);
arangodb::aql::AqlValue value(arangodb::aql::AqlValue("2"));
arangodb::aql::AqlValueGuard guard(value, true);
ExpressionContextMock ctx;
ctx.vars.emplace(var.name, value);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, &ctx);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("2")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("3")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
}
}
// array as reference, boost, analyzer
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("LET x=['1', '2', '3'] FOR d IN collection FILTER "
"ANALYZER(BOOST(x",
operation.first,
"d.a.b.c.e.f, 1.5), 'test_analyzer') RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
auto obj = arangodb::velocypack::Parser::fromJson("[ \"1\", \"2\", \"3\"]");
arangodb::aql::AqlValue value(obj->slice());
arangodb::aql::AqlValueGuard guard(value, true);
ExpressionContextMock ctx;
ctx.vars.emplace("x", value);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, &ctx);
auto subFiltersIterator = operation.second.first(actual, 1.5);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleString("a.b.c.e.f", "test_analyzer"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("2")),
mangleString("a.b.c.e.f", "test_analyzer"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("3")),
mangleString("a.b.c.e.f", "test_analyzer"));
++subFiltersIterator;
}
}
// nondeterministic value
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER [ '1', RAND(), '3' ]",
operation.first,
"d.a.b.c.e.f RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, nullptr);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
EXPECT_EQ(arangodb::iresearch::ByExpression::type(), subFiltersIterator->type());
EXPECT_NE(nullptr, dynamic_cast<arangodb::iresearch::ByExpression const*>(&*subFiltersIterator));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("3")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
}
}
// self-referenced value
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER [ '1', d, '3' ]",
operation.first,
"d.a.b.c.e.f RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, nullptr);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
EXPECT_EQ(arangodb::iresearch::ByExpression::type(), subFiltersIterator->type());
EXPECT_NE(nullptr, dynamic_cast<arangodb::iresearch::ByExpression const*>(&*subFiltersIterator));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("3")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
}
}
// self-referenced value
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER [ '1', d.e, d.a.b.c.e.f ]",
operation.first,
"d.a.b.c.e.f RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, nullptr);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
EXPECT_EQ(arangodb::iresearch::ByExpression::type(), subFiltersIterator->type());
EXPECT_NE(nullptr, dynamic_cast<arangodb::iresearch::ByExpression const*>(&*subFiltersIterator));
++subFiltersIterator;
EXPECT_EQ(arangodb::iresearch::ByExpression::type(), subFiltersIterator->type());
EXPECT_NE(nullptr, dynamic_cast<arangodb::iresearch::ByExpression const*>(&*subFiltersIterator));
}
}
// self-referenced value
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString("FOR d IN collection FILTER [ '1', 1 + d.b, '3' ]",
operation.first,
"d.a.b.c.e.f RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, nullptr);
auto subFiltersIterator = operation.second.first(actual, 1);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("1")),
mangleStringIdentity("a.b.c.e.f"));
++subFiltersIterator;
EXPECT_EQ(arangodb::iresearch::ByExpression::type(), subFiltersIterator->type());
EXPECT_NE(nullptr, dynamic_cast<arangodb::iresearch::ByExpression const*>(&*subFiltersIterator));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("3")),
mangleStringIdentity("a.b.c.e.f"));
}
}
// heterogeneous references and expression in array, analyzer, boost
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"LET strVal='str' LET boolVal=false LET nullVal=null FOR "
"d IN collection FILTER boost(ANALYZER([CONCAT(strVal, '2'), boolVal, nullVal]",
operation.first,
"d.a.b.c.e.f, 'test_analyzer'),2.5) RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
ExpressionContextMock ctx;
ctx.vars.emplace("strVal", arangodb::aql::AqlValue("str"));
ctx.vars.emplace("boolVal",
arangodb::aql::AqlValue(arangodb::aql::AqlValueHintBool(false)));
ctx.vars.emplace("nullVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintNull{}));
irs::Or actual;
buildActualFilter(vocbase(), queryString, actual, &ctx);
auto subFiltersIterator = operation.second.first(actual, 2.5);
operation.second.second(subFiltersIterator,
irs::ref_cast<irs::byte_type>(irs::string_ref("str2")),
mangleString("a.b.c.e.f", "test_analyzer"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::boolean_token_stream::value_false(),
mangleBool("a.b.c.e.f"));
++subFiltersIterator;
operation.second.second(subFiltersIterator,
irs::null_token_stream::value_null(),
mangleNull("a.b.c.e.f"));
++subFiltersIterator;
}
}
// not array as left argument
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"LET a=null LET b='b' LET c=4 LET e=5.6 FOR d IN collection FILTER a ",
operation.first,
"d.a RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
ExpressionContextMock ctx;
ctx.vars.emplace("a", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintBool{false})); // invalid value type
ctx.vars.emplace("b", arangodb::aql::AqlValue(arangodb::aql::AqlValue{"c"}));
ctx.vars.emplace("c", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintInt{4})));
ctx.vars.emplace("e", arangodb::aql::AqlValue(arangodb::aql::AqlValue(
arangodb::aql::AqlValueHintDouble{5.6})));
assertFilterExecutionFail(
vocbase(),
queryString,
&ctx);
}
}
// self-reference
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"FOR d IN myView FILTER [1,2,'3']",
operation.first,
" d RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
assertExpressionFilter(vocbase(), queryString);
}
}
// non-deterministic expression name in array
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN "
"collection FILTER "
" ['1','2','3']",
operation.first,
" d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_NONDETERM_('a')] RETURN d ");
SCOPED_TRACE(testing::Message("Query:") << queryString);
assertExpressionFilter(vocbase(), queryString);
}
}
// no reference provided
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"LET x={} FOR d IN myView FILTER [1,x.a,3] ",
operation.first,
"d.a RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
assertFilterExecutionFail(
vocbase(), queryString, &ExpressionContextMock::EMPTY);
}
}
// not a value in array
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"FOR d IN collection FILTER ['1',['2'],'3'] ",
operation.first,
"d.a RETURN d");
SCOPED_TRACE(testing::Message("Query:") << queryString);
assertFilterFail(vocbase(), queryString);
}
}
// empty array
{
for (auto operation : intervalOperations) {
auto queryString = buildQueryString(
"FOR d IN collection FILTER BOOST([]",
operation.first,
"d.a, 2.5) RETURN d");
SCOPED_TRACE(testing::Message("Query") << queryString);
irs::Or expected;
if (operation.first.find("ANY") != std::string::npos) {
expected.add<irs::empty>();
} else {
expected.add<irs::all>();
}
expected.boost(2.5);
assertFilterSuccess(
vocbase(), queryString, expected);
}
}
}

View File

@ -2250,6 +2250,196 @@ TEST_F(IResearchViewNodeTest, createBlockCoordinatorLateMaterialize) {
emptyBlock.get()));
}
TEST_F(IResearchViewNodeTest, registerPlanningLateMaterialized) {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server(),
"testVocbase", 1));
auto createJson = arangodb::velocypack::Parser::fromJson(
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
auto logicalView = vocbase.createView(createJson->slice());
ASSERT_TRUE((false == !logicalView));
// dummy query
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString("RETURN 1"),
nullptr, arangodb::velocypack::Parser::fromJson("{}"),
arangodb::aql::PART_MAIN);
query.prepare(arangodb::QueryRegistryFeature::registry(),
arangodb::aql::SerializationFormat::SHADOWROWS);
// dummy engine
arangodb::aql::ExecutionEngine engine(query, arangodb::aql::SerializationFormat::SHADOWROWS);
arangodb::aql::SingletonNode singleton(query.plan(), 0);
arangodb::aql::Variable const outVariable("variable", 0);
arangodb::aql::Variable const outNmColPtr("variable100", 100);
arangodb::aql::Variable const outNmDocId("variable101", 101);
// no filter condition, no sort condition
arangodb::iresearch::IResearchViewNode node(*query.plan(),
42, // id
vocbase, // database
logicalView, // view
outVariable,
nullptr, // no filter condition
nullptr, // no options
{}); // no sort condition
node.addDependency(&singleton);
node.setLateMaterialized(&outNmColPtr, &outNmDocId);
std::vector<arangodb::aql::RegisterId> nrRegsHere{ 0 };
std::vector<arangodb::aql::RegisterId> nrRegs{ 0 };
std::unordered_map<arangodb::aql::VariableId, arangodb::aql::VarInfo> varInfo;
unsigned int totalNrRegs = 0;
node.planNodeRegisters(nrRegsHere, nrRegs, varInfo, totalNrRegs, 1);
EXPECT_EQ(2, totalNrRegs);
EXPECT_EQ(2, nrRegs.size());
EXPECT_EQ(2, nrRegs[1]);
EXPECT_EQ(2, nrRegsHere.size());
EXPECT_EQ(2, nrRegsHere[1]);
EXPECT_EQ(2, varInfo.size());
EXPECT_NE(varInfo.end(), varInfo.find(outNmColPtr.id));
EXPECT_NE(varInfo.end(), varInfo.find(outNmDocId.id));
EXPECT_EQ(varInfo.end(), varInfo.find(outVariable.id));
}
TEST_F(IResearchViewNodeTest, registerPlanningLateMaterializedWitScore) {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server(),
"testVocbase", 1));
auto createJson = arangodb::velocypack::Parser::fromJson(
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
auto logicalView = vocbase.createView(createJson->slice());
ASSERT_TRUE((false == !logicalView));
// dummy query
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString("RETURN 1"),
nullptr, arangodb::velocypack::Parser::fromJson("{}"),
arangodb::aql::PART_MAIN);
query.prepare(arangodb::QueryRegistryFeature::registry(),
arangodb::aql::SerializationFormat::SHADOWROWS);
// dummy engine
arangodb::aql::ExecutionEngine engine(query, arangodb::aql::SerializationFormat::SHADOWROWS);
arangodb::aql::SingletonNode singleton(query.plan(), 0);
arangodb::aql::Variable const outVariable("variable", 0);
arangodb::aql::Variable const outNmColPtr("variable100", 100);
arangodb::aql::Variable const outNmDocId("variable101", 101);
arangodb::aql::Variable const scoreVariable("score", 102);
// no filter condition, no sort condition
arangodb::iresearch::IResearchViewNode node(*query.plan(),
42, // id
vocbase, // database
logicalView, // view
outVariable,
nullptr, // no filter condition
nullptr, // no options
std::vector<arangodb::iresearch::Scorer>{ {&scoreVariable, nullptr} }); //sort condition
node.addDependency(&singleton);
node.setLateMaterialized(&outNmColPtr, &outNmDocId);
std::vector<arangodb::aql::RegisterId> nrRegsHere{ 0 };
std::vector<arangodb::aql::RegisterId> nrRegs{ 0 };
std::unordered_map<arangodb::aql::VariableId, arangodb::aql::VarInfo> varInfo;
unsigned int totalNrRegs = 0;
node.planNodeRegisters(nrRegsHere, nrRegs, varInfo, totalNrRegs, 1);
EXPECT_EQ(3, totalNrRegs);
EXPECT_EQ(2, nrRegs.size());
EXPECT_EQ(3, nrRegs[1]);
EXPECT_EQ(2, nrRegsHere.size());
EXPECT_EQ(3, nrRegsHere[1]);
EXPECT_EQ(3, varInfo.size());
EXPECT_NE(varInfo.end(), varInfo.find(outNmColPtr.id));
EXPECT_NE(varInfo.end(), varInfo.find(outNmDocId.id));
EXPECT_EQ(varInfo.end(), varInfo.find(outVariable.id));
EXPECT_NE(varInfo.end(), varInfo.find(scoreVariable.id));
}
TEST_F(IResearchViewNodeTest, registerPlanning) {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server(),
"testVocbase", 1));
auto createJson = arangodb::velocypack::Parser::fromJson(
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
auto logicalView = vocbase.createView(createJson->slice());
ASSERT_TRUE((false == !logicalView));
// dummy query
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString("RETURN 1"),
nullptr, arangodb::velocypack::Parser::fromJson("{}"),
arangodb::aql::PART_MAIN);
query.prepare(arangodb::QueryRegistryFeature::registry(),
arangodb::aql::SerializationFormat::SHADOWROWS);
// dummy engine
arangodb::aql::ExecutionEngine engine(query, arangodb::aql::SerializationFormat::SHADOWROWS);
arangodb::aql::SingletonNode singleton(query.plan(), 0);
arangodb::aql::Variable const outVariable("variable", 0);
// no filter condition, no sort condition
arangodb::iresearch::IResearchViewNode node(*query.plan(),
42, // id
vocbase, // database
logicalView, // view
outVariable,
nullptr, // no filter condition
nullptr, // no options
{}); // no sort condition
node.addDependency(&singleton);
std::vector<arangodb::aql::RegisterId> nrRegsHere{ 0 };
std::vector<arangodb::aql::RegisterId> nrRegs{ 0 };
std::unordered_map<arangodb::aql::VariableId, arangodb::aql::VarInfo> varInfo;
unsigned int totalNrRegs = 0;
node.planNodeRegisters(nrRegsHere, nrRegs, varInfo, totalNrRegs, 1);
EXPECT_EQ(1, totalNrRegs);
EXPECT_EQ(2, nrRegs.size());
EXPECT_EQ(1, nrRegs[1]);
EXPECT_EQ(2, nrRegsHere.size());
EXPECT_EQ(1, nrRegsHere[1]);
EXPECT_EQ(1, varInfo.size());
EXPECT_NE(varInfo.end(), varInfo.find(outVariable.id));
}
TEST_F(IResearchViewNodeTest, registerPlanningWithScore) {
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, testDBInfo(server.server(),
"testVocbase", 1));
auto createJson = arangodb::velocypack::Parser::fromJson(
"{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
auto logicalView = vocbase.createView(createJson->slice());
ASSERT_TRUE((false == !logicalView));
// dummy query
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString("RETURN 1"),
nullptr, arangodb::velocypack::Parser::fromJson("{}"),
arangodb::aql::PART_MAIN);
query.prepare(arangodb::QueryRegistryFeature::registry(),
arangodb::aql::SerializationFormat::SHADOWROWS);
// dummy engine
arangodb::aql::ExecutionEngine engine(query, arangodb::aql::SerializationFormat::SHADOWROWS);
arangodb::aql::SingletonNode singleton(query.plan(), 0);
arangodb::aql::Variable const outVariable("variable", 0);
arangodb::aql::Variable const scoreVariable("score", 100);
// no filter condition, no sort condition
arangodb::iresearch::IResearchViewNode node(*query.plan(),
42, // id
vocbase, // database
logicalView, // view
outVariable,
nullptr, // no filter condition
nullptr, // no options
std::vector<arangodb::iresearch::Scorer>{ {&scoreVariable, nullptr} }); //sort condition
node.addDependency(&singleton);
std::vector<arangodb::aql::RegisterId> nrRegsHere{ 0 };
std::vector<arangodb::aql::RegisterId> nrRegs{ 0 };
std::unordered_map<arangodb::aql::VariableId, arangodb::aql::VarInfo> varInfo;
unsigned int totalNrRegs = 0;
node.planNodeRegisters(nrRegsHere, nrRegs, varInfo, totalNrRegs, 1);
EXPECT_EQ(2, totalNrRegs);
EXPECT_EQ(2, nrRegs.size());
EXPECT_EQ(2, nrRegs[1]);
EXPECT_EQ(2, nrRegsHere.size());
EXPECT_EQ(2, nrRegsHere[1]);
EXPECT_EQ(2, varInfo.size());
EXPECT_NE(varInfo.end(), varInfo.find(outVariable.id));
EXPECT_NE(varInfo.end(), varInfo.find(scoreVariable.id));
}
class IResearchViewBlockTest
: public ::testing::Test,
public arangodb::tests::LogSuppressor<arangodb::Logger::AUTHENTICATION, arangodb::LogLevel::ERR> {

View File

@ -627,6 +627,71 @@ void assertFilterBoost(irs::filter const& expected, irs::filter const& actual) {
}
}
void buildActualFilter(TRI_vocbase_t& vocbase,
std::string const& queryString, irs::filter& actual,
arangodb::aql::ExpressionContext* exprCtx /*= nullptr*/,
std::shared_ptr<arangodb::velocypack::Builder> bindVars /*= nullptr*/,
std::string const& refName /*= "d"*/
) {
auto options = std::make_shared<arangodb::velocypack::Builder>();
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString(queryString),
bindVars, options, arangodb::aql::PART_MAIN);
auto const parseResult = query.parse();
ASSERT_TRUE(parseResult.result.ok());
auto* ast = query.ast();
ASSERT_TRUE(ast);
auto* root = ast->root();
ASSERT_TRUE(root);
// find first FILTER node
arangodb::aql::AstNode* filterNode = nullptr;
for (size_t i = 0; i < root->numMembers(); ++i) {
auto* node = root->getMemberUnchecked(i);
ASSERT_TRUE(node);
if (arangodb::aql::NODE_TYPE_FILTER == node->type) {
filterNode = node;
break;
}
}
ASSERT_TRUE(filterNode);
// find referenced variable
auto* allVars = ast->variables();
ASSERT_TRUE(allVars);
arangodb::aql::Variable* ref = nullptr;
for (auto entry : allVars->variables(true)) {
if (entry.second == refName) {
ref = allVars->getVariable(entry.first);
break;
}
}
ASSERT_TRUE(ref);
// optimization time
{
arangodb::transaction ::Methods trx(arangodb::transaction::StandaloneContext::Create(vocbase),
{}, {}, {}, arangodb::transaction::Options());
arangodb::iresearch::QueryContext const ctx{&trx, nullptr, nullptr, nullptr, ref};
ASSERT_TRUE(arangodb::iresearch::FilterFactory::filter(nullptr, ctx, *filterNode).ok());
}
// execution time
{
arangodb::transaction ::Methods trx(arangodb::transaction::StandaloneContext::Create(vocbase),
{}, {}, {}, arangodb::transaction::Options());
auto dummyPlan = arangodb::tests::planFromQuery(vocbase, "RETURN 1");
arangodb::iresearch::QueryContext const ctx{&trx, dummyPlan.get(), ast, exprCtx, ref};
ASSERT_TRUE(arangodb::iresearch::FilterFactory::filter(dynamic_cast<irs::boolean_filter*>(&actual), ctx, *filterNode).ok());
}
}
void assertFilter(TRI_vocbase_t& vocbase, bool parseOk, bool execOk,
std::string const& queryString, irs::filter const& expected,
arangodb::aql::ExpressionContext* exprCtx /*= nullptr*/,

View File

@ -187,6 +187,11 @@ void assertFilterExecutionFail(TRI_vocbase_t& vocbase, std::string const& queryS
void assertFilterParseFail(TRI_vocbase_t& vocbase, std::string const& queryString,
std::shared_ptr<arangodb::velocypack::Builder> bindVars = nullptr);
void buildActualFilter(TRI_vocbase_t& vocbase, std::string const& queryString, irs::filter& actual,
arangodb::aql::ExpressionContext* exprCtx = nullptr,
std::shared_ptr<arangodb::velocypack::Builder> bindVars = nullptr,
std::string const& refName = "d");
inline VPackBuilder dbArgsBuilder(std::string const& name = "_system") {
VPackOptions options;
VPackBuilder builder(&options);

View File

@ -48,6 +48,11 @@
return {
setUpAll : function () {
analyzers.save("text_en", "text", "{ \"locale\": \"en.UTF-8\", \"stopwords\": [ ] }", [ "frequency", "norm", "position" ]);
db._drop("AuxUnitTestsCollection");
let auxCol = db._create("AuxUnitTestsCollection");
auxCol.save({ foobar: ['foo', 'bar'], foo: ['foo'], bar:['bar'], empty: []});
db._drop("UnitTestsCollection");
c = db._create("UnitTestsCollection", args);
@ -57,6 +62,12 @@
db._drop("AnotherUnitTestsCollection");
var ac = db._create("AnotherUnitTestsCollection", args);
db._drop("UnitTestsWithArrayCollection");
let arrayCol = db._create("UnitTestsWithArrayCollection");
arrayCol.save({ c: 0, a: ['foo', 'bar', 'baz']});
// this will allow to catch if accidentally "all" filter will be used
arrayCol.save({ c: 1, a: ['afoo', 'abar', 'abaz']});
db._dropView("UnitTestsView");
v = db._createView("UnitTestsView", "arangosearch", {});
meta = {
@ -130,6 +141,21 @@
g.save({ _from: "UnitTestsGraphCollection/begin", _to: "UnitTestsGraphCollection/intermediate" });
g.save({ _from: "UnitTestsGraphCollection/intermediate", _to: "UnitTestsGraphCollection/end" });
db._dropView("UnitTestsViewArrayView");
let arrayV = db._createView("UnitTestsWithArrayView", "arangosearch", {});
meta = {
links: {
UnitTestsWithArrayCollection: {
includeAllFields: true,
fields: {
a: { analyzers: [ "identity" ] }
}
}
}
};
arrayV.properties(meta);
},
tearDownAll : function () {
@ -148,6 +174,9 @@
db._drop("AnotherUnitTestsCollection");
db._drop("UnitTestsGraph");
db._drop("UnitTestsGraphCollection");
db._drop("AuxUnitTestsCollection");
db._dropView("UnitTestsWithArrayView");
db._drop("UnitTestsWithArrayCollection");
},
testViewInFunctionCall : function () {
@ -1028,6 +1057,508 @@
});
docs.forEach(function(element, i) { assertEqual(res[i].length, 1); });
},
testArrayComparsionOperatorsInOnSimpleField : function () {
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(15, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'foo' || doc.a === 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ANY NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo'] ANY NOT IN d.a RETURN d").toArray();
assertEqual(18, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] NONE IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] NONE NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['bar'] NONE NOT IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ALL IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['bar'] ALL IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ALL NOT IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
},
testArrayComparsionOperatorsInOnSimpleFieldWitScopedValue : function () {
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(15, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'foo' || doc.a === 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ANY IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ANY NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ANY NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foo ANY NOT IN d.a RETURN d").toArray();
assertEqual(18, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar NONE IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty NONE IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar NONE NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.bar NONE NOT IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty NONE NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ALL IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ALL IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.bar ALL IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ALL NOT IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ALL NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
},
testArrayComparsionOperatorsInOnArrayField : function() {
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['foo', 'bar'] ALL IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['foo', 'bar', 'none'] ALL IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'none', 'nani', 'afoo'] ALL NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['afoo', 'foo', 'none', 'nani'] ALL NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'bar'] ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'nani', 'none'] ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'foo', 'none', 'nani', 'afoo'] ANY NOT IN d.a OPTIONS { waitForSync : true } SORT d.c ASC RETURN d").toArray();
assertEqual(2, result.length);
assertEqual(0, result[0].c);
assertEqual(1, result[1].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'foo', 'bar'] ANY NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(1, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'nani', 'afoo'] NONE IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'nani', 'bar', 'afoo'] NONE IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'foo', 'bar'] NONE NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['bar', 'foo'] NONE NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
},
testArrayComparsionOperatorsGreaterOnSimpleField : function() {
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3, 4] ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1);
});
}
{
let result = db._query("FOR a IN [[2, 3, 4]] FOR d IN UnitTestsView SEARCH a ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3, 4] ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1 || doc.c === 2);
});
}
{
let result = db._query("FOR a IN [[2, 3, 4]] FOR d IN UnitTestsView SEARCH a ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1 || doc.c === 2);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2] ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c < 2);
});
}
{
let result = db._query("FOR a IN [[1, 2]] FOR d IN UnitTestsView SEARCH a ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c < 2);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2] ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c <= 2);
});
}
{
let result = db._query("FOR a IN [[1, 2]] FOR d IN UnitTestsView SEARCH a ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c <= 2);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR a IN [[1, 2, 3]] FOR d IN UnitTestsView SEARCH a NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR a IN [[1, 2, 3]] FOR d IN UnitTestsView SEARCH a NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
},
testArrayComparsionOperatorsLessOnSimpleField : function() {
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3] ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR a IN [[2,3]] FOR d IN UnitTestsView SEARCH a ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3] ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR a IN [[2,3]] FOR d IN UnitTestsView SEARCH a ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [3, 4] ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR a IN [[3,4]] FOR d IN UnitTestsView SEARCH a ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [3, 4] ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR a IN [[3,4]] FOR d IN UnitTestsView SEARCH a ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 1 || doc.c === 0);
});
}
{
let result = db._query("FOR a IN [[1,2,3]] FOR d IN UnitTestsView SEARCH a NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 1 || doc.c === 0);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue( doc.c === 0);
});
}
{
let result = db._query("FOR a IN [[1,2,3]] FOR d IN UnitTestsView SEARCH a NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
}
};
};

View File

@ -42,8 +42,45 @@ function iResearchAqlTestSuite () {
var c2;
var v;
var v2;
return {
setUpAll : function () {
db._drop("AuxUnitTestsCollection");
let auxCol = db._create("AuxUnitTestsCollection");
auxCol.save({ foobar: ['foo', 'bar'], foo: ['foo'], bar:['bar'], empty: []});
db._drop("AnotherUnitTestsCollection");
let ac = db._create("AnotherUnitTestsCollection");
ac.save({ a: "foo", id : 0 });
ac.save({ a: "ba", id : 1 });
db._drop("UnitTestsWithArrayCollection");
let arrayCol = db._create("UnitTestsWithArrayCollection");
arrayCol.save({ c: 0, a: ['foo', 'bar', 'baz']});
// this will allow to catch if accidentally "all" filter will be used
arrayCol.save({ c: 1, a: ['afoo', 'abar', 'abaz']});
db._dropView("UnitTestsViewArrayView");
let arrayV = db._createView("UnitTestsWithArrayView", "arangosearch", {});
let meta = {
links: {
UnitTestsWithArrayCollection: {
includeAllFields: true,
fields: {
a: { analyzers: [ "identity" ] }
}
}
}
};
arrayV.properties(meta);
},
tearDownAll : function () {
db._drop("AnotherUnitTestsCollection");
db._drop("AuxUnitTestsCollection");
db._dropView("UnitTestsWithArrayView");
db._drop("UnitTestsWithArrayCollection");
},
setUp : function () {
db._drop("UnitTestsCollection");
c = db._create("UnitTestsCollection");
@ -51,9 +88,6 @@ function iResearchAqlTestSuite () {
db._drop("UnitTestsCollection2");
c2 = db._create("UnitTestsCollection2");
db._drop("AnotherUnitTestsCollection");
var ac = db._create("AnotherUnitTestsCollection");
db._dropView("UnitTestsView");
v = db._createView("UnitTestsView", "arangosearch", {});
var meta = {
@ -77,9 +111,6 @@ function iResearchAqlTestSuite () {
}}
);
ac.save({ a: "foo", id : 0 });
ac.save({ a: "ba", id : 1 });
for (let i = 0; i < 5; i++) {
c.save({ a: "foo", b: "bar", c: i });
c.save({ a: "foo", b: "baz", c: i });
@ -109,7 +140,7 @@ function iResearchAqlTestSuite () {
v2.drop();
db._drop("UnitTestsCollection");
db._drop("UnitTestsCollection2");
db._drop("AnotherUnitTestsCollection");
},
testViewInFunctionCall : function () {
@ -1052,7 +1083,509 @@ function iResearchAqlTestSuite () {
});
docs.forEach(function(element, i) { assertEqual(res[i].length, 1); });
}
},
testArrayComparsionOperatorsInOnSimpleField : function () {
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(15, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'foo' || doc.a === 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ANY NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo'] ANY NOT IN d.a RETURN d").toArray();
assertEqual(18, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] NONE IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] NONE NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['bar'] NONE NOT IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ALL IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['bar'] ALL IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH ['foo', 'bar'] ALL NOT IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
},
testArrayComparsionOperatorsInOnSimpleFieldWitScopedValue : function () {
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(15, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'foo' || doc.a === 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ANY IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ANY NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ANY NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foo ANY NOT IN d.a RETURN d").toArray();
assertEqual(18, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar NONE IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty NONE IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar NONE NOT IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.bar NONE NOT IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty NONE NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ALL IN d.a RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ALL IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.bar ALL IN d.a RETURN d").toArray();
assertEqual(5, result.length);
result.forEach(function(doc) {
assertTrue(doc.a === 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.foobar ALL NOT IN d.a RETURN d").toArray();
assertEqual(13, result.length);
result.forEach(function(doc) {
assertTrue(doc.a !== 'foo' && doc.a !== 'bar');
});
}
{
let result = db._query("FOR a IN AuxUnitTestsCollection " +
"FOR d IN UnitTestsView SEARCH a.empty ALL NOT IN d.a RETURN d").toArray();
assertEqual(28, result.length);
}
},
testArrayComparsionOperatorsInOnArrayField : function() {
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['foo', 'bar'] ALL IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['foo', 'bar', 'none'] ALL IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'none', 'nani', 'afoo'] ALL NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['afoo', 'foo', 'none', 'nani'] ALL NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'bar'] ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'nani', 'none'] ANY IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'foo', 'none', 'nani', 'afoo'] ANY NOT IN d.a OPTIONS { waitForSync : true } SORT d.c ASC RETURN d").toArray();
assertEqual(2, result.length);
assertEqual(0, result[0].c);
assertEqual(1, result[1].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH [ 'foo', 'bar'] ANY NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(1, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'nani', 'afoo'] NONE IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'nani', 'bar', 'afoo'] NONE IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['none', 'foo', 'bar'] NONE NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsWithArrayView SEARCH ['bar', 'foo'] NONE NOT IN d.a OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(1, result.length);
assertEqual(0, result[0].c);
}
},
testArrayComparsionOperatorsGreaterOnSimpleField : function() {
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3, 4] ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1);
});
}
{
let result = db._query("FOR a IN [[2, 3, 4]] FOR d IN UnitTestsView SEARCH a ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3, 4] ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1 || doc.c === 2);
});
}
{
let result = db._query("FOR a IN [[2, 3, 4]] FOR d IN UnitTestsView SEARCH a ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0 || doc.c === 1 || doc.c === 2);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2] ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c < 2);
});
}
{
let result = db._query("FOR a IN [[1, 2]] FOR d IN UnitTestsView SEARCH a ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c < 2);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2] ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c <= 2);
});
}
{
let result = db._query("FOR a IN [[1, 2]] FOR d IN UnitTestsView SEARCH a ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(12, result.length);
result.forEach(function(doc) {
assertTrue(doc.c <= 2);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR a IN [[1, 2, 3]] FOR d IN UnitTestsView SEARCH a NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR a IN [[1, 2, 3]] FOR d IN UnitTestsView SEARCH a NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE > d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE >= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
},
testArrayComparsionOperatorsLessOnSimpleField : function() {
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3] ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR a IN [[2,3]] FOR d IN UnitTestsView SEARCH a ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [2, 3] ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR a IN [[2,3]] FOR d IN UnitTestsView SEARCH a ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ALL <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [3, 4] ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR a IN [[3,4]] FOR d IN UnitTestsView SEARCH a ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [3, 4] ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR a IN [[3,4]] FOR d IN UnitTestsView SEARCH a ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 4 || doc.c === 3);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a ANY <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(0, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 1 || doc.c === 0);
});
}
{
let result = db._query("FOR a IN [[1,2,3]] FOR d IN UnitTestsView SEARCH a NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(8, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 1 || doc.c === 0);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [1, 2, 3] NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue( doc.c === 0);
});
}
{
let result = db._query("FOR a IN [[1,2,3]] FOR d IN UnitTestsView SEARCH a NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(4, result.length);
result.forEach(function(doc) {
assertTrue(doc.c === 0);
});
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE < d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR d IN UnitTestsView SEARCH [] NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
{
let result = db._query("FOR a IN [[]] FOR d IN UnitTestsView SEARCH a NONE <= d.c OPTIONS { waitForSync : true } RETURN d").toArray();
assertEqual(28, result.length);
}
},
};
}