Query::Plan - ExpansionUniquenessFilter
Reviewers: buda, mislav.bradac, teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D214
This commit is contained in:
parent
c09d237e05
commit
bffc406662
@ -741,8 +741,8 @@ RemoveLabels::RemoveLabelsCursor::RemoveLabelsCursor(RemoveLabels &self,
|
||||
GraphDbAccessor &db)
|
||||
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||
|
||||
bool RemoveLabels::RemoveLabelsCursor::Pull(
|
||||
Frame &frame, SymbolTable &symbol_table) {
|
||||
bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame,
|
||||
SymbolTable &symbol_table) {
|
||||
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||
|
||||
TypedValue &vertex_value = frame[self_.input_symbol_];
|
||||
@ -753,5 +753,58 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAccessor>
|
||||
ExpandUniquenessFilter<TAccessor>::ExpandUniquenessFilter(
|
||||
const std::shared_ptr<LogicalOperator> &input, Symbol expand_symbol,
|
||||
const std::vector<Symbol> &previous_symbols)
|
||||
: input_(input),
|
||||
expand_symbol_(expand_symbol),
|
||||
previous_symbols_(previous_symbols) {}
|
||||
|
||||
template <typename TAccessor>
|
||||
void ExpandUniquenessFilter<TAccessor>::Accept(LogicalOperatorVisitor &visitor) {
|
||||
visitor.Visit(*this);
|
||||
input_->Accept(visitor);
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
|
||||
template <typename TAccessor>
|
||||
std::unique_ptr<Cursor> ExpandUniquenessFilter<TAccessor>::MakeCursor(
|
||||
GraphDbAccessor &db) {
|
||||
return std::make_unique<ExpandUniquenessFilterCursor>(*this, db);
|
||||
}
|
||||
|
||||
template <typename TAccessor>
|
||||
ExpandUniquenessFilter<TAccessor>::ExpandUniquenessFilterCursor::
|
||||
ExpandUniquenessFilterCursor(ExpandUniquenessFilter &self,
|
||||
GraphDbAccessor &db)
|
||||
: self_(self), input_cursor_(self.input_->MakeCursor(db)) {}
|
||||
|
||||
template <typename TAccessor>
|
||||
bool ExpandUniquenessFilter<TAccessor>::ExpandUniquenessFilterCursor::Pull(
|
||||
Frame &frame, SymbolTable &symbol_table) {
|
||||
|
||||
auto expansion_ok = [&]() {
|
||||
TypedValue &expand_value = frame[self_.expand_symbol_];
|
||||
TAccessor &expand_accessor = expand_value.Value<TAccessor>();
|
||||
for (const auto &previous_symbol : self_.previous_symbols_) {
|
||||
TypedValue &previous_value = frame[previous_symbol];
|
||||
TAccessor &previous_accessor = previous_value.Value<TAccessor>();
|
||||
if (expand_accessor == previous_accessor) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
while (input_cursor_->Pull(frame, symbol_table))
|
||||
if (expansion_ok())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// instantiations of the ExpandUniquenessFilter template class
|
||||
// we only ever need these two
|
||||
template class ExpandUniquenessFilter<VertexAccessor>;
|
||||
template class ExpandUniquenessFilter<EdgeAccessor>;
|
||||
|
||||
} // namespace plan
|
||||
} // namespace query
|
||||
|
@ -52,12 +52,15 @@ class SetProperties;
|
||||
class SetLabels;
|
||||
class RemoveProperty;
|
||||
class RemoveLabels;
|
||||
template <typename TAccessor>
|
||||
class ExpandUniquenessFilter;
|
||||
|
||||
/** @brief Base class for visitors of @c LogicalOperator class hierarchy. */
|
||||
using LogicalOperatorVisitor =
|
||||
::utils::Visitor<CreateNode, CreateExpand, ScanAll, Expand, NodeFilter,
|
||||
EdgeFilter, Filter, Produce, Delete, SetProperty,
|
||||
SetProperties, SetLabels, RemoveProperty, RemoveLabels>;
|
||||
SetProperties, SetLabels, RemoveProperty, RemoveLabels,
|
||||
ExpandUniquenessFilter<VertexAccessor>, ExpandUniquenessFilter<EdgeAccessor>>;
|
||||
|
||||
/** @brief Base class for logical operators.
|
||||
*
|
||||
@ -680,5 +683,52 @@ class RemoveLabels : public LogicalOperator {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter whose Pull returns true only when the given
|
||||
* expand_symbol frame value (the latest expansion) is not
|
||||
* equal to any of the previous_symbols frame values.
|
||||
*
|
||||
* Used for implementing [iso|cypher]morphism.
|
||||
* Isomorphism is vertex-uniqueness. It means that
|
||||
* two different vertices in a pattern can not map to the
|
||||
* same data vertex. For example, if the database
|
||||
* contains one vertex with a recursive relationship,
|
||||
* then the query
|
||||
* MATCH ()-[]->() combined with vertex uniqueness
|
||||
* yields no results (no uniqueness yields one).
|
||||
* Cyphermorphism is edge-uniqueness (the above
|
||||
* explanation applies). By default Neo4j uses
|
||||
* Cyphermorphism (that's where the name stems from,
|
||||
* it is not a valid graph-theory term).
|
||||
*
|
||||
* Works for both Edge and Vertex uniqueness checks
|
||||
* (provide the accessor type as a template argument).
|
||||
*/
|
||||
template <typename TAccessor>
|
||||
class ExpandUniquenessFilter : public LogicalOperator {
|
||||
public:
|
||||
ExpandUniquenessFilter(const std::shared_ptr<LogicalOperator> &input,
|
||||
Symbol expand_symbol,
|
||||
const std::vector<Symbol> &previous_symbols);
|
||||
void Accept(LogicalOperatorVisitor &visitor) override;
|
||||
std::unique_ptr<Cursor> MakeCursor(GraphDbAccessor &db) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<LogicalOperator> input_;
|
||||
Symbol expand_symbol_;
|
||||
std::vector<Symbol> previous_symbols_;
|
||||
|
||||
class ExpandUniquenessFilterCursor : public Cursor {
|
||||
public:
|
||||
ExpandUniquenessFilterCursor(ExpandUniquenessFilter &self,
|
||||
GraphDbAccessor &db);
|
||||
bool Pull(Frame &frame, SymbolTable &symbol_table) override;
|
||||
|
||||
private:
|
||||
ExpandUniquenessFilter &self_;
|
||||
std::unique_ptr<Cursor> input_cursor_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace plan
|
||||
} // namespace query
|
||||
|
@ -13,9 +13,9 @@
|
||||
#include "communication/result_stream_faker.hpp"
|
||||
#include "dbms/dbms.hpp"
|
||||
#include "query/context.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
#include "query/frontend/interpret/interpret.hpp"
|
||||
#include "query/frontend/logical/planner.hpp"
|
||||
#include "query/exceptions.hpp"
|
||||
|
||||
#include "query_common.hpp"
|
||||
|
||||
@ -1188,3 +1188,47 @@ TEST(Interpreter, SetRemove) {
|
||||
EXPECT_FALSE(v.has_label(label1));
|
||||
EXPECT_FALSE(v.has_label(label2));
|
||||
}
|
||||
|
||||
TEST(Interpreter, ExpandUniquenessFilter) {
|
||||
Dbms dbms;
|
||||
auto dba = dbms.active();
|
||||
|
||||
// make a graph that has (v1)->(v2) and a recursive edge (v1)->(v1)
|
||||
auto v1 = dba->insert_vertex();
|
||||
auto v2 = dba->insert_vertex();
|
||||
auto edge_type = dba->edge_type("edge_type");
|
||||
dba->insert_edge(v1, v2, edge_type);
|
||||
dba->insert_edge(v1, v1, edge_type);
|
||||
dba->advance_command();
|
||||
|
||||
auto check_expand_results = [&](bool vertex_uniqueness,
|
||||
bool edge_uniqueness) {
|
||||
AstTreeStorage storage;
|
||||
SymbolTable symbol_table;
|
||||
|
||||
auto n1 = MakeScanAll(storage, symbol_table, "n1");
|
||||
auto r1_n2 = MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1",
|
||||
EdgeAtom::Direction::RIGHT, false, "n2", false);
|
||||
std::shared_ptr<LogicalOperator> last_op = r1_n2.op_;
|
||||
if (vertex_uniqueness)
|
||||
last_op = std::make_shared<ExpandUniquenessFilter<VertexAccessor>>(
|
||||
last_op, r1_n2.node_sym_, std::vector<Symbol>{n1.sym_});
|
||||
auto r2_n3 =
|
||||
MakeExpand(storage, symbol_table, last_op, r1_n2.node_sym_, "r2",
|
||||
EdgeAtom::Direction::RIGHT, false, "n3", false);
|
||||
last_op = r2_n3.op_;
|
||||
if (edge_uniqueness)
|
||||
last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>(
|
||||
last_op, r2_n3.edge_sym_, std::vector<Symbol>{r1_n2.edge_sym_});
|
||||
if (vertex_uniqueness)
|
||||
last_op = std::make_shared<ExpandUniquenessFilter<VertexAccessor>>(
|
||||
last_op, r2_n3.node_sym_,
|
||||
std::vector<Symbol>{n1.sym_, r1_n2.node_sym_});
|
||||
|
||||
return PullAll(last_op, *dba, symbol_table);
|
||||
};
|
||||
|
||||
EXPECT_EQ(2, check_expand_results(false, false));
|
||||
EXPECT_EQ(0, check_expand_results(true, false));
|
||||
EXPECT_EQ(1, check_expand_results(false, true));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user