mirror of https://gitee.com/bigwinds/arangodb
* [3.4] bug-fix/issue-#8294 (#8430) * fix invalid optimizations for multi-valued attributes * fix broken optimizations for multivalued attributes * adjust tests * add `IN_RANGE` function * add tests * add some shallow integration tests * fix optimization for IN operator # Conflicts: # arangod/IResearch/IResearchFeature.cpp # arangod/IResearch/IResearchViewOptimizerRules.cpp # tests/IResearch/IResearchFilterBoolean-test.cpp * fix compilation errors * fix another compilation issue * address compilation errors * fix tests
This commit is contained in:
parent
e4594b72a8
commit
930b09cd93
|
@ -113,8 +113,10 @@ struct PermutationState {
|
|||
size_t const n;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Rules for single-valued variables
|
||||
//------------------------------------------------------------------------
|
||||
// | | a == y | a != y | a < y | a <= y | a >= y | a > y
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | IMP | OIS | OIS | OIS | IMP | IMP
|
||||
|
@ -140,6 +142,7 @@ struct PermutationState {
|
|||
// x < y | | SIO | DIJ | DIJ | DIJ | SIO | SIO
|
||||
// x == y | a > x | IMP | OIS | IMP | IMP | OIS | OIS
|
||||
// x > y | | IMP | OIS | IMP | IMP | OIS | OIS
|
||||
//------------------------------------------------------------------------
|
||||
// the 7th column is here as fallback if the operation is not in the table
|
||||
// above.
|
||||
// IMP -> IMPOSSIBLE -> empty result -> the complete AND set of conditions can
|
||||
|
@ -155,7 +158,7 @@ struct PermutationState {
|
|||
// larger than that of B
|
||||
// -> A can be dropped.
|
||||
|
||||
ConditionPartCompareResult const ConditionPart::ResultsTable[3][7][7] = {
|
||||
ConditionPartCompareResult const ResultsTable[3][7][7] = {
|
||||
{// X < Y
|
||||
{IMPOSSIBLE, OTHER_CONTAINED_IN_SELF, OTHER_CONTAINED_IN_SELF,
|
||||
OTHER_CONTAINED_IN_SELF, IMPOSSIBLE, IMPOSSIBLE, DISJOINT},
|
||||
|
@ -199,6 +202,96 @@ ConditionPartCompareResult const ConditionPart::ResultsTable[3][7][7] = {
|
|||
OTHER_CONTAINED_IN_SELF, OTHER_CONTAINED_IN_SELF, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT}}};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Rules for multi-valued variables
|
||||
//------------------------------------------------------------------------
|
||||
// | | a == y | a != y | a < y | a <= y | a >= y | a > y
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | DIJ | DIJ | OIS | OIS | DIJ | DIJ
|
||||
// x == y | a == x | OIS | IMP | DIJ | OIS | OIS | DIJ
|
||||
// x > y | | DIJ | DIJ | DIJ | DIJ | OIS | OIS
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | DIJ | DIJ | DIJ | DIJ | DIJ | DIJ
|
||||
// x == y | a != x | IMP | OIS | DIJ | DIJ | DIJ | DIJ
|
||||
// x > y | | DIJ | DIJ | DIJ | DIJ | DIJ | DIJ
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | DIJ | DIJ | OIS | OIS | DIJ | DIJ
|
||||
// x == y | a < x | DIJ | DIJ | OIS | OIS | DIJ | DIJ
|
||||
// x > y | | SIO | DIJ | SIO | SIO | DIJ | DIJ
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | DIJ | DIJ | OIS | OIS | DIJ | DIJ
|
||||
// x == y | a <= x | SIO | DIJ | SIO | OIS | DIJ | DIJ
|
||||
// x > y | | SIO | DIJ | SIO | SIO | DIJ | DIJ
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | SIO | DIJ | DIJ | DIJ | SIO | SIO
|
||||
// x == y | a >= x | SIO | DIJ | DIJ | DIJ | OIS | SIO
|
||||
// x > y | | DIJ | DIJ | DIJ | DIJ | OIS | OIS
|
||||
// -------|------------------|--------|--------|--------|--------|--------
|
||||
// x < y | | SIO | DIJ | DIJ | DIJ | SIO | SIO
|
||||
// x == y | a > x | DIJ | DIJ | DIJ | DIJ | OIS | OIS
|
||||
// x > y | | DIJ | DIJ | DIJ | DIJ | OIS | OIS
|
||||
//------------------------------------------------------------------------
|
||||
// the 7th column is here as fallback if the operation is not in the table
|
||||
// above.
|
||||
// IMP -> IMPOSSIBLE -> empty result -> the complete AND set of conditions can
|
||||
// be dropped.
|
||||
// CEQ -> CONVERT_EQUAL -> both conditions can be combined to a equals x.
|
||||
// DIJ -> DISJOINT -> neither condition is a consequence of the other -> both
|
||||
// have to stay in place.
|
||||
// SIO -> SELF_CONTAINED_IN_OTHER -> the left condition is a consequence of the
|
||||
// right condition
|
||||
// OIS -> OTHER_CONTAINED_IN_SELF -> the right condition is a consequence of the
|
||||
// left condition
|
||||
// If a condition (A) is a consequence of another (B), the solution set of A is
|
||||
// larger than that of B
|
||||
// -> A can be dropped.
|
||||
|
||||
ConditionPartCompareResult const ResultsTableMultiValued[3][7][7] = {
|
||||
{// X < Y
|
||||
{DISJOINT, DISJOINT, OTHER_CONTAINED_IN_SELF,
|
||||
OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT,
|
||||
DISJOINT, DISJOINT, DISJOINT},
|
||||
{DISJOINT, DISJOINT, OTHER_CONTAINED_IN_SELF,
|
||||
OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT, DISJOINT},
|
||||
{DISJOINT, DISJOINT, OTHER_CONTAINED_IN_SELF,
|
||||
OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT, DISJOINT},
|
||||
{SELF_CONTAINED_IN_OTHER, DISJOINT, DISJOINT, DISJOINT,
|
||||
SELF_CONTAINED_IN_OTHER, SELF_CONTAINED_IN_OTHER, DISJOINT},
|
||||
{SELF_CONTAINED_IN_OTHER, DISJOINT, DISJOINT, DISJOINT,
|
||||
SELF_CONTAINED_IN_OTHER, SELF_CONTAINED_IN_OTHER, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT}},
|
||||
{// X == Y
|
||||
{OTHER_CONTAINED_IN_SELF, IMPOSSIBLE, DISJOINT, OTHER_CONTAINED_IN_SELF,
|
||||
OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT},
|
||||
{IMPOSSIBLE, OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT,
|
||||
DISJOINT, DISJOINT, DISJOINT},
|
||||
{DISJOINT, DISJOINT, OTHER_CONTAINED_IN_SELF,
|
||||
OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT, DISJOINT},
|
||||
{SELF_CONTAINED_IN_OTHER, DISJOINT, SELF_CONTAINED_IN_OTHER,
|
||||
OTHER_CONTAINED_IN_SELF, DISJOINT, DISJOINT, DISJOINT},
|
||||
{SELF_CONTAINED_IN_OTHER, DISJOINT, DISJOINT, DISJOINT,
|
||||
OTHER_CONTAINED_IN_SELF, SELF_CONTAINED_IN_OTHER, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT,
|
||||
OTHER_CONTAINED_IN_SELF, OTHER_CONTAINED_IN_SELF, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT}},
|
||||
{// X > Y
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT,
|
||||
OTHER_CONTAINED_IN_SELF, OTHER_CONTAINED_IN_SELF, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT,
|
||||
DISJOINT, DISJOINT, DISJOINT, DISJOINT},
|
||||
{SELF_CONTAINED_IN_OTHER, DISJOINT, SELF_CONTAINED_IN_OTHER,
|
||||
SELF_CONTAINED_IN_OTHER, DISJOINT, DISJOINT, DISJOINT},
|
||||
{SELF_CONTAINED_IN_OTHER, DISJOINT, SELF_CONTAINED_IN_OTHER,
|
||||
SELF_CONTAINED_IN_OTHER, DISJOINT, DISJOINT, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT,
|
||||
OTHER_CONTAINED_IN_SELF, OTHER_CONTAINED_IN_SELF, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT,
|
||||
OTHER_CONTAINED_IN_SELF, OTHER_CONTAINED_IN_SELF, DISJOINT},
|
||||
{DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT, DISJOINT}}};
|
||||
|
||||
} // namespace
|
||||
|
||||
ConditionPart::ConditionPart(Variable const* variable, std::string const& attributeName,
|
||||
AstNode const* operatorNode, AttributeSideType side, void* data)
|
||||
: variable(variable),
|
||||
|
@ -271,7 +364,7 @@ bool ConditionPart::isCoveredBy(ConditionPart const& other, bool isReversed) con
|
|||
auto w = other.valueNode->getMemberUnchecked(j);
|
||||
|
||||
ConditionPartCompareResult res =
|
||||
ConditionPart::ResultsTable[CompareAstNodes(v, w, true) + 1][0][0];
|
||||
ResultsTable[CompareAstNodes(v, w, true) + 1][0][0];
|
||||
|
||||
if (res != CompareResult::OTHER_CONTAINED_IN_SELF &&
|
||||
res != CompareResult::CONVERT_EQUAL && res != CompareResult::IMPOSSIBLE) {
|
||||
|
@ -333,8 +426,8 @@ bool ConditionPart::isCoveredBy(ConditionPart const& other, bool isReversed) con
|
|||
|
||||
// Results are -1, 0, 1, move to 0, 1, 2 for the lookup:
|
||||
ConditionPartCompareResult res =
|
||||
ConditionPart::ResultsTable[CompareAstNodes(other.valueNode, valueNode, true) + 1]
|
||||
[other.whichCompareOperation()][whichCompareOperation()];
|
||||
ResultsTable[CompareAstNodes(other.valueNode, valueNode, true) + 1]
|
||||
[other.whichCompareOperation()][whichCompareOperation()];
|
||||
|
||||
if (res == CompareResult::OTHER_CONTAINED_IN_SELF ||
|
||||
res == CompareResult::CONVERT_EQUAL || res == CompareResult::IMPOSSIBLE) {
|
||||
|
@ -521,7 +614,7 @@ std::vector<std::vector<arangodb::basics::AttributeName>> Condition::getConstAtt
|
|||
|
||||
/// @brief normalize the condition
|
||||
/// this will convert the condition into its disjunctive normal form
|
||||
void Condition::normalize(ExecutionPlan* plan) {
|
||||
void Condition::normalize(ExecutionPlan* plan, bool multivalued /*= false*/) {
|
||||
if (_isNormalized) {
|
||||
// already normalized
|
||||
return;
|
||||
|
@ -531,7 +624,7 @@ void Condition::normalize(ExecutionPlan* plan) {
|
|||
_root = transformNodePostorder(_root);
|
||||
_root = fixRoot(_root, 0);
|
||||
|
||||
optimize(plan);
|
||||
optimize(plan, multivalued);
|
||||
|
||||
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
||||
if (_root != nullptr) {
|
||||
|
@ -802,7 +895,7 @@ bool Condition::removeInvalidVariables(arangodb::HashSet<Variable const*> const&
|
|||
}
|
||||
|
||||
/// @brief optimize the condition expression tree
|
||||
void Condition::optimize(ExecutionPlan* plan) {
|
||||
void Condition::optimize(ExecutionPlan* plan, bool multivalued) {
|
||||
if (_root == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
@ -822,6 +915,10 @@ void Condition::optimize(ExecutionPlan* plan) {
|
|||
size_t n = _root->numMembers();
|
||||
size_t r = 0;
|
||||
|
||||
const auto* resultsTable = multivalued
|
||||
? ResultsTableMultiValued
|
||||
: ResultsTable;
|
||||
|
||||
while (r < n) { // foreach OR-Node
|
||||
bool retry = false;
|
||||
auto oldAnd = _root->getMemberUnchecked(r);
|
||||
|
@ -981,7 +1078,8 @@ void Condition::optimize(ExecutionPlan* plan) {
|
|||
|
||||
// IN-merging
|
||||
if (leftNode->type == NODE_TYPE_OPERATOR_BINARY_IN &&
|
||||
leftNode->getMemberUnchecked(1)->isConstant()) {
|
||||
leftNode->getMemberUnchecked(1)->isConstant() &&
|
||||
!multivalued) {
|
||||
TRI_ASSERT(leftNode->numMembers() == 2);
|
||||
|
||||
if (rightNode->type == NODE_TYPE_OPERATOR_BINARY_IN &&
|
||||
|
@ -1007,8 +1105,8 @@ void Condition::optimize(ExecutionPlan* plan) {
|
|||
for (size_t k = 0; k < values->numMembers(); ++k) {
|
||||
auto value = values->getMemberUnchecked(k);
|
||||
ConditionPartCompareResult res =
|
||||
ConditionPart::ResultsTable[CompareAstNodes(value, other.valueNode, true) + 1][0 /*NODE_TYPE_OPERATOR_BINARY_EQ*/]
|
||||
[other.whichCompareOperation()];
|
||||
ResultsTable[CompareAstNodes(value, other.valueNode, true) + 1][0 /*NODE_TYPE_OPERATOR_BINARY_EQ*/]
|
||||
[other.whichCompareOperation()];
|
||||
|
||||
bool const keep = (res == CompareResult::OTHER_CONTAINED_IN_SELF ||
|
||||
res == CompareResult::CONVERT_EQUAL);
|
||||
|
@ -1036,7 +1134,7 @@ void Condition::optimize(ExecutionPlan* plan) {
|
|||
// end of IN-merging
|
||||
|
||||
// Results are -1, 0, 1, move to 0, 1, 2 for the lookup:
|
||||
ConditionPartCompareResult res = ConditionPart::ResultsTable
|
||||
ConditionPartCompareResult res = resultsTable
|
||||
[CompareAstNodes(current.valueNode, other.valueNode, true) + 1]
|
||||
[current.whichCompareOperation()][other.whichCompareOperation()];
|
||||
|
||||
|
|
|
@ -56,8 +56,6 @@ enum ConditionPartCompareResult {
|
|||
enum AttributeSideType { ATTRIBUTE_LEFT, ATTRIBUTE_RIGHT };
|
||||
|
||||
struct ConditionPart {
|
||||
static ConditionPartCompareResult const ResultsTable[3][7][7];
|
||||
|
||||
ConditionPart() = delete;
|
||||
|
||||
ConditionPart(Variable const*, std::string const&, AstNode const*,
|
||||
|
@ -215,7 +213,9 @@ class Condition {
|
|||
|
||||
/// @brief normalize the condition
|
||||
/// this will convert the condition into its disjunctive normal form
|
||||
void normalize(ExecutionPlan*);
|
||||
/// @param mutlivalued attributes may have more than one value
|
||||
/// (ArangoSearch view case)
|
||||
void normalize(ExecutionPlan*, bool multivalued = false);
|
||||
|
||||
/// @brief normalize the condition
|
||||
/// this will convert the condition into its disjunctive normal form
|
||||
|
@ -251,7 +251,7 @@ class Condition {
|
|||
bool sortOrs(Variable const*, std::vector<Index const*>&);
|
||||
|
||||
/// @brief optimize the condition expression tree
|
||||
void optimize(ExecutionPlan*);
|
||||
void optimize(ExecutionPlan*, bool multivalued);
|
||||
|
||||
/// @brief registers an attribute access for a particular (collection)
|
||||
/// variable
|
||||
|
|
|
@ -137,7 +137,7 @@ arangodb::aql::AqlValue dummyFilterFunc(arangodb::aql::ExpressionContext*,
|
|||
arangodb::SmallVector<arangodb::aql::AqlValue> const&) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_NOT_IMPLEMENTED,
|
||||
"ArangoSearch filter functions EXISTS, STARTS_WITH, PHRASE, MIN_MATCH, "
|
||||
"ArangoSearch filter functions EXISTS, STARTS_WITH, IN_RANGE, PHRASE, MIN_MATCH, "
|
||||
"BOOST and ANALYZER "
|
||||
" are designed to be used only within a corresponding SEARCH statement "
|
||||
"of ArangoSearch view."
|
||||
|
@ -346,15 +346,11 @@ void registerFilters(arangodb::aql::AqlFunctionFeature& functions) {
|
|||
arangodb::aql::Function::makeFlags(arangodb::aql::Function::Flags::Deterministic,
|
||||
arangodb::aql::Function::Flags::Cacheable,
|
||||
arangodb::aql::Function::Flags::CanRunOnDBServer);
|
||||
addFunction(functions, {"EXISTS", ".|.,.", flags, &dummyFilterFunc}); // (attribute, [
|
||||
// "analyzer"|"type"|"string"|"numeric"|"bool"|"null"
|
||||
// ])
|
||||
addFunction(functions, {"EXISTS", ".|.,.", flags, &dummyFilterFunc}); // (attribute, [ // "analyzer"|"type"|"string"|"numeric"|"bool"|"null" // ])
|
||||
addFunction(functions, {"STARTS_WITH", ".,.|.", flags, &dummyFilterFunc}); // (attribute, prefix, scoring-limit)
|
||||
addFunction(functions, {"PHRASE", ".,.|.+", flags,
|
||||
&dummyFilterFunc}); // (attribute, input [, offset,
|
||||
// input... ] [, analyzer])
|
||||
addFunction(functions, {"MIN_MATCH", ".,.|.+", flags, &dummyFilterFunc}); // (filter expression [, filter expression,
|
||||
// ... ], min match count)
|
||||
addFunction(functions, {"PHRASE", ".,.|.+", flags, &dummyFilterFunc}); // (attribute, input [, offset, input... ] [, analyzer])
|
||||
addFunction(functions, {"IN_RANGE", ".,.,.,.,.", flags, &dummyFilterFunc}); // (attribute, lower, upper, include lower, include upper)
|
||||
addFunction(functions, {"MIN_MATCH", ".,.|.+", flags, &dummyFilterFunc}); // (filter expression [, filter expression, ... ], min match count)
|
||||
addFunction(functions, {"BOOST", ".,.", flags, &dummyFilterFunc}); // (filter expression, boost)
|
||||
addFunction(functions, {"ANALYZER", ".,.", flags, &dummyFilterFunc}); // (filter expression, analyzer)
|
||||
}
|
||||
|
@ -1097,4 +1093,4 @@ void IResearchFeature::validateOptions(std::shared_ptr<arangodb::options::Progra
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -353,7 +353,10 @@ bool byRange(irs::boolean_filter* filter, arangodb::aql::AstNode const& attribut
|
|||
}
|
||||
|
||||
if (!min.execute(ctx)) {
|
||||
// failed to execute expression
|
||||
LOG_TOPIC("1e23d", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "Failed to evaluate lower boundary from node '"
|
||||
<< arangodb::aql::AstNode::toString(&minValueNode)
|
||||
<< "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -367,13 +370,18 @@ bool byRange(irs::boolean_filter* filter, arangodb::aql::AstNode const& attribut
|
|||
}
|
||||
|
||||
if (!max.execute(ctx)) {
|
||||
// failed to execute expression
|
||||
LOG_TOPIC("5a0a4", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "Failed to evaluate upper boundary from node '"
|
||||
<< arangodb::aql::AstNode::toString(&maxValueNode)
|
||||
<< "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (min.type() != max.type()) {
|
||||
// type mismatch
|
||||
LOG_TOPIC("03078", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "Failed to build range query, lower boundary '" << arangodb::aql::AstNode::toString(&minValueNode)
|
||||
<< "' mismatches upper boundary '" << arangodb::aql::AstNode::toString(&maxValueNode) << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -396,10 +404,8 @@ bool byRange(irs::boolean_filter* filter, arangodb::aql::AstNode const& attribut
|
|||
range.boost(filterCtx.boost);
|
||||
range.term<irs::Bound::MIN>(irs::null_token_stream::value_null());
|
||||
range.include<irs::Bound::MIN>(minInclude);
|
||||
;
|
||||
range.term<irs::Bound::MAX>(irs::null_token_stream::value_null());
|
||||
range.include<irs::Bound::MAX>(maxInclude);
|
||||
;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -981,6 +987,7 @@ bool fromNegation(irs::boolean_filter* filter, QueryContext const& ctx,
|
|||
return ::filter(filter, ctx, subFilterCtx, *member);
|
||||
}
|
||||
|
||||
/*
|
||||
bool rangeFromBinaryAnd(irs::boolean_filter* filter, QueryContext const& ctx,
|
||||
FilterContext const& filterCtx,
|
||||
arangodb::aql::AstNode const& node) {
|
||||
|
@ -1029,6 +1036,7 @@ bool rangeFromBinaryAnd(irs::boolean_filter* filter, QueryContext const& ctx,
|
|||
// unable to create range
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
template <typename Filter>
|
||||
bool fromGroup(irs::boolean_filter* filter, QueryContext const& ctx,
|
||||
|
@ -1048,12 +1056,6 @@ bool fromGroup(irs::boolean_filter* filter, QueryContext const& ctx,
|
|||
// Note: cannot optimize for single member in AND/OR since 'a OR NOT b'
|
||||
// translates to 'a OR (OR NOT b)'
|
||||
|
||||
if (std::is_same<Filter, irs::And>::value && 2 == n &&
|
||||
rangeFromBinaryAnd(filter, ctx, filterCtx, node)) {
|
||||
// range case
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
filter = &filter->add<Filter>();
|
||||
filter->boost(filterCtx.boost);
|
||||
|
@ -1821,6 +1823,103 @@ bool fromFuncStartsWith(irs::boolean_filter* filter, QueryContext const& ctx,
|
|||
return true;
|
||||
}
|
||||
|
||||
// IN_RANGE(<attribute>, <low>, <high>, <include-low>, <include-high>)
|
||||
bool fromFuncInRange(irs::boolean_filter* filter, QueryContext const& ctx,
|
||||
FilterContext const& filterCtx,
|
||||
arangodb::aql::AstNode const& args) {
|
||||
if (!args.isDeterministic()) {
|
||||
LOG_TOPIC("dff45", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "Unable to handle non-deterministic arguments for 'IN_RANGE' "
|
||||
"function";
|
||||
return false; // nondeterministic
|
||||
}
|
||||
|
||||
auto const argc = args.numMembers();
|
||||
|
||||
if (argc != 5) {
|
||||
LOG_TOPIC("2f5a8", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: Invalid number of arguments passed "
|
||||
"(should be 5)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1st argument defines a field
|
||||
auto const* field =
|
||||
arangodb::iresearch::checkAttributeAccess(args.getMemberUnchecked(0), *ctx.ref);
|
||||
|
||||
if (!field) {
|
||||
LOG_TOPIC("7c56a", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: Unable to parse 1st argument as an "
|
||||
"attribute identifier";
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedAqlValue includeValue;
|
||||
auto getInclusion = [&ctx, filter, &includeValue](
|
||||
arangodb::aql::AstNode const* arg,
|
||||
bool& include,
|
||||
irs::string_ref const& argName) -> bool {
|
||||
if (!arg) {
|
||||
LOG_TOPIC("8ec00", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: " << argName << " argument is invalid";
|
||||
return false;
|
||||
}
|
||||
|
||||
includeValue.reset(*arg);
|
||||
|
||||
if (filter || includeValue.isConstant()) {
|
||||
if (!includeValue.execute(ctx)) {
|
||||
LOG_TOPIC("32f3b", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: Failed to evaluate " << argName << " argument";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arangodb::iresearch::SCOPED_VALUE_TYPE_BOOL != includeValue.type()) {
|
||||
LOG_TOPIC("57a29", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: " << argName << " argument has invalid type '"
|
||||
<< includeValue.type() << "' (boolean expected)";
|
||||
return false;
|
||||
}
|
||||
|
||||
include = includeValue.getBoolean();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 2nd argument defines a lower boundary
|
||||
auto const* lhsArg = args.getMemberUnchecked(1);
|
||||
|
||||
if (!lhsArg) {
|
||||
LOG_TOPIC("f1167", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: 2nd argument is invalid";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3rd argument defines an upper boundary
|
||||
auto const* rhsArg = args.getMemberUnchecked(2);
|
||||
|
||||
if (!rhsArg) {
|
||||
LOG_TOPIC("d5fe6", WARN, arangodb::iresearch::TOPIC)
|
||||
<< "'IN_RANGE' AQL function: 3rd argument is invalid";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4th argument defines inclusion of lower boundary
|
||||
bool lhsInclude = false;
|
||||
if (!getInclusion(args.getMemberUnchecked(3), lhsInclude, "4th")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5th argument defines inclusion of upper boundary
|
||||
bool rhsInclude = false;
|
||||
if (!getInclusion(args.getMemberUnchecked(4), rhsInclude, "5th")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return byRange(filter, *field, *lhsArg, lhsInclude, *rhsArg, rhsInclude, ctx, filterCtx);
|
||||
}
|
||||
|
||||
std::map<irs::string_ref, ConvertionHandler> const FCallUserConvertionHandlers;
|
||||
|
||||
bool fromFCallUser(irs::boolean_filter* filter, QueryContext const& ctx,
|
||||
|
@ -1868,7 +1967,9 @@ bool fromFCallUser(irs::boolean_filter* filter, QueryContext const& ctx,
|
|||
std::map<std::string, ConvertionHandler> const FCallSystemConvertionHandlers{
|
||||
{"PHRASE", fromFuncPhrase}, {"STARTS_WITH", fromFuncStartsWith},
|
||||
{"EXISTS", fromFuncExists}, {"BOOST", fromFuncBoost},
|
||||
{"ANALYZER", fromFuncAnalyzer}, {"MIN_MATCH", fromFuncMinMatch}};
|
||||
{"ANALYZER", fromFuncAnalyzer}, {"MIN_MATCH", fromFuncMinMatch},
|
||||
{"IN_RANGE", fromFuncInRange}
|
||||
};
|
||||
|
||||
bool fromFCall(irs::boolean_filter* filter, QueryContext const& ctx,
|
||||
FilterContext const& filterCtx, arangodb::aql::AstNode const& node) {
|
||||
|
@ -1987,4 +2088,4 @@ namespace iresearch {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -177,6 +177,11 @@ class IResearchView final: public arangodb::LogicalView {
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
bool visitCollections(CollectionVisitor const& visitor) const override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief persist data store states for all known links to permanent storage
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
arangodb::Result commit();
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -219,11 +224,6 @@ class IResearchView final: public arangodb::LogicalView {
|
|||
IResearchViewMeta&& meta // view meta
|
||||
);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief persist data store states for all known links to permanent storage
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
arangodb::Result commit();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief called when a view's properties are updated (i.e. delta-modified)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -241,4 +241,4 @@ class IResearchView final: public arangodb::LogicalView {
|
|||
} // iresearch
|
||||
} // arangodb
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -80,7 +80,7 @@ bool optimizeSearchCondition(IResearchViewNode& viewNode, Query& query, Executio
|
|||
|
||||
if (!viewNode.filterConditionIsEmpty()) {
|
||||
searchCondition.andCombine(&viewNode.filterCondition());
|
||||
searchCondition.normalize(&plan); // normalize the condition
|
||||
searchCondition.normalize(&plan, true); // normalize the condition
|
||||
|
||||
if (searchCondition.isEmpty()) {
|
||||
// condition is always false
|
||||
|
@ -102,11 +102,11 @@ bool optimizeSearchCondition(IResearchViewNode& viewNode, Query& query, Executio
|
|||
}
|
||||
|
||||
// check filter condition
|
||||
auto const conditionValid =
|
||||
!searchCondition.root() ||
|
||||
FilterFactory::filter(nullptr,
|
||||
{ query.trx(), nullptr, nullptr, nullptr, &viewNode.outVariable() },
|
||||
*searchCondition.root());
|
||||
auto const conditionValid = !searchCondition.root() || FilterFactory::filter(
|
||||
nullptr,
|
||||
{ query.trx(), nullptr, nullptr, nullptr, &viewNode.outVariable() },
|
||||
*searchCondition.root()
|
||||
);
|
||||
|
||||
if (!conditionValid) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_PARSE,
|
||||
|
@ -308,4 +308,4 @@ void scatterViewInClusterRule(arangodb::aql::Optimizer* opt,
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -47,10 +47,12 @@ if (USE_IRESEARCH)
|
|||
IResearch/IResearchQueryNullTerm-test.cpp
|
||||
IResearch/IResearchQueryNumericTerm-test.cpp
|
||||
IResearch/IResearchQueryOr-test.cpp
|
||||
IResearch/IResearchQueryOptimization-test.cpp
|
||||
IResearch/IResearchQueryPhrase-test.cpp
|
||||
IResearch/IResearchQueryScorer-test.cpp
|
||||
IResearch/IResearchQuerySelectAll-test.cpp
|
||||
IResearch/IResearchQueryStartsWith-test.cpp
|
||||
IResearch/IResearchQueryInRange-test.cpp
|
||||
IResearch/IResearchQueryStringTerm-test.cpp
|
||||
IResearch/IResearchQueryTokens-test.cpp
|
||||
IResearch/IResearchQueryValue-test.cpp
|
||||
|
|
|
@ -2160,9 +2160,12 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false).insert<irs::Bound::MIN>(minTerm)
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false).insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MAX>(false).insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c > 15 and d.a.b.c < 40 RETURN d", expected);
|
||||
|
@ -2188,11 +2191,16 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(false).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.boost(1.5);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER boost(d.a.b.c > 15 and d.a.b.c < 40, 1.5) RETURN d", expected);
|
||||
}
|
||||
|
@ -2445,10 +2453,15 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.field(mangleNumeric("a.b[42].c"))
|
||||
.include<irs::Bound::MIN>(false).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(false).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b[42].c"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b[42].c"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b[42].c > 15 and d.a.b[42].c < 40 RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d['a'].b[42].c > 15 and d['a']['b'][42]['c'] < 40 RETURN d", expected);
|
||||
|
@ -2473,10 +2486,15 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(false).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c >= 15 and d.a.b.c < 40 RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a['b']['c'] >= 15 and d['a']['b']['c'] < 40 RETURN d", expected);
|
||||
|
@ -2497,10 +2515,15 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(true).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c >= 15 and d.a.b.c <= 40 RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a['b']['c'] >= 15 and d.a.b.c <= 40 RETURN d", expected);
|
||||
|
@ -2612,10 +2635,15 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(true).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c > 15 and d.a.b.c <= 40 RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d['a'].b.c > 15 and d.a.b.c <= 40 RETURN d", expected);
|
||||
|
@ -2733,10 +2761,15 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.field(mangleNumeric("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MIN>(false).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(true).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] > 15 && d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] <= 40 RETURN d", expected, &ctx);
|
||||
assertFilterSuccess("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER 15 < d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] && 40 >= d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] RETURN d", expected, &ctx);
|
||||
|
@ -2777,10 +2810,15 @@ SECTION("BinaryAnd") {
|
|||
// string range
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(false).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c > '15' and d.a.b.c < '40' RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d['a']['b']['c'] > '15' and d.a.b.c < '40' RETURN d", expected);
|
||||
|
@ -2795,10 +2833,15 @@ SECTION("BinaryAnd") {
|
|||
// string range
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(false).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c >= '15' and d.a.b.c < '40' RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d['a']['b'].c >= '15' and d['a']['b']['c'] < '40' RETURN d", expected);
|
||||
|
@ -2813,11 +2856,16 @@ SECTION("BinaryAnd") {
|
|||
// string range, boost, analyzer
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(0.5);
|
||||
range.field(mangleString("a.b.c", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(false).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.boost(0.5);
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleString("a.b.c", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleString("a.b.c", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER analyzer(boost(d.a.b.c >= '15' and d.a.b.c < '40', 0.5), 'test_analyzer') RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER boost(analyzer(d['a']['b'].c >= '15' and d['a']['b']['c'] < '40', 'test_analyzer'), 0.5) RETURN d", expected);
|
||||
|
@ -2826,10 +2874,15 @@ SECTION("BinaryAnd") {
|
|||
// string range
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c >= '15' and d.a.b.c <= '40' RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d['a']['b']['c'] >= '15' and d.a.b.c <= '40' RETURN d", expected);
|
||||
|
@ -2896,10 +2949,15 @@ SECTION("BinaryAnd") {
|
|||
// string range
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c > '15' and d.a.b.c <= '40' RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c > '15' and d.a.b.c <= '40' RETURN d", expected);
|
||||
|
@ -2917,10 +2975,15 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c.e.f"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER d.a.b.c.e.f > TO_STRING(numVal+13) && d.a.b.c.e.f <= TO_STRING(numVal+38) RETURN d",
|
||||
|
@ -2941,11 +3004,16 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(2.f);
|
||||
range.field(mangleString("a.b.c.e.f", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.boost(2.f);
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleString("a.b.c.e.f", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleString("a.b.c.e.f", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER boost(analyzer(d.a.b.c.e.f > TO_STRING(numVal+13) && d.a.b.c.e.f <= TO_STRING(numVal+38), 'test_analyzer'), numVal) RETURN d",
|
||||
|
@ -2969,10 +3037,15 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("offsetDbl", arangodb::aql::AqlValue(arangodb::aql::AqlValue(arangodb::aql::AqlValueHintDouble{5.6})));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("15")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("40");
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MIN>(false)
|
||||
.term<irs::Bound::MIN>("15");
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>("40");
|
||||
|
||||
assertFilterSuccess("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] > '15' && d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] <= '40' RETURN d", expected, &ctx);
|
||||
assertFilterSuccess("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER '15' < d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] && '40' >= d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')] RETURN d", expected, &ctx);
|
||||
|
@ -3113,10 +3186,15 @@ SECTION("BinaryAnd") {
|
|||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
irs::Or expected;
|
||||
expected.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MIN>(true).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess("FOR d IN collection FILTER d.a.b.c >= 15.5 and d.a.b.c < 40 RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN collection FILTER d['a']['b'].c >= 15.5 and d['a']['b'].c < 40 RETURN d", expected);
|
||||
|
@ -3414,10 +3492,15 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>(irs::boolean_token_stream::value_true())
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>(irs::boolean_token_stream::value_true());
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>(irs::boolean_token_stream::value_true());
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>(irs::boolean_token_stream::value_true());
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER d.a.b.c.e.f >= (numVal < 13) && d.a.b.c.e.f <= (numVal > 1) RETURN d",
|
||||
|
@ -3438,11 +3521,16 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>(irs::boolean_token_stream::value_true())
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>(irs::boolean_token_stream::value_true());
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.boost(1.5);
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>(irs::boolean_token_stream::value_true());
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>(irs::boolean_token_stream::value_true());
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER boost(d.a.b.c.e.f >= (numVal < 13) && d.a.b.c.e.f <= (numVal > 1), 1.5) RETURN d",
|
||||
|
@ -3492,10 +3580,16 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("nullVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintNull{}));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>(irs::null_token_stream::value_null())
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>(irs::null_token_stream::value_null());
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>(irs::null_token_stream::value_null());
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>(irs::null_token_stream::value_null());
|
||||
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET nullVal=null FOR d IN collection FILTER d.a.b.c.e.f >= (nullVal && true) && d.a.b.c.e.f <= (nullVal && false) RETURN d",
|
||||
|
@ -3516,11 +3610,16 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("nullVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintNull{}));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>(irs::null_token_stream::value_null())
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>(irs::null_token_stream::value_null());
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.boost(1.5);
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.term<irs::Bound::MIN>(irs::null_token_stream::value_null());
|
||||
root.add<irs::by_range>()
|
||||
.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MAX>(true)
|
||||
.term<irs::Bound::MAX>(irs::null_token_stream::value_null());
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET nullVal=null FOR d IN collection FILTER boost(d.a.b.c.e.f >= (nullVal && true) && d.a.b.c.e.f <= (nullVal && false), 1.5) RETURN d",
|
||||
|
@ -3544,10 +3643,15 @@ SECTION("BinaryAnd") {
|
|||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
expected.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(false).insert<irs::Bound::MAX>(maxTerm);
|
||||
auto& root = expected.add<irs::And>();
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true)
|
||||
.insert<irs::Bound::MIN>(minTerm);
|
||||
root.add<irs::by_granular_range>()
|
||||
.field(mangleNumeric("a.b.c.e.f"))
|
||||
.include<irs::Bound::MAX>(false)
|
||||
.insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER d.a['b'].c.e.f >= (numVal + 13.5) && d.a.b.c.e.f < (numVal + 38) RETURN d",
|
||||
|
@ -3653,4 +3757,4 @@ SECTION("BinaryAnd") {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -2283,8 +2283,199 @@ SECTION("StartsWith") {
|
|||
assertFilterFail("FOR d IN myView FILTER starts_with(d.name, 'abc', RAND() ? 128 : 10) RETURN d");
|
||||
}
|
||||
|
||||
SECTION("IN_RANGE") {
|
||||
// d.name > 'a' && d.name < 'z'
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("name"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("a")
|
||||
.include<irs::Bound::MAX>(false).term<irs::Bound::MAX>("z");
|
||||
|
||||
assertFilterSuccess("FOR d IN myView FILTER in_range(d['name'], 'a', 'z', false, false) RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN myView FILTER in_range(d.name, 'a', 'z', false, false) RETURN d", expected);
|
||||
}
|
||||
|
||||
// BOOST(d.name >= 'a' && d.name <= 'z', 1.5)
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleStringIdentity("name"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>("a")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("z");
|
||||
|
||||
assertFilterSuccess("FOR d IN myView FILTER boost(in_range(d['name'], 'a', 'z', true, true), 1.5) RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN myView FILTER boost(in_range(d.name, 'a', 'z', true, true), 1.5) RETURN d", expected);
|
||||
}
|
||||
|
||||
// ANALYZER(BOOST(d.name > 'a' && d.name <= 'z', 1.5), "testVocbase::test_analyzer")
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleString("name", "testVocbase::test_analyzer"))
|
||||
.include<irs::Bound::MIN>(false).term<irs::Bound::MIN>("a")
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>("z");
|
||||
|
||||
assertFilterSuccess("FOR d IN myView FILTER analyzer(boost(in_range(d['name'], 'a', 'z', false, true), 1.5), 'testVocbase::test_analyzer') RETURN d", expected);
|
||||
assertFilterSuccess("FOR d IN myView FILTER analyzer(boost(in_range(d.name, 'a', 'z', false, true), 1.5), 'testVocbase::test_analyzer') RETURN d", expected);
|
||||
}
|
||||
|
||||
// dynamic complex attribute field
|
||||
{
|
||||
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})));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.field(mangleStringIdentity("a.b.c.e[4].f[5].g[3].g.a"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>("abc")
|
||||
.include<irs::Bound::MAX>(false).term<irs::Bound::MAX>("bce");
|
||||
|
||||
assertFilterSuccess("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER in_range(d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')], 'abc', 'bce', true, false) RETURN d", expected, &ctx);
|
||||
assertFilterSuccess("LET a='a' LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER in_range(d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')], CONCAT(_FORWARD_('a'), _FORWARD_('bc')), CONCAT(_FORWARD_('bc'), _FORWARD_('e')), _FORWARD_(5) > _FORWARD_(4), _FORWARD_(5) > _FORWARD_(6)) RETURN d", expected, &ctx);
|
||||
}
|
||||
|
||||
// invalid dynamic attribute name (null value)
|
||||
{
|
||||
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("LET a=null LET c='c' LET offsetInt=4 LET offsetDbl=5.6 FOR d IN collection FILTER in_range(d[a].b[c].e[offsetInt].f[offsetDbl].g[_FORWARD_(3)].g[_FORWARD_('a')], 'abc', 'bce', true, false) RETURN d", &ctx);
|
||||
}
|
||||
|
||||
// boolean expression in range, boost
|
||||
{
|
||||
ExpressionContextMock ctx;
|
||||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleBool("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>(irs::boolean_token_stream::value_true())
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>(irs::boolean_token_stream::value_true());
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER boost(in_rangE(d.a.b.c.e.f, (numVal < 13), (numVal > 1), true, true), 1.5) RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER boost(in_rangE(d.a.b.c.e.f, (numVal < 13), (numVal > 1), true, true), 1.5) RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
}
|
||||
|
||||
// null expression in range, boost
|
||||
{
|
||||
ExpressionContextMock ctx;
|
||||
ctx.vars.emplace("nullVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintNull{}));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleNull("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).term<irs::Bound::MIN>(irs::null_token_stream::value_null())
|
||||
.include<irs::Bound::MAX>(true).term<irs::Bound::MAX>(irs::null_token_stream::value_null());
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET nullVal=null FOR d IN collection FILTER BOOST(in_range(d.a.b.c.e.f, (nullVal && true), (nullVal && false), true, true), 1.5) RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET nullVal=null FOR d IN collection FILTER bOoST(in_range(d.a.b.c.e.f, (nullVal && false), (nullVal && true), true, true), 1.5) RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
}
|
||||
|
||||
// numeric expression in range, boost
|
||||
{
|
||||
irs::numeric_token_stream minTerm; minTerm.reset(15.5);
|
||||
irs::numeric_token_stream maxTerm; maxTerm.reset(40.);
|
||||
|
||||
ExpressionContextMock ctx;
|
||||
ctx.vars.emplace("numVal", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
|
||||
irs::Or expected;
|
||||
auto& range = expected.add<irs::by_granular_range>();
|
||||
range.boost(1.5);
|
||||
range.field(mangleNumeric("a.b.c.e.f"))
|
||||
.include<irs::Bound::MIN>(true).insert<irs::Bound::MIN>(minTerm)
|
||||
.include<irs::Bound::MAX>(false).insert<irs::Bound::MAX>(maxTerm);
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER boost(in_range(d.a['b'].c.e.f, (numVal + 13.5), (numVal + 38), true, false), 1.5) RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER boost(IN_RANGE(d.a.b.c.e.f, (numVal + 13.5), (numVal + 38), true, false), 1.5) RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
|
||||
assertFilterSuccess(
|
||||
"LET numVal=2 FOR d IN collection FILTER analyzer(boost(in_range(d.a.b.c.e.f, (numVal + 13.5), (numVal + 38), true, false), 1.5), 'test_analyzer') RETURN d",
|
||||
expected,
|
||||
&ctx // expression context
|
||||
);
|
||||
}
|
||||
|
||||
// invalid attribute access
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(['d'], 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range([d], 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d, 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d[*], 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.a[*].c, 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range('d.name', 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(123, 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(123.5, 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(null, 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(true, 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(false, 'abc', true, 'z', false) RETURN d");
|
||||
|
||||
// invalid type of inclusion argument
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, 'z', 'false') RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, 'z', 0) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, 'z', null) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', 'true', 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', 1, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', null, 'z', false) RETURN d");
|
||||
|
||||
// non-deterministic argument
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d[RAND() ? 'name' : 'x'], 'abc', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, RAND() ? 'abc' : 'def', true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', RAND() ? true : false, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, RAND() ? 'z' : 'x', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, 'z', RAND() ? false : true) RETURN d");
|
||||
|
||||
// lower/upper boundary type mismatch
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 1, true, 'z', false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, null, false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, bool, true, null, false) RETURN d");
|
||||
assertFilterFail("FOR d IN myView FILTER in_range(d.name, bool, true, 1, false) RETURN d");
|
||||
|
||||
// wrong number of arguments
|
||||
assertFilterParseFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, 'z') RETURN d");
|
||||
assertFilterParseFail("FOR d IN myView FILTER in_range(d.name, 'abc', true, 'z', false, false) RETURN d");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,711 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2017 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Andrey Abramov
|
||||
/// @author Vasiliy Nabatchikov
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "common.h"
|
||||
|
||||
#include "../Mocks/StorageEngineMock.h"
|
||||
|
||||
#if USE_ENTERPRISE
|
||||
#include "Enterprise/Ldap/LdapFeature.h"
|
||||
#endif
|
||||
|
||||
#include "Cluster/ClusterFeature.h"
|
||||
#include "V8Server/V8DealerFeature.h"
|
||||
#include "Aql/AqlFunctionFeature.h"
|
||||
#include "Aql/Ast.h"
|
||||
#include "Aql/OptimizerRulesFeature.h"
|
||||
#include "Aql/Query.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "GeneralServer/AuthenticationFeature.h"
|
||||
#include "IResearch/IResearchCommon.h"
|
||||
#include "IResearch/IResearchFeature.h"
|
||||
#include "IResearch/IResearchFilterFactory.h"
|
||||
#include "IResearch/IResearchView.h"
|
||||
#include "IResearch/IResearchAnalyzerFeature.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "Logger/LogTopic.h"
|
||||
#include "StorageEngine/EngineSelectorFeature.h"
|
||||
#include "RestServer/DatabasePathFeature.h"
|
||||
#include "RestServer/ViewTypesFeature.h"
|
||||
#include "RestServer/AqlFeature.h"
|
||||
#include "RestServer/DatabaseFeature.h"
|
||||
#include "RestServer/QueryRegistryFeature.h"
|
||||
#include "RestServer/SystemDatabaseFeature.h"
|
||||
#include "RestServer/TraverserEngineRegistryFeature.h"
|
||||
#include "Sharding/ShardingFeature.h"
|
||||
#include "V8/v8-globals.h"
|
||||
#include "VocBase/LogicalCollection.h"
|
||||
#include "VocBase/LogicalView.h"
|
||||
|
||||
#include "3rdParty/iresearch/tests/tests_config.hpp"
|
||||
#include "Transaction/StandaloneContext.h"
|
||||
#include "Utils/SingleCollectionTransaction.h"
|
||||
|
||||
#include "IResearch/VelocyPackHelper.h"
|
||||
#include "analysis/analyzers.hpp"
|
||||
#include "analysis/token_attributes.hpp"
|
||||
#include "utils/utf8_path.hpp"
|
||||
|
||||
#include <velocypack/Iterator.h>
|
||||
|
||||
extern const char* ARGV0; // defined in main.cpp
|
||||
|
||||
NS_LOCAL
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- setup / tear-down
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct IResearchQueryInRangeSetup {
|
||||
StorageEngineMock engine;
|
||||
arangodb::application_features::ApplicationServer server;
|
||||
std::unique_ptr<TRI_vocbase_t> system;
|
||||
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
|
||||
|
||||
IResearchQueryInRangeSetup(): engine(server), server(nullptr, nullptr) {
|
||||
arangodb::EngineSelectorFeature::ENGINE = &engine;
|
||||
|
||||
arangodb::tests::init(true);
|
||||
|
||||
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
|
||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
|
||||
|
||||
// suppress log messages since tests check error conditions
|
||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::FIXME.name(), arangodb::LogLevel::ERR); // suppress WARNING DefaultCustomTypeHandler called
|
||||
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::FATAL);
|
||||
irs::logger::output_le(iresearch::logger::IRL_FATAL, stderr);
|
||||
|
||||
// setup required application features
|
||||
features.emplace_back(new arangodb::V8DealerFeature(server), false); // required for DatabaseFeature::createDatabase(...)
|
||||
features.emplace_back(new arangodb::ViewTypesFeature(server), true);
|
||||
features.emplace_back(new arangodb::AuthenticationFeature(server), true);
|
||||
features.emplace_back(new arangodb::DatabasePathFeature(server), false);
|
||||
features.emplace_back(new arangodb::DatabaseFeature(server), false);
|
||||
features.emplace_back(new arangodb::ShardingFeature(server), false); //
|
||||
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // must be first
|
||||
arangodb::application_features::ApplicationServer::server->addFeature(features.back().first); // need QueryRegistryFeature feature to be added now in order to create the system database
|
||||
system = irs::memory::make_unique<TRI_vocbase_t>(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE);
|
||||
features.emplace_back(new arangodb::SystemDatabaseFeature(server, system.get()), false); // required for IResearchAnalyzerFeature
|
||||
features.emplace_back(new arangodb::TraverserEngineRegistryFeature(server), false); // must be before AqlFeature
|
||||
features.emplace_back(new arangodb::AqlFeature(server), true);
|
||||
features.emplace_back(new arangodb::aql::OptimizerRulesFeature(server), true);
|
||||
features.emplace_back(new arangodb::aql::AqlFunctionFeature(server), true); // required for IResearchAnalyzerFeature
|
||||
features.emplace_back(new arangodb::iresearch::IResearchAnalyzerFeature(server), true);
|
||||
features.emplace_back(new arangodb::iresearch::IResearchFeature(server), true);
|
||||
|
||||
#if USE_ENTERPRISE
|
||||
features.emplace_back(new arangodb::LdapFeature(server), false); // required for AuthenticationFeature with USE_ENTERPRISE
|
||||
#endif
|
||||
|
||||
// required for V8DealerFeature::prepare(), ClusterFeature::prepare() not required
|
||||
arangodb::application_features::ApplicationServer::server->addFeature(
|
||||
new arangodb::ClusterFeature(server)
|
||||
);
|
||||
|
||||
for (auto& f : features) {
|
||||
arangodb::application_features::ApplicationServer::server->addFeature(f.first);
|
||||
}
|
||||
|
||||
for (auto& f : features) {
|
||||
f.first->prepare();
|
||||
}
|
||||
|
||||
for (auto& f : features) {
|
||||
if (f.second) {
|
||||
f.first->start();
|
||||
}
|
||||
}
|
||||
|
||||
TRI_vocbase_t* vocbase;
|
||||
|
||||
auto* dbFeature = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||
arangodb::DatabaseFeature
|
||||
>("Database");
|
||||
dbFeature->createDatabase(1, "testVocbase", vocbase); // required for IResearchAnalyzerFeature::emplace(...)
|
||||
|
||||
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||
arangodb::iresearch::IResearchAnalyzerFeature
|
||||
>();
|
||||
|
||||
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
|
||||
analyzers->emplace(
|
||||
result,
|
||||
"testVocbase::test_csv_analyzer",
|
||||
"TestDelimAnalyzer",
|
||||
","
|
||||
); // cache analyzer
|
||||
|
||||
analyzers->emplace(
|
||||
result,
|
||||
"testVocbase::test_analyzer",
|
||||
"TestAnalyzer",
|
||||
"abc"
|
||||
); // cache analyzer
|
||||
|
||||
auto* dbPathFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabasePathFeature>("DatabasePath");
|
||||
arangodb::tests::setDatabasePath(*dbPathFeature); // ensure test data is stored in a unique directory
|
||||
}
|
||||
|
||||
~IResearchQueryInRangeSetup() {
|
||||
system.reset(); // destroy before reseting the 'ENGINE'
|
||||
arangodb::AqlFeature(server).stop(); // unset singleton instance
|
||||
arangodb::LogTopic::setLogLevel(arangodb::iresearch::TOPIC.name(), arangodb::LogLevel::DEFAULT);
|
||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::FIXME.name(), arangodb::LogLevel::DEFAULT);
|
||||
arangodb::application_features::ApplicationServer::server = nullptr;
|
||||
|
||||
// destroy application features
|
||||
for (auto& f : features) {
|
||||
if (f.second) {
|
||||
f.first->stop();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& f : features) {
|
||||
f.first->unprepare();
|
||||
}
|
||||
|
||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
|
||||
arangodb::EngineSelectorFeature::ENGINE = nullptr;
|
||||
}
|
||||
}; // IResearchQueryInRangeSetup
|
||||
|
||||
NS_END
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- test suite
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief setup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TEST_CASE("IResearchQueryInRange", "[iresearch][iresearch-query]") {
|
||||
IResearchQueryInRangeSetup s;
|
||||
UNUSED(s);
|
||||
|
||||
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase");
|
||||
std::vector<arangodb::velocypack::Builder> insertedDocs;
|
||||
arangodb::LogicalView* view;
|
||||
|
||||
// create collection0
|
||||
{
|
||||
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection0\" }");
|
||||
auto collection = vocbase.createCollection(createJson->slice());
|
||||
REQUIRE((nullptr != collection));
|
||||
|
||||
std::vector<std::shared_ptr<arangodb::velocypack::Builder>> docs {
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -6, \"value\": null }"),
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -5, \"value\": true }"),
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -4, \"value\": \"abc\" }"),
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -3, \"value\": [ 3.14, -3.14 ] }"),
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -2, \"value\": [ 1, \"abc\" ] }"),
|
||||
arangodb::velocypack::Parser::fromJson("{ \"seq\": -1, \"value\": { \"a\": 7, \"b\": \"c\" } }"),
|
||||
};
|
||||
|
||||
arangodb::OperationOptions options;
|
||||
options.returnNew = true;
|
||||
arangodb::SingleCollectionTransaction trx(
|
||||
arangodb::transaction::StandaloneContext::Create(vocbase),
|
||||
*collection,
|
||||
arangodb::AccessMode::Type::WRITE
|
||||
);
|
||||
CHECK((trx.begin().ok()));
|
||||
|
||||
for (auto& entry: docs) {
|
||||
auto res = trx.insert(collection->name(), entry->slice(), options);
|
||||
CHECK((res.ok()));
|
||||
insertedDocs.emplace_back(res.slice().get("new"));
|
||||
}
|
||||
|
||||
CHECK((trx.commit().ok()));
|
||||
}
|
||||
|
||||
// create collection1
|
||||
{
|
||||
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\" }");
|
||||
auto collection = vocbase.createCollection(createJson->slice());
|
||||
REQUIRE((nullptr != collection));
|
||||
|
||||
irs::utf8_path resource;
|
||||
resource/=irs::string_ref(IResearch_test_resource_dir);
|
||||
resource/=irs::string_ref("simple_sequential.json");
|
||||
|
||||
auto builder = arangodb::basics::VelocyPackHelper::velocyPackFromFile(resource.utf8());
|
||||
auto slice = builder.slice();
|
||||
REQUIRE(slice.isArray());
|
||||
|
||||
arangodb::OperationOptions options;
|
||||
options.returnNew = true;
|
||||
arangodb::SingleCollectionTransaction trx(
|
||||
arangodb::transaction::StandaloneContext::Create(vocbase),
|
||||
*collection,
|
||||
arangodb::AccessMode::Type::WRITE
|
||||
);
|
||||
CHECK((trx.begin().ok()));
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto res = trx.insert(collection->name(), itr.value(), options);
|
||||
CHECK((res.ok()));
|
||||
insertedDocs.emplace_back(res.slice().get("new"));
|
||||
}
|
||||
|
||||
CHECK((trx.commit().ok()));
|
||||
}
|
||||
|
||||
// create view
|
||||
{
|
||||
auto createJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }");
|
||||
auto logicalView = vocbase.createView(createJson->slice());
|
||||
REQUIRE((false == !logicalView));
|
||||
|
||||
view = logicalView.get();
|
||||
auto* impl = dynamic_cast<arangodb::iresearch::IResearchView*>(view);
|
||||
REQUIRE((false == !impl));
|
||||
|
||||
auto updateJson = arangodb::velocypack::Parser::fromJson(
|
||||
"{ \"links\": {"
|
||||
"\"testCollection0\": { \"analyzers\": [ \"test_analyzer\", \"identity\" ], \"includeAllFields\": true, \"trackListPositions\": false, \"storeValues\":\"id\" },"
|
||||
"\"testCollection1\": { \"analyzers\": [ \"test_analyzer\", \"identity\" ], \"includeAllFields\": true, \"storeValues\":\"id\" }"
|
||||
"}}"
|
||||
);
|
||||
|
||||
CHECK((impl->properties(updateJson->slice(), true).ok()));
|
||||
std::set<TRI_voc_cid_t> cids;
|
||||
impl->visitCollections([&cids](TRI_voc_cid_t cid)->bool { cids.emplace(cid); return true; });
|
||||
CHECK((2 == cids.size()));
|
||||
CHECK(impl->commit().ok());
|
||||
}
|
||||
|
||||
// d.value > false && d.value <= true
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[1].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.value, false, true, false, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.value >= null && d.value <= null
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[0].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.value, null, null, true, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.value > null && d.value <= null
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.value, null, null, false, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.name >= 'A' && d.name <= 'A'
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'A', true, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.name >= 'B' && d.name <= 'A'
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'B', 'A', true, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.name >= 'A' && d.name <= 'E'
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(),
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
insertedDocs[10].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'E', true, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.name >= 'A' && d.name < 'E'
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(),
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'E', true, false) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.name > 'A' && d.name <= 'E'
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
insertedDocs[10].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'E', false, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.name > 'A' && d.name < 'E'
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'E', false, false) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.seq >= 5 && d.seq <= -1
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.seq, 5, -1, true, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.seq >= 1 && d.seq <= 5
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
insertedDocs[10].slice(),
|
||||
insertedDocs[11].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.seq, 1, 5, true, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.seq > -2 && d.seq <= 5
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[5].slice(),
|
||||
insertedDocs[6].slice(),
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
insertedDocs[10].slice(),
|
||||
insertedDocs[11].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.seq, -2, 5, false, true) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.seq > 1 && d.seq < 5
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
insertedDocs[10].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.seq, 1, 5, false, false) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.seq >= 1 && d.seq < 5
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[7].slice(),
|
||||
insertedDocs[8].slice(),
|
||||
insertedDocs[9].slice(),
|
||||
insertedDocs[10].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.seq, 1, 5, true, false) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.value > 3 && d.value < 4
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[3].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.value, 3, 4, false, false) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
|
||||
// d.value > -4 && d.value < -3
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[3].slice(),
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.value, -4, -3, false, false) SORT d.seq RETURN d"
|
||||
);
|
||||
REQUIRE(TRI_ERROR_NO_ERROR == result.code);
|
||||
auto slice = result.result->slice();
|
||||
CHECK(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
|
||||
CHECK((i < expected.size()));
|
||||
CHECK((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++], resolved, true)));
|
||||
}
|
||||
|
||||
CHECK((i == expected.size()));
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
File diff suppressed because it is too large
Load Diff
|
@ -366,7 +366,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
"LET arr = [0,1] "
|
||||
"FOR i in 0..1 "
|
||||
" LET rnd = _NONDETERM_(i) "
|
||||
" FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
" FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"LIMIT 10 "
|
||||
"RETURN { d, score: d.seq + 3*customscorer(d, arr[TO_NUMBER(rnd != 0)]) }";
|
||||
|
||||
|
@ -478,7 +478,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET i = 1"
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name < 'B' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'B', true, false) "
|
||||
"RETURN [ customscorer(d, i), customscorer(d, 1) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -584,7 +584,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_({ value : 2 }) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj.value), customscorer(d, obj.value) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -695,7 +695,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_({ value : 2 }) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj.value+1), customscorer(d, obj.value+1) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -806,7 +806,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj[1]), customscorer(d, obj[1]) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -917,7 +917,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj[0] > obj[1] ? 1 : 2), customscorer(d, obj[0] > obj[1] ? 1 : 2) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1028,7 +1028,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj[0] > obj[1] ? 1 : 2), customscorer(d, obj[1] > obj[2] ? 1 : 2) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1148,7 +1148,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, 5*obj[0]*TO_NUMBER(obj[1] > obj[2])/obj[1] - 1), customscorer(d, 5*obj[0]*TO_NUMBER(obj[1] > obj[2])/obj[1] - 1) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1259,7 +1259,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, { [ CONCAT(obj[0], obj[1]) ] : 1 }), customscorer(d, { [ CONCAT(obj[0], obj[1]) ] : 1 }) ]";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1346,7 +1346,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, { foo : obj[1] }), customscorer(d, { foo : obj[1] }) ]";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1433,7 +1433,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, 5*obj[0]*TO_NUMBER(obj[1] > obj[2])/obj[1] - 1), customscorer(d, 5*obj[0]*TO_NUMBER(obj[1] > obj[2])/obj[1] - 2) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1553,7 +1553,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj any == 3), customscorer(d, obj any == 3) ]";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1640,7 +1640,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
{
|
||||
std::string const queryString =
|
||||
"LET obj = _NONDETERM_([ 2, 5 ]) "
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ customscorer(d, obj any == 3), customscorer(d, obj all == 3) ]";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1724,7 +1724,7 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
// con't deduplicate scorers with default values
|
||||
{
|
||||
std::string const queryString =
|
||||
"FOR d IN testView SEARCH d.name >= 'A' AND d.name <= 'C' "
|
||||
"FOR d IN testView SEARCH IN_RANGE(d.name, 'A', 'C', true, true) "
|
||||
"RETURN [ tfidf(d), tfidf(d, false) ] ";
|
||||
|
||||
CHECK(arangodb::tests::assertRules(
|
||||
|
@ -1828,4 +1828,4 @@ TEST_CASE("IResearchQueryScorer", "[iresearch][iresearch-query]") {
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "IResearch/AqlHelper.h"
|
||||
#include "IResearch/ExpressionFilter.h"
|
||||
#include "IResearch/IResearchFilterFactory.h"
|
||||
#include "IResearch/IResearchViewNode.h"
|
||||
#include "IResearch/IResearchKludge.h"
|
||||
#include "IResearch/VelocyPackHelper.h"
|
||||
#include "tests/Basics/icu-helper.h"
|
||||
|
@ -440,6 +441,59 @@ std::string mangleStringIdentity(std::string name) {
|
|||
return name;
|
||||
}
|
||||
|
||||
void assertFilterOptimized(
|
||||
TRI_vocbase_t& vocbase,
|
||||
std::string const& queryString,
|
||||
irs::filter const& expectedFilter,
|
||||
arangodb::aql::ExpressionContext* exprCtx /*= nullptr*/,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> bindVars /* = nullptr */
|
||||
) {
|
||||
auto options = arangodb::velocypack::Parser::fromJson(
|
||||
// "{ \"tracing\" : 1 }"
|
||||
"{ }"
|
||||
);
|
||||
|
||||
arangodb::aql::Query query(
|
||||
false,
|
||||
vocbase,
|
||||
arangodb::aql::QueryString(queryString),
|
||||
bindVars,
|
||||
options,
|
||||
arangodb::aql::PART_MAIN
|
||||
);
|
||||
|
||||
query.prepare(arangodb::QueryRegistryFeature::registry());
|
||||
CHECK(query.plan());
|
||||
auto& plan = *query.plan();
|
||||
|
||||
arangodb::SmallVector<arangodb::aql::ExecutionNode*>::allocator_type::arena_type a;
|
||||
arangodb::SmallVector<arangodb::aql::ExecutionNode*> nodes{a};
|
||||
plan.findNodesOfType(nodes, arangodb::aql::ExecutionNode::ENUMERATE_IRESEARCH_VIEW, true);
|
||||
|
||||
CHECK(nodes.size() == 1);
|
||||
|
||||
auto* viewNode = arangodb::aql::ExecutionNode::castTo<arangodb::iresearch::IResearchViewNode*>(nodes.front());
|
||||
|
||||
CHECK(viewNode);
|
||||
|
||||
// execution time
|
||||
{
|
||||
arangodb::transaction ::Methods trx(
|
||||
arangodb::transaction::StandaloneContext::Create(vocbase),
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
arangodb::transaction::Options()
|
||||
);
|
||||
|
||||
irs::Or actualFilter;
|
||||
arangodb::iresearch::QueryContext const ctx{ &trx, &plan, plan.getAst(), exprCtx, &viewNode->outVariable() };
|
||||
CHECK(arangodb::iresearch::FilterFactory::filter(&actualFilter, ctx, viewNode->filterCondition()));
|
||||
CHECK(!actualFilter.empty());
|
||||
CHECK(expectedFilter == *actualFilter.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void assertExpressionFilter(
|
||||
std::string const& queryString,
|
||||
irs::boost::boost_t boost /*= irs::boost::no_boost()*/,
|
||||
|
|
|
@ -132,6 +132,14 @@ void assertFilterBoost(
|
|||
irs::filter const& actual
|
||||
);
|
||||
|
||||
void assertFilterOptimized(
|
||||
TRI_vocbase_t& vocbase,
|
||||
std::string const& queryString,
|
||||
irs::filter const& expectedFilter,
|
||||
arangodb::aql::ExpressionContext* exprCtx = nullptr,
|
||||
std::shared_ptr<arangodb::velocypack::Builder> bindVars = nullptr
|
||||
);
|
||||
|
||||
void assertFilter(
|
||||
bool parseOk,
|
||||
bool execOk,
|
||||
|
@ -169,4 +177,4 @@ void assertFilterParseFail(
|
|||
std::shared_ptr<arangodb::velocypack::Builder> bindVars = nullptr
|
||||
);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -834,6 +834,24 @@ function IResearchAqlTestSuite(args) {
|
|||
linksView.drop();
|
||||
entities.drop();
|
||||
links.drop();
|
||||
},
|
||||
|
||||
testAttributeInRangeOpenInterval : function () {
|
||||
var result = db._query("FOR doc IN UnitTestsView SEARCH IN_RANGE(doc.c, 1, 3, false, false) OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result.length, 4);
|
||||
result.forEach(function(res) {
|
||||
assertTrue(res.c > 1 && res.c < 3);
|
||||
});
|
||||
},
|
||||
|
||||
testAttributeInRangeClosedInterval : function () {
|
||||
var result = db._query("FOR doc IN UnitTestsView SEARCH IN_RANGE(doc.c, 1, 3, true, true) OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result.length, 12);
|
||||
result.forEach(function(res) {
|
||||
assertTrue(res.c >= 1 && res.c <= 3);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -857,6 +857,24 @@ function iResearchAqlTestSuite () {
|
|||
linksView.drop();
|
||||
entities.drop();
|
||||
links.drop();
|
||||
},
|
||||
|
||||
testAttributeInRangeOpenInterval : function () {
|
||||
var result = db._query("FOR doc IN UnitTestsView SEARCH IN_RANGE(doc.c, 1, 3, false, false) OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result.length, 4);
|
||||
result.forEach(function(res) {
|
||||
assertTrue(res.c > 1 && res.c < 3);
|
||||
});
|
||||
},
|
||||
|
||||
testAttributeInRangeClosedInterval : function () {
|
||||
var result = db._query("FOR doc IN UnitTestsView SEARCH IN_RANGE(doc.c, 1, 3, true, true) OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result.length, 12);
|
||||
result.forEach(function(res) {
|
||||
assertTrue(res.c >= 1 && res.c <= 3);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue