Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Optimizer Rule for ANY ==#22164

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
cpjulia wants to merge26 commits intodevel
base:devel
Choose a base branch
Loading
fromfeature/optimizer-rules-any
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
26 commits
Select commitHold shift + click to select a range
7538d9d
Started introduction of mapping from query with ANY == into query wit…
cpjuliaDec 5, 2025
9308dfc
Create struct AnySimplifier and function replaceAnyWithInRule; Regist…
knakatasfDec 8, 2025
f901712
Simplify the code for AnySiimplifier
knakatasfDec 8, 2025
cb3abcc
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 9, 2025
cd176f2
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 10, 2025
8bfce4c
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 10, 2025
def5066
Introduced optimization rule for filter queries with ANY== to be mapp…
cpjuliaDec 10, 2025
b371e29
Remove redundant/unused test functions from aql-optimizer-rule-replac…
knakatasfDec 11, 2025
372d86d
Remove unnecessary 'ANY' logic from OrSimplifier
knakatasfDec 11, 2025
90884d5
Revert the logic of OrSimplifier
knakatasfDec 11, 2025
badc23f
Simplified code, expanded tests
cpjuliaDec 11, 2025
0d9313b
Simplified code, expanded tests
cpjuliaDec 11, 2025
3f5ec4e
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 12, 2025
d7d278a
Removed duplicate in test
cpjuliaDec 12, 2025
c374459
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 12, 2025
30af6f3
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 15, 2025
01c52aa
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 17, 2025
38883b1
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 17, 2025
ff73ce4
Add implementation for rule that maps query with FILTER ... ANY == to…
cpjuliaDec 17, 2025
6c9fe55
Fixed compilation errors
cpjuliaDec 17, 2025
23f5cba
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 17, 2025
bc8afb1
Add more optimization in Condition.cpp for inner AND expressions afte…
cpjuliaDec 17, 2025
bf60c6c
Revert 'include' for OptimizerRules.cppp
knakatasfDec 17, 2025
a2cdf46
Fix optimizations
cpjuliaDec 19, 2025
9ae0457
Merge branch 'feature/optimizer-rules-any' of github.com:arangodb/ara…
cpjuliaDec 19, 2025
8289adb
Merge branch 'devel' of github.com:arangodb/arangodb into feature/opt…
cpjuliaDec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletionsarangod/Aql/AstNode.cpp
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,6 +31,7 @@
#include "Aql/Query.h"
#include "Aql/Scopes.h"
#include "Aql/types.h"
#include "Assertions/Assert.h"
#include "Basics/FloatingPoint.h"
#include "Basics/StringUtils.h"
#include "Basics/Utf8Helper.h"
Expand DownExpand Up@@ -1401,6 +1402,17 @@ bool AstNode::isTrue() const {
// ! false => true
return true;
}
} else if (type == NODE_TYPE_OPERATOR_BINARY_IN ||
type == NODE_TYPE_OPERATOR_BINARY_NIN) {
// Handle empty IN arrays: x.name NOT IN [] → true
if (numMembers() >= 2) {
TRI_ASSERT(numMembers() == 2);
AstNode const* rhs = getMember(1);
if (rhs != nullptr && rhs->type == NODE_TYPE_ARRAY &&
rhs->numMembers() == 0) {
return (type == NODE_TYPE_OPERATOR_BINARY_NIN);
}
}
}

return false;
Expand DownExpand Up@@ -1445,6 +1457,17 @@ bool AstNode::isFalse() const {
// ! true => false
return true;
}
} else if (type == NODE_TYPE_OPERATOR_BINARY_IN ||
type == NODE_TYPE_OPERATOR_BINARY_NIN) {
// Handle empty IN arrays: x.name IN [] → false
if (numMembers() >= 2) {
TRI_ASSERT(numMembers() == 2);
AstNode const* rhs = getMember(1);
if (rhs != nullptr && rhs->type == NODE_TYPE_ARRAY &&
rhs->numMembers() == 0) {
return (type == NODE_TYPE_OPERATOR_BINARY_IN);
}
}
}

return false;
Expand Down
255 changes: 250 additions & 5 deletionsarangod/Aql/Condition.cpp
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1467,14 +1467,137 @@ void Condition::optimize(ExecutionPlan* plan, bool multivalued) {
if (op->type == NODE_TYPE_OPERATOR_BINARY_IN) {
++inComparisons;
auto deduplicated = deduplicateInOperation(op);

// Optimize IN with single value to equality: x IN [5] → x == 5
if (deduplicated->numMembers() == 2) {
auto rhs = deduplicated->getMemberUnchecked(1);
if (rhs->type == NODE_TYPE_ARRAY && rhs->isConstant() &&
rhs->numMembers() == 1) {
auto lhs = deduplicated->getMemberUnchecked(0);
auto optimized = plan->getAst()->createNodeBinaryOperator(
NODE_TYPE_OPERATOR_BINARY_EQ, lhs, rhs->getMemberUnchecked(0));
andNode->changeMember(j, optimized);
--inComparisons;
continue;
}
}

andNode->changeMember(j, deduplicated);
}
}
andNumMembers = andNode->numMembers();

if (andNumMembers <= 1) {
// simple AND item with 0 or 1 members. nothing to do
++r;
// Check for false conditions in AND node
// If any condition is false, the entire AND is false
bool andIsFalse = false;
for (size_t j = 0; j < andNumMembers; ++j) {
auto op = andNode->getMemberUnchecked(j);
if (op->isFalse()) {
andIsFalse = true;
break;
}
}

if (andIsFalse) {
// Entire AND branch is impossible, remove from OR
_root->removeMemberUncheckedUnordered(r);
retry = true;
n = _root->numMembers();
continue;
}

// Remove true conditions from AND node (but keep if it's the only
// condition)
if (andNumMembers > 1) {
for (size_t j = andNumMembers; j > 0; --j) {
auto op = andNode->getMemberUnchecked(j - 1);

if (op->isTrue()) {
andNode->removeMemberUncheckedUnordered(j - 1);
--andNumMembers;
}
}
}

// Remove exact duplicate conditions (IN operations and comparisons)
for (size_t j = andNumMembers; j > 1; --j) {
auto op1 = andNode->getMemberUnchecked(j - 1);

// Only check deterministic operations to avoid side effects
if (!op1->isDeterministic()) {
continue;
}

for (size_t k = j - 1; k > 0; --k) {
auto op2 = andNode->getMemberUnchecked(k - 1);

if (!op2->isDeterministic()) {
continue;
}

// Check if conditions are structurally identical
if (op1->type == op2->type && op1->numMembers() == op2->numMembers()) {
bool isDuplicate = false;

// For IN operations, check if they're identical
if (op1->type == NODE_TYPE_OPERATOR_BINARY_IN &&
op1->numMembers() == 2 && op2->numMembers() == 2) {
auto lhs1 = op1->getMember(0);
auto rhs1 = op1->getMember(1);
auto lhs2 = op2->getMember(0);
auto rhs2 = op2->getMember(1);

// Compare LHS (attribute) and RHS (array)
if (compareAstNodes(lhs1, lhs2, false) == 0 &&
rhs1->type == NODE_TYPE_ARRAY &&
rhs2->type == NODE_TYPE_ARRAY &&
rhs1->numMembers() == rhs2->numMembers()) {
isDuplicate = true;
// Compare array values
for (size_t m = 0; m < rhs1->numMembers(); ++m) {
if (compareAstNodes(rhs1->getMember(m), rhs2->getMember(m),
true) != 0) {
isDuplicate = false;
break;
}
}
}
} else if (op1->isComparisonOperator() && op1->numMembers() == 2) {
// For comparison operators, check if both sides match
auto lhs1 = op1->getMember(0);
auto rhs1 = op1->getMember(1);
auto lhs2 = op2->getMember(0);
auto rhs2 = op2->getMember(1);

if (compareAstNodes(lhs1, lhs2, false) == 0 &&
compareAstNodes(rhs1, rhs2, true) == 0) {
isDuplicate = true;
}
}

if (isDuplicate) {
// Remove the duplicate (keep the first one)
andNode->removeMemberUncheckedUnordered(j - 1);
--andNumMembers;
break; // Found duplicate, move to next condition
}
}
}
}

// If AND node is now empty, remove it from OR
if (andNumMembers == 0) {
_root->removeMemberUncheckedUnordered(r);
retry = true;
n = _root->numMembers();
continue;
}

// Simplify AND node with single member: OR(AND(x)) → OR(x)
if (andNumMembers == 1) {
auto singleMember = andNode->getMemberUnchecked(0);
_root->changeMember(r, singleMember);
retry = true;
n = _root->numMembers();
continue;
}
Expand DownExpand Up@@ -1628,10 +1751,21 @@ void Condition::optimize(ExecutionPlan* plan, bool multivalued) {
// merge IN with IN on same attribute
TRI_ASSERT(rightNode->numMembers() == 2);

auto mergedArray = mergeInOperations(leftNode, rightNode);
auto merged = _ast->createNodeBinaryOperator(
NODE_TYPE_OPERATOR_BINARY_IN,
leftNode->getMemberUnchecked(0),
mergeInOperations(leftNode, rightNode));
leftNode->getMemberUnchecked(0), mergedArray);

// Optimize IN with single value to equality: x IN [5] → x == 5
if (mergedArray->type == NODE_TYPE_ARRAY &&
mergedArray->isConstant() &&
mergedArray->numMembers() == 1) {
auto lhs = leftNode->getMemberUnchecked(0);
merged = plan->getAst()->createNodeBinaryOperator(
NODE_TYPE_OPERATOR_BINARY_EQ, lhs,
mergedArray->getMemberUnchecked(0));
}

andNode->removeMemberUncheckedUnordered(rightPos);
andNode->changeMember(leftPos, merged);
goto restartThisOrItem;
Expand DownExpand Up@@ -1670,6 +1804,15 @@ void Condition::optimize(ExecutionPlan* plan, bool multivalued) {
// use the new array of values
leftNode->changeMember(1, inNode);

// Optimize IN with single value to equality: x IN [5] → x == 5
if (inNode->numMembers() == 1) {
auto lhs = leftNode->getMemberUnchecked(0);
auto optimized = plan->getAst()->createNodeBinaryOperator(
NODE_TYPE_OPERATOR_BINARY_EQ, lhs,
inNode->getMemberUnchecked(0));
andNode->changeMember(leftPos, optimized);
}

// remove the other operator
andNode->removeMemberUncheckedUnordered(rightPos);
goto restartThisOrItem;
Expand DownExpand Up@@ -1756,6 +1899,108 @@ void Condition::optimize(ExecutionPlan* plan, bool multivalued) {
// now recalculate the number and don't modify r!
n = _root->numMembers();
}

// After processing all AND branches, check for empty OR (impossible
// condition)
if (_root->numMembers() == 0) {
// OR with no branches is impossible (always false)
// This will be handled by isEmpty() check
return;
}

// Remove duplicate OR branches: OR(AND(x>5, y>3), AND(x>5, y>3)) →
// OR(AND(x>5, y>3))
n = _root->numMembers();
for (size_t i = n; i > 1; --i) {
auto branch1 = _root->getMemberUnchecked(i - 1);
if (branch1->type != NODE_TYPE_OPERATOR_NARY_AND) {
continue;
}

for (size_t j = i - 1; j > 0; --j) {
auto branch2 = _root->getMemberUnchecked(j - 1);
if (branch2->type != NODE_TYPE_OPERATOR_NARY_AND) {
continue;
}

// Check if branches are identical
if (branch1->numMembers() != branch2->numMembers()) {
continue;
}

// Compare all conditions in both branches
bool isDuplicate = true;
for (size_t k = 0; k < branch1->numMembers(); ++k) {
auto cond1 = branch1->getMemberUnchecked(k);
auto cond2 = branch2->getMemberUnchecked(k);

// Quick structural check
if (cond1->type != cond2->type ||
cond1->numMembers() != cond2->numMembers()) {
isDuplicate = false;
break;
}

// For deterministic conditions, do deeper comparison
if (cond1->isDeterministic() && cond2->isDeterministic()) {
if (cond1->type == NODE_TYPE_OPERATOR_BINARY_IN &&
cond1->numMembers() == 2 && cond2->numMembers() == 2) {
auto lhs1 = cond1->getMember(0);
auto rhs1 = cond1->getMember(1);
auto lhs2 = cond2->getMember(0);
auto rhs2 = cond2->getMember(1);

if (compareAstNodes(lhs1, lhs2, false) != 0 ||
rhs1->type != NODE_TYPE_ARRAY ||
rhs2->type != NODE_TYPE_ARRAY ||
rhs1->numMembers() != rhs2->numMembers()) {
isDuplicate = false;
break;
}

for (size_t m = 0; m < rhs1->numMembers(); ++m) {
if (compareAstNodes(rhs1->getMember(m), rhs2->getMember(m),
true) != 0) {
isDuplicate = false;
break;
}
}
if (!isDuplicate) break;
} else if (cond1->isComparisonOperator() &&
cond1->numMembers() == 2) {
auto lhs1 = cond1->getMember(0);
auto rhs1 = cond1->getMember(1);
auto lhs2 = cond2->getMember(0);
auto rhs2 = cond2->getMember(1);

if (compareAstNodes(lhs1, lhs2, false) != 0 ||
compareAstNodes(rhs1, rhs2, true) != 0) {
isDuplicate = false;
break;
}
} else {
// For other types, use string comparison as fallback
// This is less precise but catches obvious duplicates
if (cond1->toString() != cond2->toString()) {
isDuplicate = false;
break;
}
}
} else {
// Non-deterministic - skip comparison to avoid side effects
isDuplicate = false;
break;
}
}

if (isDuplicate) {
// Remove the duplicate branch (keep the first one)
_root->removeMemberUncheckedUnordered(i - 1);
--n;
break; // Found duplicate, move to next branch
}
}
}
}

/// @brief registers an attribute access for a particular (collection) variable
Expand Down
3 changes: 3 additions & 0 deletionsarangod/Aql/OptimizerRule.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,9 @@ struct OptimizerRule {
// replace simple OR conditions with IN
replaceOrWithInRule,

// replace ANY == conditions with IN
replaceAnyEqWithInRule,

// remove redundant OR conditions
removeRedundantOrRule,

Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp