Split MATCH ... WHERE filtering on AND
Summary: Test planner splits MATCH ... WHERE Remove distinction between FilterAnd and AndOperator Reviewers: florijan, mislav.bradac Reviewed By: mislav.bradac Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D814
This commit is contained in:
parent
0eceefb2d4
commit
4a602a445a
@ -192,27 +192,6 @@ class AndOperator : public BinaryOperator {
|
|||||||
using BinaryOperator::BinaryOperator;
|
using BinaryOperator::BinaryOperator;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is separate operator so that we can implement different short-circuiting
|
|
||||||
// semantics than regular AndOperator. At this point CypherMainVisitor shouldn't
|
|
||||||
// concern itself with this, and should constructor only AndOperator-s. This is
|
|
||||||
// used in query planner at the moment.
|
|
||||||
class FilterAndOperator : public BinaryOperator {
|
|
||||||
friend class AstTreeStorage;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DEFVISITABLE(TreeVisitor<TypedValue>);
|
|
||||||
bool Accept(HierarchicalTreeVisitor &visitor) override {
|
|
||||||
if (visitor.PreVisit(*this)) {
|
|
||||||
expression1_->Accept(visitor) && expression2_->Accept(visitor);
|
|
||||||
}
|
|
||||||
return visitor.PostVisit(*this);
|
|
||||||
}
|
|
||||||
CLONE_BINARY_EXPRESSION;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using BinaryOperator::BinaryOperator;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AdditionOperator : public BinaryOperator {
|
class AdditionOperator : public BinaryOperator {
|
||||||
friend class AstTreeStorage;
|
friend class AstTreeStorage;
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ class MapLiteral;
|
|||||||
class OrOperator;
|
class OrOperator;
|
||||||
class XorOperator;
|
class XorOperator;
|
||||||
class AndOperator;
|
class AndOperator;
|
||||||
class FilterAndOperator;
|
|
||||||
class NotOperator;
|
class NotOperator;
|
||||||
class AdditionOperator;
|
class AdditionOperator;
|
||||||
class SubtractionOperator;
|
class SubtractionOperator;
|
||||||
@ -61,16 +60,16 @@ class Unwind;
|
|||||||
class CreateIndex;
|
class CreateIndex;
|
||||||
|
|
||||||
using TreeCompositeVisitor = ::utils::CompositeVisitor<
|
using TreeCompositeVisitor = ::utils::CompositeVisitor<
|
||||||
Query, NamedExpression, OrOperator, XorOperator, AndOperator,
|
Query, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator,
|
||||||
FilterAndOperator, NotOperator, AdditionOperator, SubtractionOperator,
|
AdditionOperator, SubtractionOperator, MultiplicationOperator,
|
||||||
MultiplicationOperator, DivisionOperator, ModOperator, NotEqualOperator,
|
DivisionOperator, ModOperator, NotEqualOperator, EqualOperator,
|
||||||
EqualOperator, LessOperator, GreaterOperator, LessEqualOperator,
|
LessOperator, GreaterOperator, LessEqualOperator, GreaterEqualOperator,
|
||||||
GreaterEqualOperator, InListOperator, ListMapIndexingOperator,
|
InListOperator, ListMapIndexingOperator, ListSlicingOperator, IfOperator,
|
||||||
ListSlicingOperator, IfOperator, UnaryPlusOperator, UnaryMinusOperator,
|
UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, ListLiteral,
|
||||||
IsNullOperator, ListLiteral, MapLiteral, PropertyLookup, LabelsTest,
|
MapLiteral, PropertyLookup, LabelsTest, EdgeTypeTest, Aggregation, Function,
|
||||||
EdgeTypeTest, Aggregation, Function, All, Create, Match, Return, With,
|
All, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom,
|
||||||
Pattern, NodeAtom, EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty,
|
BreadthFirstAtom, Delete, Where, SetProperty, SetProperties, SetLabels,
|
||||||
SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind>;
|
RemoveProperty, RemoveLabels, Merge, Unwind>;
|
||||||
|
|
||||||
using TreeLeafVisitor = ::utils::LeafVisitor<Identifier, PrimitiveLiteral,
|
using TreeLeafVisitor = ::utils::LeafVisitor<Identifier, PrimitiveLiteral,
|
||||||
ParameterLookup, CreateIndex>;
|
ParameterLookup, CreateIndex>;
|
||||||
@ -78,24 +77,24 @@ using TreeLeafVisitor = ::utils::LeafVisitor<Identifier, PrimitiveLiteral,
|
|||||||
class HierarchicalTreeVisitor : public TreeCompositeVisitor,
|
class HierarchicalTreeVisitor : public TreeCompositeVisitor,
|
||||||
public TreeLeafVisitor {
|
public TreeLeafVisitor {
|
||||||
public:
|
public:
|
||||||
using TreeCompositeVisitor::PreVisit;
|
|
||||||
using TreeCompositeVisitor::PostVisit;
|
using TreeCompositeVisitor::PostVisit;
|
||||||
using typename TreeLeafVisitor::ReturnType;
|
using TreeCompositeVisitor::PreVisit;
|
||||||
using TreeLeafVisitor::Visit;
|
using TreeLeafVisitor::Visit;
|
||||||
|
using typename TreeLeafVisitor::ReturnType;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TResult>
|
template <typename TResult>
|
||||||
using TreeVisitor = ::utils::Visitor<
|
using TreeVisitor = ::utils::Visitor<
|
||||||
TResult, Query, NamedExpression, OrOperator, XorOperator, AndOperator,
|
TResult, Query, NamedExpression, OrOperator, XorOperator, AndOperator,
|
||||||
FilterAndOperator, NotOperator, AdditionOperator, SubtractionOperator,
|
NotOperator, AdditionOperator, SubtractionOperator, MultiplicationOperator,
|
||||||
MultiplicationOperator, DivisionOperator, ModOperator, NotEqualOperator,
|
DivisionOperator, ModOperator, NotEqualOperator, EqualOperator,
|
||||||
EqualOperator, LessOperator, GreaterOperator, LessEqualOperator,
|
LessOperator, GreaterOperator, LessEqualOperator, GreaterEqualOperator,
|
||||||
GreaterEqualOperator, InListOperator, ListMapIndexingOperator,
|
InListOperator, ListMapIndexingOperator, ListSlicingOperator, IfOperator,
|
||||||
ListSlicingOperator, IfOperator, UnaryPlusOperator, UnaryMinusOperator,
|
UnaryPlusOperator, UnaryMinusOperator, IsNullOperator, ListLiteral,
|
||||||
IsNullOperator, ListLiteral, MapLiteral, PropertyLookup, LabelsTest,
|
MapLiteral, PropertyLookup, LabelsTest, EdgeTypeTest, Aggregation, Function,
|
||||||
EdgeTypeTest, Aggregation, Function, All, ParameterLookup, Create, Match,
|
All, ParameterLookup, Create, Match, Return, With, Pattern, NodeAtom,
|
||||||
Return, With, Pattern, NodeAtom, EdgeAtom, BreadthFirstAtom, Delete, Where,
|
EdgeAtom, BreadthFirstAtom, Delete, Where, SetProperty, SetProperties,
|
||||||
SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge,
|
SetLabels, RemoveProperty, RemoveLabels, Merge, Unwind, Identifier,
|
||||||
Unwind, Identifier, PrimitiveLiteral, CreateIndex>;
|
PrimitiveLiteral, CreateIndex>;
|
||||||
|
|
||||||
} // namespace query
|
} // namespace query
|
||||||
|
@ -96,7 +96,6 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
|
|||||||
|
|
||||||
BINARY_OPERATOR_VISITOR(OrOperator, ||, OR);
|
BINARY_OPERATOR_VISITOR(OrOperator, ||, OR);
|
||||||
BINARY_OPERATOR_VISITOR(XorOperator, ^, XOR);
|
BINARY_OPERATOR_VISITOR(XorOperator, ^, XOR);
|
||||||
BINARY_OPERATOR_VISITOR(AndOperator, &&, AND);
|
|
||||||
BINARY_OPERATOR_VISITOR(AdditionOperator, +, +);
|
BINARY_OPERATOR_VISITOR(AdditionOperator, +, +);
|
||||||
BINARY_OPERATOR_VISITOR(SubtractionOperator, -, -);
|
BINARY_OPERATOR_VISITOR(SubtractionOperator, -, -);
|
||||||
BINARY_OPERATOR_VISITOR(MultiplicationOperator, *, *);
|
BINARY_OPERATOR_VISITOR(MultiplicationOperator, *, *);
|
||||||
@ -116,7 +115,7 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
|
|||||||
#undef BINARY_OPERATOR_VISITOR
|
#undef BINARY_OPERATOR_VISITOR
|
||||||
#undef UNARY_OPERATOR_VISITOR
|
#undef UNARY_OPERATOR_VISITOR
|
||||||
|
|
||||||
TypedValue Visit(FilterAndOperator &op) override {
|
TypedValue Visit(AndOperator &op) override {
|
||||||
auto value1 = op.expression1_->Accept(*this);
|
auto value1 = op.expression1_->Accept(*this);
|
||||||
if (value1.IsNull() || !value1.Value<bool>()) {
|
if (value1.IsNull() || !value1.Value<bool>()) {
|
||||||
// If first expression is null or false, don't execute the second one.
|
// If first expression is null or false, don't execute the second one.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <stack>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "utils/algorithm.hpp"
|
#include "utils/algorithm.hpp"
|
||||||
@ -331,7 +332,6 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
|
|||||||
VISIT_BINARY_OPERATOR(OrOperator)
|
VISIT_BINARY_OPERATOR(OrOperator)
|
||||||
VISIT_BINARY_OPERATOR(XorOperator)
|
VISIT_BINARY_OPERATOR(XorOperator)
|
||||||
VISIT_BINARY_OPERATOR(AndOperator)
|
VISIT_BINARY_OPERATOR(AndOperator)
|
||||||
VISIT_BINARY_OPERATOR(FilterAndOperator)
|
|
||||||
VISIT_BINARY_OPERATOR(AdditionOperator)
|
VISIT_BINARY_OPERATOR(AdditionOperator)
|
||||||
VISIT_BINARY_OPERATOR(SubtractionOperator)
|
VISIT_BINARY_OPERATOR(SubtractionOperator)
|
||||||
VISIT_BINARY_OPERATOR(MultiplicationOperator)
|
VISIT_BINARY_OPERATOR(MultiplicationOperator)
|
||||||
@ -608,7 +608,7 @@ void AddMatching(const Match &match, SymbolTable &symbol_table,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Iterates over `all_filters` joining them in one expression via
|
// Iterates over `all_filters` joining them in one expression via
|
||||||
// `FilterAndOperator`. Filters which use unbound symbols are skipped, as well
|
// `AndOperator`. Filters which use unbound symbols are skipped, as well
|
||||||
// as those that fail the `predicate` function. The function takes a single
|
// as those that fail the `predicate` function. The function takes a single
|
||||||
// argument, `FilterInfo`. All the joined filters are removed from
|
// argument, `FilterInfo`. All the joined filters are removed from
|
||||||
// `all_filters`.
|
// `all_filters`.
|
||||||
@ -622,8 +622,8 @@ Expression *ExtractFilters(const std::unordered_set<Symbol> &bound_symbols,
|
|||||||
filters_it != all_filters.end();) {
|
filters_it != all_filters.end();) {
|
||||||
if (HasBoundFilterSymbols(bound_symbols, *filters_it) &&
|
if (HasBoundFilterSymbols(bound_symbols, *filters_it) &&
|
||||||
predicate(*filters_it)) {
|
predicate(*filters_it)) {
|
||||||
filter_expr = impl::BoolJoin<FilterAndOperator>(storage, filter_expr,
|
filter_expr = impl::BoolJoin<AndOperator>(storage, filter_expr,
|
||||||
filters_it->expression);
|
filters_it->expression);
|
||||||
filters_it = all_filters.erase(filters_it);
|
filters_it = all_filters.erase(filters_it);
|
||||||
} else {
|
} else {
|
||||||
filters_it++;
|
filters_it++;
|
||||||
@ -631,6 +631,24 @@ Expression *ExtractFilters(const std::unordered_set<Symbol> &bound_symbols,
|
|||||||
}
|
}
|
||||||
return filter_expr;
|
return filter_expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto SplitExpressionOnAnd(Expression *expression) {
|
||||||
|
std::vector<Expression *> expressions;
|
||||||
|
std::stack<Expression *> pending_expressions;
|
||||||
|
pending_expressions.push(expression);
|
||||||
|
while (!pending_expressions.empty()) {
|
||||||
|
auto *current_expression = pending_expressions.top();
|
||||||
|
pending_expressions.pop();
|
||||||
|
if (auto *and_op = dynamic_cast<AndOperator *>(current_expression)) {
|
||||||
|
pending_expressions.push(and_op->expression1_);
|
||||||
|
pending_expressions.push(and_op->expression2_);
|
||||||
|
} else {
|
||||||
|
expressions.push_back(current_expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expressions;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
@ -995,10 +1013,13 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
|
|||||||
// information for potential property and label indexing.
|
// information for potential property and label indexing.
|
||||||
void Filters::CollectWhereFilter(Where &where,
|
void Filters::CollectWhereFilter(Where &where,
|
||||||
const SymbolTable &symbol_table) {
|
const SymbolTable &symbol_table) {
|
||||||
UsedSymbolsCollector collector(symbol_table);
|
auto where_filters = SplitExpressionOnAnd(where.expression_);
|
||||||
where.expression_->Accept(collector);
|
for (const auto &filter : where_filters) {
|
||||||
all_filters_.emplace_back(FilterInfo{where.expression_, collector.symbols_});
|
UsedSymbolsCollector collector(symbol_table);
|
||||||
AnalyzeFilter(where.expression_, symbol_table);
|
filter->Accept(collector);
|
||||||
|
all_filters_.emplace_back(FilterInfo{filter, collector.symbols_});
|
||||||
|
AnalyzeFilter(filter, symbol_table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a Query to multiple QueryParts. In the process new Ast nodes may be
|
// Converts a Query to multiple QueryParts. In the process new Ast nodes may be
|
||||||
|
@ -548,9 +548,10 @@ class RuleBasedPlanner {
|
|||||||
const auto &next_node_symbol =
|
const auto &next_node_symbol =
|
||||||
symbol_table.at(*bf_atom->next_node_identifier_);
|
symbol_table.at(*bf_atom->next_node_identifier_);
|
||||||
// Inline BFS edge filtering together with its filter expression.
|
// Inline BFS edge filtering together with its filter expression.
|
||||||
auto *filter_expr = impl::BoolJoin<FilterAndOperator>(
|
auto *filter_expr = impl::BoolJoin<AndOperator>(
|
||||||
storage, impl::ExtractMultiExpandFilter(
|
storage,
|
||||||
bound_symbols, node_symbol, all_filters, storage),
|
impl::ExtractMultiExpandFilter(bound_symbols, node_symbol,
|
||||||
|
all_filters, storage),
|
||||||
bf_atom->filter_expression_);
|
bf_atom->filter_expression_);
|
||||||
last_op = new ExpandBreadthFirst(
|
last_op = new ExpandBreadthFirst(
|
||||||
node_symbol, edge_symbol, expansion.direction, edge_type,
|
node_symbol, edge_symbol, expansion.direction, edge_type,
|
||||||
|
@ -101,25 +101,18 @@ TEST(ExpressionEvaluator, AndOperator) {
|
|||||||
ASSERT_EQ(val2.Value<bool>(), false);
|
ASSERT_EQ(val2.Value<bool>(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ExpressionEvaluator, FilterAndOperator) {
|
TEST(ExpressionEvaluator, AndOperatorShortCircuit) {
|
||||||
AstTreeStorage storage;
|
AstTreeStorage storage;
|
||||||
NoContextExpressionEvaluator eval;
|
NoContextExpressionEvaluator eval;
|
||||||
{
|
{
|
||||||
auto *op = storage.Create<FilterAndOperator>(
|
auto *op =
|
||||||
storage.Create<PrimitiveLiteral>(true),
|
storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false),
|
||||||
storage.Create<PrimitiveLiteral>(true));
|
storage.Create<PrimitiveLiteral>(5));
|
||||||
auto value = op->Accept(eval.eval);
|
|
||||||
EXPECT_EQ(value.Value<bool>(), true);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto *op = storage.Create<FilterAndOperator>(
|
|
||||||
storage.Create<PrimitiveLiteral>(false),
|
|
||||||
storage.Create<PrimitiveLiteral>(5));
|
|
||||||
auto value = op->Accept(eval.eval);
|
auto value = op->Accept(eval.eval);
|
||||||
EXPECT_EQ(value.Value<bool>(), false);
|
EXPECT_EQ(value.Value<bool>(), false);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto *op = storage.Create<FilterAndOperator>(
|
auto *op = storage.Create<AndOperator>(
|
||||||
storage.Create<PrimitiveLiteral>(TypedValue::Null),
|
storage.Create<PrimitiveLiteral>(TypedValue::Null),
|
||||||
storage.Create<PrimitiveLiteral>(5));
|
storage.Create<PrimitiveLiteral>(5));
|
||||||
auto value = op->Accept(eval.eval);
|
auto value = op->Accept(eval.eval);
|
||||||
|
@ -1399,4 +1399,18 @@ TEST(TestLogicalPlanner, MatchScanToExpand) {
|
|||||||
ExpectFilter(), ExpectProduce());
|
ExpectFilter(), ExpectProduce());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(TestLogicalPlanner, MatchWhereAndSplit) {
|
||||||
|
// Test MATCH (n) -[r]- (m) WHERE n.prop AND r.prop RETURN m
|
||||||
|
Dbms dbms;
|
||||||
|
auto dba = dbms.active();
|
||||||
|
auto prop = PROPERTY_PAIR("prop");
|
||||||
|
AstTreeStorage storage;
|
||||||
|
QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
|
||||||
|
WHERE(AND(PROPERTY_LOOKUP("n", prop), PROPERTY_LOOKUP("r", prop))),
|
||||||
|
RETURN("m"));
|
||||||
|
// We expect `n.prop` filter right after scanning `n`.
|
||||||
|
CheckPlan(storage, ExpectScanAll(), ExpectFilter(), ExpectExpand(),
|
||||||
|
ExpectFilter(), ExpectProduce());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user