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:
florijan 2017-04-04 09:04:26 +02:00
parent c09d237e05
commit bffc406662
3 changed files with 151 additions and 4 deletions

View File

@ -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

View File

@ -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

View File

@ -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));
}