Query::Plan::ScanAll accepts optional input (for cartesian)
Reviewers: buda, mislav.bradac, teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D219
This commit is contained in:
parent
bffc406662
commit
9eb4428023
@ -148,17 +148,46 @@ void CreateExpand::CreateExpandCursor::CreateEdge(
|
||||
frame[symbol_table[*self_.edge_atom_->identifier_]] = edge;
|
||||
}
|
||||
|
||||
ScanAll::ScanAll(NodeAtom *node_atom) : node_atom_(node_atom) {}
|
||||
ScanAll::ScanAll(NodeAtom *node_atom)
|
||||
: node_atom_(node_atom), input_(nullptr) {}
|
||||
|
||||
ScanAll::ScanAll(NodeAtom *node_atom, std::shared_ptr<LogicalOperator> input)
|
||||
: node_atom_(node_atom), input_(input) {}
|
||||
|
||||
void ScanAll::Accept(LogicalOperatorVisitor &visitor) {
|
||||
visitor.Visit(*this);
|
||||
if (input_) input_->Accept(visitor);
|
||||
visitor.PostVisit(*this);
|
||||
}
|
||||
|
||||
std::unique_ptr<Cursor> ScanAll::MakeCursor(GraphDbAccessor &db) {
|
||||
return std::make_unique<ScanAllCursor>(*this, db);
|
||||
}
|
||||
|
||||
ScanAll::ScanAllCursor::ScanAllCursor(ScanAll &self, GraphDbAccessor &db)
|
||||
: self_(self), vertices_(db.vertices()), vertices_it_(vertices_.begin()) {}
|
||||
: self_(self),
|
||||
input_cursor_(self.input_ ? self.input_->MakeCursor(db) : nullptr),
|
||||
vertices_(db.vertices()),
|
||||
vertices_it_(vertices_.begin()) {}
|
||||
|
||||
bool ScanAll::ScanAllCursor::Pull(Frame &frame, SymbolTable &symbol_table) {
|
||||
if (vertices_it_ == vertices_.end()) return false;
|
||||
if (input_cursor_) {
|
||||
// using an input. we need to pull from it if we are in the first pull
|
||||
// of this cursor, or if we have exhausted vertices_it_
|
||||
if (first_pull_ || vertices_it_ == vertices_.end()) {
|
||||
first_pull_ = false;
|
||||
// if the input is empty, we are for sure done
|
||||
if (!input_cursor_->Pull(frame, symbol_table)) return false;
|
||||
vertices_it_ = vertices_.begin();
|
||||
}
|
||||
}
|
||||
|
||||
// if we have no more vertices, we're done (if input_ is set we have
|
||||
// just tried to re-init vertices_it_, and if not we only iterate
|
||||
// through it once
|
||||
if (vertices_it_ == vertices_.end())
|
||||
return false;
|
||||
|
||||
frame[symbol_table[*self_.node_atom_->identifier_]] = *vertices_it_++;
|
||||
return true;
|
||||
}
|
||||
|
@ -204,15 +204,22 @@ class CreateExpand : public LogicalOperator {
|
||||
|
||||
/**
|
||||
* @brief Operator which iterates over all the nodes currently in the database.
|
||||
* When given an input (optional), does a cartesian product.
|
||||
*
|
||||
* It accepts an optional input. If provided then this op scans all the nodes
|
||||
* currently in the database for each successful Pull from it's input, thereby
|
||||
* producing a cartesian product of input Pulls and database elements.
|
||||
*/
|
||||
class ScanAll : public LogicalOperator {
|
||||
public:
|
||||
ScanAll(NodeAtom *node_atom);
|
||||
DEFVISITABLE(LogicalOperatorVisitor);
|
||||
ScanAll(NodeAtom *node_atom, std::shared_ptr<LogicalOperator> input);
|
||||
void Accept(LogicalOperatorVisitor &visitor) override;
|
||||
std::unique_ptr<Cursor> MakeCursor(GraphDbAccessor &db) override;
|
||||
|
||||
private:
|
||||
NodeAtom *node_atom_ = nullptr;
|
||||
std::shared_ptr<LogicalOperator> input_;
|
||||
|
||||
class ScanAllCursor : public Cursor {
|
||||
public:
|
||||
@ -221,8 +228,11 @@ class ScanAll : public LogicalOperator {
|
||||
|
||||
private:
|
||||
ScanAll &self_;
|
||||
std::unique_ptr<Cursor> input_cursor_;
|
||||
decltype(std::declval<GraphDbAccessor>().vertices()) vertices_;
|
||||
decltype(vertices_.begin()) vertices_it_;
|
||||
// if this is the first pull from this cursor
|
||||
bool first_pull_{true};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -92,9 +92,10 @@ struct ScanAllTuple {
|
||||
* Returns (node_atom, scan_all_logical_op, symbol).
|
||||
*/
|
||||
ScanAllTuple MakeScanAll(AstTreeStorage &storage, SymbolTable &symbol_table,
|
||||
const std::string &identifier) {
|
||||
const std::string &identifier,
|
||||
std::shared_ptr<LogicalOperator> input = {nullptr}) {
|
||||
auto node = NODE(identifier);
|
||||
auto logical_op = std::make_shared<ScanAll>(node);
|
||||
auto logical_op = std::make_shared<ScanAll>(node, input);
|
||||
auto symbol = symbol_table.CreateSymbol(identifier);
|
||||
symbol_table[*node->identifier_] = symbol;
|
||||
// return std::make_tuple(node, logical_op, symbol);
|
||||
@ -159,6 +160,38 @@ TEST(Interpreter, MatchReturn) {
|
||||
EXPECT_EQ(result.GetResults().size(), 2);
|
||||
}
|
||||
|
||||
TEST(Interpreter, MatchReturnCartesian) {
|
||||
Dbms dbms;
|
||||
auto dba = dbms.active();
|
||||
|
||||
dba->insert_vertex().add_label(dba->label("l1"));
|
||||
dba->insert_vertex().add_label(dba->label("l2"));
|
||||
dba->advance_command();
|
||||
|
||||
AstTreeStorage storage;
|
||||
SymbolTable symbol_table;
|
||||
|
||||
auto n = MakeScanAll(storage, symbol_table, "n");
|
||||
auto m = MakeScanAll(storage, symbol_table, "m", n.op_);
|
||||
auto return_n = NEXPR("n", IDENT("n"));
|
||||
symbol_table[*return_n->expression_] = n.sym_;
|
||||
symbol_table[*return_n] = symbol_table.CreateSymbol("named_expression_1");
|
||||
auto return_m = NEXPR("m", IDENT("m"));
|
||||
symbol_table[*return_m->expression_] = m.sym_;
|
||||
symbol_table[*return_m] = symbol_table.CreateSymbol("named_expression_2");
|
||||
auto produce = MakeProduce(m.op_, return_n, return_m);
|
||||
|
||||
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
||||
auto result_data = result.GetResults();
|
||||
EXPECT_EQ(result_data.size(), 4);
|
||||
// ensure the result ordering is OK:
|
||||
// "n" from the results is the same for the first two rows, while "m" isn't
|
||||
EXPECT_EQ(result_data[0][0].Value<VertexAccessor>(),
|
||||
result_data[1][0].Value<VertexAccessor>());
|
||||
EXPECT_NE(result_data[0][1].Value<VertexAccessor>(),
|
||||
result_data[1][1].Value<VertexAccessor>());
|
||||
}
|
||||
|
||||
TEST(Interpreter, StandaloneReturn) {
|
||||
Dbms dbms;
|
||||
auto dba = dbms.active();
|
||||
|
Loading…
Reference in New Issue
Block a user