mirror of https://gitee.com/bigwinds/arangodb
* 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:
parent
a7fd2e68f7
commit
cfce3d8df8
|
@ -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
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
|
@ -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*/,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue