2017-03-15 22:49:19 +08:00
|
|
|
//
|
|
|
|
// Copyright 2017 Memgraph
|
|
|
|
// Created by Florijan Stamenkovic on 14.03.17.
|
|
|
|
//
|
|
|
|
|
2017-03-22 20:40:50 +08:00
|
|
|
#include <iterator>
|
2017-03-15 22:49:19 +08:00
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
|
2017-03-21 18:25:44 +08:00
|
|
|
#include "gmock/gmock.h"
|
2017-03-15 22:49:19 +08:00
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
#include "communication/result_stream_faker.hpp"
|
|
|
|
#include "dbms/dbms.hpp"
|
2017-03-22 23:38:43 +08:00
|
|
|
#include "query/context.hpp"
|
|
|
|
#include "query/frontend/interpret/interpret.hpp"
|
|
|
|
#include "query/frontend/logical/planner.hpp"
|
2017-03-15 22:49:19 +08:00
|
|
|
|
2017-03-24 23:50:42 +08:00
|
|
|
#include "query_common.hpp"
|
|
|
|
|
2017-03-15 22:49:19 +08:00
|
|
|
using namespace query;
|
2017-03-27 19:09:14 +08:00
|
|
|
using namespace query::plan;
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function that collects all the results from the given
|
|
|
|
* Produce into a ResultStreamFaker and returns that object.
|
|
|
|
*
|
|
|
|
* @param produce
|
|
|
|
* @param symbol_table
|
|
|
|
* @param db_accessor
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
auto CollectProduce(std::shared_ptr<Produce> produce, SymbolTable &symbol_table,
|
|
|
|
GraphDbAccessor &db_accessor) {
|
|
|
|
ResultStreamFaker stream;
|
|
|
|
Frame frame(symbol_table.max_position());
|
|
|
|
|
|
|
|
// top level node in the operator tree is a produce (return)
|
|
|
|
// so stream out results
|
|
|
|
|
|
|
|
// generate header
|
|
|
|
std::vector<std::string> header;
|
|
|
|
for (auto named_expression : produce->named_expressions())
|
|
|
|
header.push_back(named_expression->name_);
|
|
|
|
stream.Header(header);
|
|
|
|
|
|
|
|
// collect the symbols from the return clause
|
|
|
|
std::vector<Symbol> symbols;
|
|
|
|
for (auto named_expression : produce->named_expressions())
|
|
|
|
symbols.emplace_back(symbol_table[*named_expression]);
|
|
|
|
|
|
|
|
// stream out results
|
|
|
|
auto cursor = produce->MakeCursor(db_accessor);
|
|
|
|
while (cursor->Pull(frame, symbol_table)) {
|
|
|
|
std::vector<TypedValue> values;
|
2017-03-21 18:25:44 +08:00
|
|
|
for (auto &symbol : symbols) values.emplace_back(frame[symbol]);
|
2017-03-15 22:49:19 +08:00
|
|
|
stream.Result(values);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream.Summary({{std::string("type"), TypedValue("r")}});
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
2017-03-29 14:50:55 +08:00
|
|
|
int PullAll(std::shared_ptr<LogicalOperator> logical_op, GraphDbAccessor &db,
|
|
|
|
SymbolTable symbol_table) {
|
2017-03-16 16:28:16 +08:00
|
|
|
Frame frame(symbol_table.max_position());
|
2017-03-27 19:09:14 +08:00
|
|
|
auto cursor = logical_op->MakeCursor(db);
|
2017-03-29 14:50:55 +08:00
|
|
|
int count = 0;
|
|
|
|
while (cursor->Pull(frame, symbol_table)) count++;
|
|
|
|
return count;
|
2017-03-15 22:49:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename... TNamedExpressions>
|
|
|
|
auto MakeProduce(std::shared_ptr<LogicalOperator> input,
|
|
|
|
TNamedExpressions... named_expressions) {
|
|
|
|
return std::make_shared<Produce>(
|
2017-03-16 22:00:34 +08:00
|
|
|
input, std::vector<NamedExpression *>{named_expressions...});
|
2017-03-15 22:49:19 +08:00
|
|
|
}
|
|
|
|
|
2017-03-28 21:43:58 +08:00
|
|
|
struct ScanAllTuple {
|
|
|
|
NodeAtom *node_;
|
|
|
|
std::shared_ptr<LogicalOperator> op_;
|
|
|
|
Symbol sym_;
|
|
|
|
};
|
|
|
|
|
2017-03-21 18:25:44 +08:00
|
|
|
/**
|
|
|
|
* Creates and returns a tuple of stuff for a scan-all starting
|
|
|
|
* from the node with the given name.
|
|
|
|
*
|
|
|
|
* Returns (node_atom, scan_all_logical_op, symbol).
|
|
|
|
*/
|
2017-03-28 21:43:58 +08:00
|
|
|
ScanAllTuple MakeScanAll(AstTreeStorage &storage, SymbolTable &symbol_table,
|
|
|
|
const std::string &identifier) {
|
2017-03-24 23:50:42 +08:00
|
|
|
auto node = NODE(identifier);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto logical_op = std::make_shared<ScanAll>(node);
|
|
|
|
auto symbol = symbol_table.CreateSymbol(identifier);
|
|
|
|
symbol_table[*node->identifier_] = symbol;
|
2017-03-28 21:43:58 +08:00
|
|
|
// return std::make_tuple(node, logical_op, symbol);
|
|
|
|
return ScanAllTuple{node, logical_op, symbol};
|
2017-03-21 18:25:44 +08:00
|
|
|
}
|
|
|
|
|
2017-03-28 21:43:58 +08:00
|
|
|
struct ExpandTuple {
|
|
|
|
EdgeAtom *edge_;
|
|
|
|
Symbol edge_sym_;
|
|
|
|
NodeAtom *node_;
|
|
|
|
Symbol node_sym_;
|
|
|
|
std::shared_ptr<LogicalOperator> op_;
|
|
|
|
};
|
|
|
|
|
|
|
|
ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table,
|
|
|
|
std::shared_ptr<LogicalOperator> input,
|
|
|
|
Symbol input_symbol, const std::string &edge_identifier,
|
|
|
|
EdgeAtom::Direction direction, bool edge_cycle,
|
|
|
|
const std::string &node_identifier, bool node_cycle) {
|
2017-03-24 23:50:42 +08:00
|
|
|
auto edge = EDGE(edge_identifier, direction);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto edge_sym = symbol_table.CreateSymbol(edge_identifier);
|
|
|
|
symbol_table[*edge->identifier_] = edge_sym;
|
|
|
|
|
2017-03-24 23:50:42 +08:00
|
|
|
auto node = NODE(node_identifier);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto node_sym = symbol_table.CreateSymbol(node_identifier);
|
|
|
|
symbol_table[*node->identifier_] = node_sym;
|
|
|
|
|
|
|
|
auto op = std::make_shared<Expand>(node, edge, input, input_symbol,
|
|
|
|
node_cycle, edge_cycle);
|
|
|
|
|
2017-03-28 21:43:58 +08:00
|
|
|
return ExpandTuple{edge, edge_sym, node, node_sym, op};
|
2017-03-21 18:25:44 +08:00
|
|
|
}
|
|
|
|
|
2017-03-22 20:40:50 +08:00
|
|
|
template <typename TIterable>
|
|
|
|
auto CountIterable(TIterable iterable) {
|
|
|
|
return std::distance(iterable.begin(), iterable.end());
|
|
|
|
}
|
|
|
|
|
2017-03-15 22:49:19 +08:00
|
|
|
/*
|
|
|
|
* Actual tests start here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
TEST(Interpreter, MatchReturn) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add a few nodes to the database
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-15 22:49:19 +08:00
|
|
|
|
2017-03-16 22:00:34 +08:00
|
|
|
AstTreeStorage storage;
|
2017-03-21 18:25:44 +08:00
|
|
|
SymbolTable symbol_table;
|
2017-03-15 22:49:19 +08:00
|
|
|
|
2017-03-21 18:25:44 +08:00
|
|
|
auto scan_all = MakeScanAll(storage, symbol_table, "n");
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("n", IDENT("n"));
|
2017-03-28 21:43:58 +08:00
|
|
|
auto produce = MakeProduce(scan_all.op_, output);
|
|
|
|
symbol_table[*output->expression_] = scan_all.sym_;
|
2017-03-15 22:49:19 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), 2);
|
|
|
|
}
|
|
|
|
|
2017-03-27 21:31:58 +08:00
|
|
|
TEST(Interpreter, StandaloneReturn) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add a few nodes to the database
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto output = NEXPR("n", LITERAL(42));
|
|
|
|
auto produce = MakeProduce(std::shared_ptr<LogicalOperator>(nullptr), output);
|
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), 1);
|
|
|
|
EXPECT_EQ(result.GetResults()[0].size(), 1);
|
|
|
|
EXPECT_EQ(result.GetResults()[0][0].Value<int64_t>(), 42);
|
|
|
|
}
|
|
|
|
|
2017-03-15 22:49:19 +08:00
|
|
|
TEST(Interpreter, NodeFilterLabelsAndProperties) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add a few nodes to the database
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Label label = dba->label("Label");
|
|
|
|
GraphDbTypes::Property property = dba->property("Property");
|
2017-03-15 22:49:19 +08:00
|
|
|
auto v1 = dba->insert_vertex();
|
|
|
|
auto v2 = dba->insert_vertex();
|
|
|
|
auto v3 = dba->insert_vertex();
|
|
|
|
auto v4 = dba->insert_vertex();
|
|
|
|
auto v5 = dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
2017-03-16 16:28:16 +08:00
|
|
|
// test all combination of (label | no_label) * (no_prop | wrong_prop |
|
|
|
|
// right_prop)
|
2017-03-15 22:49:19 +08:00
|
|
|
// only v1 will have the right labels
|
|
|
|
v1.add_label(label);
|
|
|
|
v2.add_label(label);
|
|
|
|
v3.add_label(label);
|
|
|
|
v1.PropsSet(property, 42);
|
|
|
|
v2.PropsSet(property, 1);
|
|
|
|
v4.PropsSet(property, 42);
|
|
|
|
v5.PropsSet(property, 1);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-15 22:49:19 +08:00
|
|
|
|
2017-03-16 22:00:34 +08:00
|
|
|
AstTreeStorage storage;
|
2017-03-21 18:25:44 +08:00
|
|
|
SymbolTable symbol_table;
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
// make a scan all
|
2017-03-21 18:25:44 +08:00
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
n.node_->labels_.emplace_back(label);
|
|
|
|
n.node_->properties_[property] = LITERAL(42);
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
// node filtering
|
2017-03-28 21:43:58 +08:00
|
|
|
auto node_filter = std::make_shared<NodeFilter>(n.op_, n.sym_, n.node_);
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("x", IDENT("n"));
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = n.sym_;
|
2017-03-15 22:49:19 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
2017-03-21 18:25:44 +08:00
|
|
|
auto produce = MakeProduce(node_filter, output);
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
2017-03-22 20:40:50 +08:00
|
|
|
EXPECT_EQ(result.GetResults().size(), 1);
|
2017-03-15 22:49:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, NodeFilterMultipleLabels) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add a few nodes to the database
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Label label1 = dba->label("label1");
|
|
|
|
GraphDbTypes::Label label2 = dba->label("label2");
|
|
|
|
GraphDbTypes::Label label3 = dba->label("label3");
|
2017-03-15 22:49:19 +08:00
|
|
|
// the test will look for nodes that have label1 and label2
|
2017-03-21 18:25:44 +08:00
|
|
|
dba->insert_vertex(); // NOT accepted
|
|
|
|
dba->insert_vertex().add_label(label1); // NOT accepted
|
|
|
|
dba->insert_vertex().add_label(label2); // NOT accepted
|
|
|
|
dba->insert_vertex().add_label(label3); // NOT accepted
|
|
|
|
auto v1 = dba->insert_vertex(); // YES accepted
|
2017-03-15 22:49:19 +08:00
|
|
|
v1.add_label(label1);
|
|
|
|
v1.add_label(label2);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto v2 = dba->insert_vertex(); // NOT accepted
|
2017-03-15 22:49:19 +08:00
|
|
|
v2.add_label(label1);
|
|
|
|
v2.add_label(label3);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto v3 = dba->insert_vertex(); // YES accepted
|
2017-03-15 22:49:19 +08:00
|
|
|
v3.add_label(label1);
|
|
|
|
v3.add_label(label2);
|
|
|
|
v3.add_label(label3);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-15 22:49:19 +08:00
|
|
|
|
2017-03-16 22:00:34 +08:00
|
|
|
AstTreeStorage storage;
|
2017-03-21 18:25:44 +08:00
|
|
|
SymbolTable symbol_table;
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
// make a scan all
|
2017-03-21 18:25:44 +08:00
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
n.node_->labels_.emplace_back(label1);
|
|
|
|
n.node_->labels_.emplace_back(label2);
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
// node filtering
|
2017-03-28 21:43:58 +08:00
|
|
|
auto node_filter = std::make_shared<NodeFilter>(n.op_, n.sym_, n.node_);
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("n", IDENT("n"));
|
2017-03-15 22:49:19 +08:00
|
|
|
auto produce = MakeProduce(node_filter, output);
|
|
|
|
|
|
|
|
// fill up the symbol table
|
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = n.sym_;
|
2017-03-15 22:49:19 +08:00
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), 2);
|
|
|
|
}
|
2017-03-16 16:28:16 +08:00
|
|
|
|
|
|
|
TEST(Interpreter, CreateNodeWithAttributes) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Label label = dba->label("Person");
|
|
|
|
GraphDbTypes::Property property = dba->label("age");
|
2017-03-16 16:28:16 +08:00
|
|
|
|
2017-03-16 22:00:34 +08:00
|
|
|
AstTreeStorage storage;
|
2017-03-22 20:40:50 +08:00
|
|
|
SymbolTable symbol_table;
|
2017-03-16 22:00:34 +08:00
|
|
|
|
2017-03-24 23:50:42 +08:00
|
|
|
auto node = NODE("n");
|
2017-03-22 20:40:50 +08:00
|
|
|
symbol_table[*node->identifier_] = symbol_table.CreateSymbol("n");
|
2017-03-16 16:28:16 +08:00
|
|
|
node->labels_.emplace_back(label);
|
2017-03-24 23:50:42 +08:00
|
|
|
node->properties_[property] = LITERAL(42);
|
2017-03-16 16:28:16 +08:00
|
|
|
|
2017-03-24 19:59:23 +08:00
|
|
|
auto create = std::make_shared<CreateNode>(node, nullptr);
|
2017-03-27 19:09:14 +08:00
|
|
|
PullAll(create, *dba, symbol_table);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-16 16:28:16 +08:00
|
|
|
|
|
|
|
// count the number of vertices
|
|
|
|
int vertex_count = 0;
|
|
|
|
for (VertexAccessor vertex : dba->vertices()) {
|
|
|
|
vertex_count++;
|
|
|
|
EXPECT_EQ(vertex.labels().size(), 1);
|
|
|
|
EXPECT_EQ(*vertex.labels().begin(), label);
|
2017-03-22 20:40:50 +08:00
|
|
|
EXPECT_EQ(vertex.Properties().size(), 1);
|
|
|
|
auto prop_eq = vertex.PropsAt(property) == TypedValue(42);
|
|
|
|
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
|
|
|
|
EXPECT_TRUE(prop_eq.Value<bool>());
|
2017-03-16 16:28:16 +08:00
|
|
|
}
|
|
|
|
EXPECT_EQ(vertex_count, 1);
|
|
|
|
}
|
2017-03-21 18:25:44 +08:00
|
|
|
|
2017-03-22 20:40:50 +08:00
|
|
|
TEST(Interpreter, CreateReturn) {
|
|
|
|
// test CREATE (n:Person {age: 42}) RETURN n, n.age
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Label label = dba->label("Person");
|
|
|
|
GraphDbTypes::Property property = dba->label("age");
|
2017-03-22 20:40:50 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
2017-03-24 23:50:42 +08:00
|
|
|
auto node = NODE("n");
|
2017-03-22 20:40:50 +08:00
|
|
|
auto sym_n = symbol_table.CreateSymbol("n");
|
|
|
|
symbol_table[*node->identifier_] = sym_n;
|
|
|
|
node->labels_.emplace_back(label);
|
2017-03-24 23:50:42 +08:00
|
|
|
node->properties_[property] = LITERAL(42);
|
2017-03-22 20:40:50 +08:00
|
|
|
|
2017-03-24 19:59:23 +08:00
|
|
|
auto create = std::make_shared<CreateNode>(node, nullptr);
|
2017-03-24 23:50:42 +08:00
|
|
|
auto named_expr_n = NEXPR("n", IDENT("n"));
|
2017-03-22 20:40:50 +08:00
|
|
|
symbol_table[*named_expr_n] = symbol_table.CreateSymbol("named_expr_n");
|
|
|
|
symbol_table[*named_expr_n->expression_] = sym_n;
|
2017-03-24 23:50:42 +08:00
|
|
|
auto prop_lookup = PROPERTY_LOOKUP("n", property);
|
2017-03-22 20:40:50 +08:00
|
|
|
symbol_table[*prop_lookup->expression_] = sym_n;
|
2017-03-24 23:50:42 +08:00
|
|
|
auto named_expr_n_p = NEXPR("n", prop_lookup);
|
2017-03-22 20:40:50 +08:00
|
|
|
symbol_table[*named_expr_n_p] = symbol_table.CreateSymbol("named_expr_n_p");
|
|
|
|
symbol_table[*named_expr_n->expression_] = sym_n;
|
|
|
|
|
|
|
|
auto produce = MakeProduce(create, named_expr_n, named_expr_n_p);
|
|
|
|
auto result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(1, result.GetResults().size());
|
|
|
|
EXPECT_EQ(2, result.GetResults()[0].size());
|
|
|
|
EXPECT_EQ(TypedValue::Type::Vertex, result.GetResults()[0][0].type());
|
|
|
|
EXPECT_EQ(1,
|
|
|
|
result.GetResults()[0][0].Value<VertexAccessor>().labels().size());
|
|
|
|
EXPECT_EQ(label,
|
|
|
|
result.GetResults()[0][0].Value<VertexAccessor>().labels()[0]);
|
|
|
|
EXPECT_EQ(TypedValue::Type::Int, result.GetResults()[0][1].type());
|
|
|
|
EXPECT_EQ(42, result.GetResults()[0][1].Value<int64_t>());
|
|
|
|
|
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(1, CountIterable(dba->vertices()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, CreateExpand) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Label label_node_1 = dba->label("Node1");
|
|
|
|
GraphDbTypes::Label label_node_2 = dba->label("Node2");
|
|
|
|
GraphDbTypes::Property property = dba->label("prop");
|
|
|
|
GraphDbTypes::EdgeType edge_type = dba->label("edge_type");
|
2017-03-22 20:40:50 +08:00
|
|
|
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
AstTreeStorage storage;
|
|
|
|
|
|
|
|
auto test_create_path = [&](bool cycle, int expected_nodes_created,
|
|
|
|
int expected_edges_created) {
|
|
|
|
int before_v = CountIterable(dba->vertices());
|
|
|
|
int before_e = CountIterable(dba->edges());
|
|
|
|
|
|
|
|
// data for the first node
|
2017-03-24 23:50:42 +08:00
|
|
|
auto n = NODE("n");
|
2017-03-22 20:40:50 +08:00
|
|
|
n->labels_.emplace_back(label_node_1);
|
2017-03-24 23:50:42 +08:00
|
|
|
n->properties_[property] = LITERAL(1);
|
2017-03-22 20:40:50 +08:00
|
|
|
auto n_sym = symbol_table.CreateSymbol("n");
|
|
|
|
symbol_table[*n->identifier_] = n_sym;
|
|
|
|
|
|
|
|
// data for the second node
|
2017-03-24 23:50:42 +08:00
|
|
|
auto m = NODE("m");
|
2017-03-22 20:40:50 +08:00
|
|
|
m->labels_.emplace_back(label_node_2);
|
2017-03-24 23:50:42 +08:00
|
|
|
m->properties_[property] = LITERAL(2);
|
2017-03-22 20:40:50 +08:00
|
|
|
if (cycle)
|
|
|
|
symbol_table[*m->identifier_] = n_sym;
|
|
|
|
else
|
|
|
|
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
|
|
|
|
|
2017-03-24 23:50:42 +08:00
|
|
|
auto r = EDGE("r", EdgeAtom::Direction::RIGHT);
|
2017-03-27 20:42:33 +08:00
|
|
|
symbol_table[*r->identifier_] = symbol_table.CreateSymbol("r");
|
2017-03-22 20:40:50 +08:00
|
|
|
r->edge_types_.emplace_back(edge_type);
|
2017-03-24 23:50:42 +08:00
|
|
|
r->properties_[property] = LITERAL(3);
|
2017-03-22 20:40:50 +08:00
|
|
|
|
2017-03-24 19:59:23 +08:00
|
|
|
auto create_op = std::make_shared<CreateNode>(n, nullptr);
|
2017-03-22 20:40:50 +08:00
|
|
|
auto create_expand =
|
|
|
|
std::make_shared<CreateExpand>(m, r, create_op, n_sym, cycle);
|
2017-03-27 19:09:14 +08:00
|
|
|
PullAll(create_expand, *dba, symbol_table);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
EXPECT_EQ(CountIterable(dba->vertices()) - before_v,
|
|
|
|
expected_nodes_created);
|
|
|
|
EXPECT_EQ(CountIterable(dba->edges()) - before_e, expected_edges_created);
|
|
|
|
};
|
|
|
|
|
|
|
|
test_create_path(false, 2, 1);
|
|
|
|
test_create_path(true, 1, 1);
|
|
|
|
|
|
|
|
for (VertexAccessor vertex : dba->vertices()) {
|
|
|
|
EXPECT_EQ(vertex.labels().size(), 1);
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Label label = vertex.labels()[0];
|
2017-03-22 20:40:50 +08:00
|
|
|
if (label == label_node_1) {
|
|
|
|
// node created by first op
|
|
|
|
EXPECT_EQ(vertex.PropsAt(property).Value<int64_t>(), 1);
|
|
|
|
} else if (label == label_node_2) {
|
|
|
|
// node create by expansion
|
|
|
|
EXPECT_EQ(vertex.PropsAt(property).Value<int64_t>(), 2);
|
|
|
|
} else {
|
|
|
|
// should not happen
|
|
|
|
FAIL();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (EdgeAccessor edge : dba->edges()) {
|
|
|
|
EXPECT_EQ(edge.edge_type(), edge_type);
|
|
|
|
EXPECT_EQ(edge.PropsAt(property).Value<int64_t>(), 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 19:59:23 +08:00
|
|
|
TEST(Interpreter, MatchCreateNode) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add three nodes we'll match and expand-create from
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
AstTreeStorage storage;
|
|
|
|
|
|
|
|
// first node
|
|
|
|
auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
// second node
|
2017-03-24 23:50:42 +08:00
|
|
|
auto m = NODE("m");
|
2017-03-24 19:59:23 +08:00
|
|
|
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
|
|
|
|
// creation op
|
2017-03-28 21:43:58 +08:00
|
|
|
auto create_node = std::make_shared<CreateNode>(m, n_scan_all.op_);
|
2017-03-24 19:59:23 +08:00
|
|
|
|
|
|
|
EXPECT_EQ(CountIterable(dba->vertices()), 3);
|
2017-03-27 19:09:14 +08:00
|
|
|
PullAll(create_node, *dba, symbol_table);
|
2017-03-24 19:59:23 +08:00
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(CountIterable(dba->vertices()), 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, MatchCreateExpand) {
|
2017-03-22 20:40:50 +08:00
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add three nodes we'll match and expand-create from
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->insert_vertex();
|
|
|
|
dba->advance_command();
|
|
|
|
|
2017-03-29 18:37:58 +08:00
|
|
|
// GraphDbTypes::Label label_node_1 = dba->label("Node1");
|
|
|
|
// GraphDbTypes::Label label_node_2 = dba->label("Node2");
|
|
|
|
// GraphDbTypes::Property property = dba->label("prop");
|
|
|
|
GraphDbTypes::EdgeType edge_type = dba->label("edge_type");
|
2017-03-22 20:40:50 +08:00
|
|
|
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
AstTreeStorage storage;
|
|
|
|
|
|
|
|
auto test_create_path = [&](bool cycle, int expected_nodes_created,
|
|
|
|
int expected_edges_created) {
|
|
|
|
int before_v = CountIterable(dba->vertices());
|
|
|
|
int before_e = CountIterable(dba->edges());
|
|
|
|
|
|
|
|
// data for the first node
|
|
|
|
auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
|
|
|
|
// data for the second node
|
2017-03-24 23:50:42 +08:00
|
|
|
auto m = NODE("m");
|
2017-03-22 20:40:50 +08:00
|
|
|
if (cycle)
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*m->identifier_] = n_scan_all.sym_;
|
2017-03-22 20:40:50 +08:00
|
|
|
else
|
|
|
|
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
|
|
|
|
|
2017-03-24 23:50:42 +08:00
|
|
|
auto r = EDGE("r", EdgeAtom::Direction::RIGHT);
|
2017-03-27 20:42:33 +08:00
|
|
|
symbol_table[*r->identifier_] = symbol_table.CreateSymbol("r");
|
2017-03-22 20:40:50 +08:00
|
|
|
r->edge_types_.emplace_back(edge_type);
|
|
|
|
|
2017-03-28 21:43:58 +08:00
|
|
|
auto create_expand = std::make_shared<CreateExpand>(m, r, n_scan_all.op_,
|
|
|
|
n_scan_all.sym_, cycle);
|
2017-03-27 19:09:14 +08:00
|
|
|
PullAll(create_expand, *dba, symbol_table);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
EXPECT_EQ(CountIterable(dba->vertices()) - before_v,
|
|
|
|
expected_nodes_created);
|
|
|
|
EXPECT_EQ(CountIterable(dba->edges()) - before_e, expected_edges_created);
|
|
|
|
};
|
|
|
|
|
|
|
|
test_create_path(false, 3, 3);
|
|
|
|
test_create_path(true, 0, 6);
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:25:44 +08:00
|
|
|
TEST(Interpreter, Expand) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
|
|
|
|
auto v1 = dba->insert_vertex();
|
2017-03-29 18:37:58 +08:00
|
|
|
v1.add_label((GraphDbTypes::Label)1);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto v2 = dba->insert_vertex();
|
2017-03-29 18:37:58 +08:00
|
|
|
v2.add_label((GraphDbTypes::Label)2);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto v3 = dba->insert_vertex();
|
2017-03-29 18:37:58 +08:00
|
|
|
v3.add_label((GraphDbTypes::Label)3);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto edge_type = dba->edge_type("Edge");
|
|
|
|
dba->insert_edge(v1, v2, edge_type);
|
|
|
|
dba->insert_edge(v1, v3, edge_type);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
2017-03-22 20:40:50 +08:00
|
|
|
auto test_expand = [&](EdgeAtom::Direction direction,
|
|
|
|
int expected_result_count) {
|
2017-03-21 18:25:44 +08:00
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", direction,
|
|
|
|
false, "m", false);
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("m", IDENT("m"));
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = r_m.node_sym_;
|
2017-03-21 18:25:44 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto produce = MakeProduce(r_m.op_, output);
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), expected_result_count);
|
|
|
|
};
|
|
|
|
|
|
|
|
test_expand(EdgeAtom::Direction::RIGHT, 2);
|
|
|
|
test_expand(EdgeAtom::Direction::LEFT, 2);
|
|
|
|
test_expand(EdgeAtom::Direction::BOTH, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, ExpandNodeCycle) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// make a graph (v1)->(v2) that
|
|
|
|
// has a recursive edge (v1)->(v1)
|
|
|
|
auto v1 = dba->insert_vertex();
|
|
|
|
auto v2 = dba->insert_vertex();
|
|
|
|
auto edge_type = dba->edge_type("Edge");
|
|
|
|
dba->insert_edge(v1, v1, edge_type);
|
|
|
|
dba->insert_edge(v1, v2, edge_type);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto test_cycle = [&](bool with_cycle, int expected_result_count) {
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_n = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::RIGHT, false, "n", with_cycle);
|
2017-03-21 18:25:44 +08:00
|
|
|
if (with_cycle)
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*r_n.node_->identifier_] =
|
|
|
|
symbol_table[*n.node_->identifier_];
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("n", IDENT("n"));
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = n.sym_;
|
2017-03-21 18:25:44 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto produce = MakeProduce(r_n.op_, output);
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), expected_result_count);
|
|
|
|
};
|
|
|
|
|
|
|
|
test_cycle(true, 1);
|
|
|
|
test_cycle(false, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, ExpandEdgeCycle) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
|
|
|
|
auto v1 = dba->insert_vertex();
|
2017-03-29 18:37:58 +08:00
|
|
|
v1.add_label((GraphDbTypes::Label)1);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto v2 = dba->insert_vertex();
|
2017-03-29 18:37:58 +08:00
|
|
|
v2.add_label((GraphDbTypes::Label)2);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto v3 = dba->insert_vertex();
|
2017-03-29 18:37:58 +08:00
|
|
|
v3.add_label((GraphDbTypes::Label)3);
|
2017-03-21 18:25:44 +08:00
|
|
|
auto edge_type = dba->edge_type("Edge");
|
|
|
|
dba->insert_edge(v1, v2, edge_type);
|
|
|
|
dba->insert_edge(v1, v3, edge_type);
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto test_cycle = [&](bool with_cycle, int expected_result_count) {
|
|
|
|
auto i = MakeScanAll(storage, symbol_table, "i");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_j = MakeExpand(storage, symbol_table, i.op_, i.sym_, "r",
|
|
|
|
EdgeAtom::Direction::BOTH, false, "j", false);
|
|
|
|
auto r_k = MakeExpand(storage, symbol_table, r_j.op_, r_j.node_sym_, "r",
|
|
|
|
EdgeAtom::Direction::BOTH, with_cycle, "k", false);
|
2017-03-21 18:25:44 +08:00
|
|
|
if (with_cycle)
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*r_k.edge_->identifier_] =
|
|
|
|
symbol_table[*r_j.edge_->identifier_];
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("r", IDENT("r"));
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = r_j.edge_sym_;
|
2017-03-21 18:25:44 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto produce = MakeProduce(r_k.op_, output);
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), expected_result_count);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
test_cycle(true, 4);
|
|
|
|
test_cycle(false, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, EdgeFilter) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// make an N-star expanding from (v1)
|
|
|
|
// where only one edge will qualify
|
|
|
|
// and there are all combinations of
|
|
|
|
// (edge_type yes|no) * (property yes|absent|no)
|
2017-03-29 18:37:58 +08:00
|
|
|
std::vector<GraphDbTypes::EdgeType> edge_types;
|
2017-03-21 18:25:44 +08:00
|
|
|
for (int j = 0; j < 2; ++j)
|
|
|
|
edge_types.push_back(dba->edge_type("et" + std::to_string(j)));
|
|
|
|
std::vector<VertexAccessor> vertices;
|
|
|
|
for (int i = 0; i < 7; ++i) vertices.push_back(dba->insert_vertex());
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Property prop = dba->property("prop");
|
2017-03-21 18:25:44 +08:00
|
|
|
std::vector<EdgeAccessor> edges;
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
|
|
edges.push_back(
|
|
|
|
dba->insert_edge(vertices[0], vertices[i + 1], edge_types[i % 2]));
|
|
|
|
switch (i % 3) {
|
|
|
|
case 0:
|
|
|
|
edges.back().PropsSet(prop, 42);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
edges.back().PropsSet(prop, 100);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-03-22 20:40:50 +08:00
|
|
|
dba->advance_command();
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// define an operator tree for query
|
|
|
|
// MATCH (n)-[r]->(m) RETURN m
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::RIGHT, false, "m", false);
|
|
|
|
r_m.edge_->edge_types_.push_back(edge_types[0]);
|
|
|
|
r_m.edge_->properties_[prop] = LITERAL(42);
|
|
|
|
auto edge_filter =
|
|
|
|
std::make_shared<EdgeFilter>(r_m.op_, r_m.edge_sym_, r_m.edge_);
|
2017-03-21 18:25:44 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("m", IDENT("m"));
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = r_m.node_sym_;
|
2017-03-21 18:25:44 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
|
|
|
auto produce = MakeProduce(edge_filter, output);
|
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
2017-03-24 16:37:28 +08:00
|
|
|
EXPECT_EQ(result.GetResults().size(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, EdgeFilterMultipleTypes) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
auto v1 = dba->insert_vertex();
|
|
|
|
auto v2 = dba->insert_vertex();
|
|
|
|
auto type_1 = dba->edge_type("type_1");
|
|
|
|
auto type_2 = dba->edge_type("type_2");
|
|
|
|
auto type_3 = dba->edge_type("type_3");
|
|
|
|
dba->insert_edge(v1, v2, type_1);
|
|
|
|
dba->insert_edge(v1, v2, type_2);
|
|
|
|
dba->insert_edge(v1, v2, type_3);
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// make a scan all
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::RIGHT, false, "m", false);
|
2017-03-24 16:37:28 +08:00
|
|
|
// add a property filter
|
2017-03-28 21:43:58 +08:00
|
|
|
auto edge_filter =
|
|
|
|
std::make_shared<EdgeFilter>(r_m.op_, r_m.edge_sym_, r_m.edge_);
|
|
|
|
r_m.edge_->edge_types_.push_back(type_1);
|
|
|
|
r_m.edge_->edge_types_.push_back(type_2);
|
2017-03-24 16:37:28 +08:00
|
|
|
|
|
|
|
// make a named expression and a produce
|
2017-03-24 23:50:42 +08:00
|
|
|
auto output = NEXPR("m", IDENT("m"));
|
2017-03-24 16:37:28 +08:00
|
|
|
auto produce = MakeProduce(edge_filter, output);
|
|
|
|
|
|
|
|
// fill up the symbol table
|
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = r_m.node_sym_;
|
2017-03-24 16:37:28 +08:00
|
|
|
|
|
|
|
ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(result.GetResults().size(), 2);
|
2017-03-21 18:25:44 +08:00
|
|
|
}
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
TEST(Interpreter, Delete) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
// make a fully-connected (one-direction, no cycles) with 4 nodes
|
|
|
|
std::vector<VertexAccessor> vertices;
|
|
|
|
for (int i = 0; i < 4; ++i) vertices.push_back(dba->insert_vertex());
|
|
|
|
auto type = dba->edge_type("type");
|
|
|
|
for (int j = 0; j < 4; ++j)
|
|
|
|
for (int k = j + 1; k < 4; ++k)
|
|
|
|
dba->insert_edge(vertices[j], vertices[k], type);
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(4, CountIterable(dba->vertices()));
|
|
|
|
EXPECT_EQ(6, CountIterable(dba->edges()));
|
2017-03-25 00:01:06 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
2017-03-27 19:09:14 +08:00
|
|
|
SymbolTable symbol_table;
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
// attempt to delete a vertex, and fail
|
|
|
|
{
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_get = storage.Create<Identifier>("n");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*n_get] = n.sym_;
|
2017-03-27 19:09:14 +08:00
|
|
|
auto delete_op = std::make_shared<plan::Delete>(
|
2017-03-28 21:43:58 +08:00
|
|
|
n.op_, std::vector<Expression *>{n_get}, false);
|
2017-03-27 19:09:14 +08:00
|
|
|
EXPECT_THROW(PullAll(delete_op, *dba, symbol_table), QueryRuntimeException);
|
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(4, CountIterable(dba->vertices()));
|
|
|
|
EXPECT_EQ(6, CountIterable(dba->edges()));
|
|
|
|
}
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
// detach delete a single vertex
|
|
|
|
{
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_get = storage.Create<Identifier>("n");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*n_get] = n.sym_;
|
2017-03-27 19:09:14 +08:00
|
|
|
auto delete_op = std::make_shared<plan::Delete>(
|
2017-03-28 21:43:58 +08:00
|
|
|
n.op_, std::vector<Expression *>{n_get}, true);
|
2017-03-27 19:09:14 +08:00
|
|
|
Frame frame(symbol_table.max_position());
|
|
|
|
delete_op->MakeCursor(*dba)->Pull(frame, symbol_table);
|
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(3, CountIterable(dba->vertices()));
|
|
|
|
EXPECT_EQ(3, CountIterable(dba->edges()));
|
|
|
|
}
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
// delete all remaining edges
|
|
|
|
{
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::RIGHT, false, "m", false);
|
2017-03-27 19:09:14 +08:00
|
|
|
auto r_get = storage.Create<Identifier>("r");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*r_get] = r_m.edge_sym_;
|
2017-03-27 19:09:14 +08:00
|
|
|
auto delete_op = std::make_shared<plan::Delete>(
|
2017-03-28 21:43:58 +08:00
|
|
|
r_m.op_, std::vector<Expression *>{r_get}, false);
|
2017-03-27 19:09:14 +08:00
|
|
|
PullAll(delete_op, *dba, symbol_table);
|
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(3, CountIterable(dba->vertices()));
|
|
|
|
EXPECT_EQ(0, CountIterable(dba->edges()));
|
|
|
|
}
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
// delete all remaining vertices
|
|
|
|
{
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_get = storage.Create<Identifier>("n");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*n_get] = n.sym_;
|
2017-03-27 19:09:14 +08:00
|
|
|
auto delete_op = std::make_shared<plan::Delete>(
|
2017-03-28 21:43:58 +08:00
|
|
|
n.op_, std::vector<Expression *>{n_get}, false);
|
2017-03-27 19:09:14 +08:00
|
|
|
PullAll(delete_op, *dba, symbol_table);
|
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(0, CountIterable(dba->vertices()));
|
|
|
|
EXPECT_EQ(0, CountIterable(dba->edges()));
|
|
|
|
}
|
2017-03-25 00:01:06 +08:00
|
|
|
}
|
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
TEST(Interpreter, DeleteReturn) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
// make a fully-connected (one-direction, no cycles) with 4 nodes
|
|
|
|
auto prop = dba->property("prop");
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
auto va = dba->insert_vertex();
|
|
|
|
va.PropsSet(prop, 42);
|
|
|
|
}
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(4, CountIterable(dba->vertices()));
|
|
|
|
EXPECT_EQ(0, CountIterable(dba->edges()));
|
2017-03-25 00:01:06 +08:00
|
|
|
|
|
|
|
AstTreeStorage storage;
|
2017-03-27 19:09:14 +08:00
|
|
|
SymbolTable symbol_table;
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
auto n_get = storage.Create<Identifier>("n");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*n_get] = n.sym_;
|
2017-03-27 19:09:14 +08:00
|
|
|
auto delete_op = std::make_shared<plan::Delete>(
|
2017-03-28 21:43:58 +08:00
|
|
|
n.op_, std::vector<Expression *>{n_get}, true);
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
auto prop_lookup =
|
|
|
|
storage.Create<PropertyLookup>(storage.Create<Identifier>("n"), prop);
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*prop_lookup->expression_] = n.sym_;
|
2017-03-27 19:09:14 +08:00
|
|
|
auto n_p = storage.Create<NamedExpression>("n", prop_lookup);
|
|
|
|
symbol_table[*n_p] = symbol_table.CreateSymbol("bla");
|
|
|
|
auto produce = MakeProduce(delete_op, n_p);
|
2017-03-25 00:01:06 +08:00
|
|
|
|
2017-03-27 19:09:14 +08:00
|
|
|
auto result = CollectProduce(produce, symbol_table, *dba);
|
|
|
|
EXPECT_EQ(4, result.GetResults().size());
|
|
|
|
dba->advance_command();
|
|
|
|
EXPECT_EQ(0, CountIterable(dba->vertices()));
|
2017-03-25 00:01:06 +08:00
|
|
|
}
|
2017-03-27 19:15:26 +08:00
|
|
|
|
|
|
|
TEST(Interpreter, Filter) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// add a 6 nodes with property 'prop', 2 have true as value
|
2017-03-29 18:37:58 +08:00
|
|
|
GraphDbTypes::Property property = dba->property("Property");
|
2017-03-27 19:15:26 +08:00
|
|
|
for (int i = 0; i < 6; ++i)
|
|
|
|
dba->insert_vertex().PropsSet(property, i % 3 == 0);
|
2017-03-29 14:50:55 +08:00
|
|
|
dba->insert_vertex(); // prop not set, gives NULL
|
2017-03-27 19:15:26 +08:00
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto e =
|
|
|
|
storage.Create<PropertyLookup>(storage.Create<Identifier>("n"), property);
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*e->expression_] = n.sym_;
|
|
|
|
auto f = std::make_shared<Filter>(n.op_, e);
|
2017-03-27 19:15:26 +08:00
|
|
|
|
|
|
|
auto output =
|
|
|
|
storage.Create<NamedExpression>("x", storage.Create<Identifier>("n"));
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*output->expression_] = n.sym_;
|
2017-03-27 19:15:26 +08:00
|
|
|
symbol_table[*output] = symbol_table.CreateSymbol("named_expression_1");
|
|
|
|
auto produce = MakeProduce(f, output);
|
|
|
|
|
|
|
|
EXPECT_EQ(CollectProduce(produce, symbol_table, *dba).GetResults().size(), 2);
|
|
|
|
}
|
2017-03-29 14:50:55 +08:00
|
|
|
|
|
|
|
TEST(Interpreter, SetProperty) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// graph with 4 vertices in connected pairs
|
|
|
|
// the origin vertex in each par and both edges
|
|
|
|
// have a property set
|
|
|
|
auto v1 = dba->insert_vertex();
|
|
|
|
auto v2 = dba->insert_vertex();
|
|
|
|
auto v3 = dba->insert_vertex();
|
|
|
|
auto v4 = dba->insert_vertex();
|
|
|
|
auto edge_type = dba->edge_type("edge_type");
|
|
|
|
dba->insert_edge(v1, v3, edge_type);
|
|
|
|
dba->insert_edge(v2, v4, edge_type);
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// scan (n)-[r]->(m)
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::RIGHT, false, "m", false);
|
2017-03-29 14:50:55 +08:00
|
|
|
|
|
|
|
// set prop1 to 42 on n and r
|
|
|
|
auto prop1 = dba->property("prop1");
|
|
|
|
auto literal = LITERAL(42);
|
|
|
|
|
|
|
|
auto n_p = PROPERTY_LOOKUP("n", prop1);
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*n_p->expression_] = n.sym_;
|
|
|
|
auto set_n_p = std::make_shared<plan::SetProperty>(r_m.op_, n_p, literal);
|
2017-03-29 14:50:55 +08:00
|
|
|
|
|
|
|
auto r_p = PROPERTY_LOOKUP("r", prop1);
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*r_p->expression_] = r_m.edge_sym_;
|
2017-03-29 14:50:55 +08:00
|
|
|
auto set_r_p = std::make_shared<plan::SetProperty>(set_n_p, r_p, literal);
|
|
|
|
EXPECT_EQ(2, PullAll(set_r_p, *dba, symbol_table));
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
EXPECT_EQ(CountIterable(dba->edges()), 2);
|
|
|
|
for (EdgeAccessor edge : dba->edges()) {
|
|
|
|
ASSERT_EQ(edge.PropsAt(prop1).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(edge.PropsAt(prop1).Value<int64_t>(), 42);
|
|
|
|
VertexAccessor from = edge.from();
|
|
|
|
VertexAccessor to = edge.to();
|
|
|
|
ASSERT_EQ(from.PropsAt(prop1).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(from.PropsAt(prop1).Value<int64_t>(), 42);
|
|
|
|
ASSERT_EQ(to.PropsAt(prop1).type(), PropertyValue::Type::Null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, SetProperties) {
|
|
|
|
auto test_set_properties = [](bool update) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
// graph: ({a: 0})-[:R {b:1}]->({c:2})
|
|
|
|
auto prop_a = dba->property("a");
|
|
|
|
auto prop_b = dba->property("b");
|
|
|
|
auto prop_c = dba->property("c");
|
|
|
|
auto v1 = dba->insert_vertex();
|
|
|
|
auto v2 = dba->insert_vertex();
|
|
|
|
auto e = dba->insert_edge(v1, v2, dba->edge_type("R"));
|
|
|
|
v1.PropsSet(prop_a, 0);
|
|
|
|
e.PropsSet(prop_b, 1);
|
|
|
|
v2.PropsSet(prop_c, 2);
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// scan (n)-[r]->(m)
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2017-03-28 21:43:58 +08:00
|
|
|
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::RIGHT, false, "m", false);
|
2017-03-29 14:50:55 +08:00
|
|
|
|
|
|
|
auto op = update ? plan::SetProperties::Op::UPDATE
|
|
|
|
: plan::SetProperties::Op::REPLACE;
|
|
|
|
|
|
|
|
// set properties on r to n, and on r to m
|
|
|
|
auto r_ident = IDENT("r");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*r_ident] = r_m.edge_sym_;
|
2017-03-29 14:50:55 +08:00
|
|
|
auto m_ident = IDENT("m");
|
2017-03-28 21:43:58 +08:00
|
|
|
symbol_table[*m_ident] = r_m.node_sym_;
|
|
|
|
auto set_r_to_n =
|
|
|
|
std::make_shared<plan::SetProperties>(r_m.op_, n.sym_, r_ident, op);
|
2017-03-29 14:50:55 +08:00
|
|
|
auto set_m_to_r = std::make_shared<plan::SetProperties>(
|
2017-03-28 21:43:58 +08:00
|
|
|
set_r_to_n, r_m.edge_sym_, m_ident, op);
|
2017-03-29 14:50:55 +08:00
|
|
|
EXPECT_EQ(1, PullAll(set_m_to_r, *dba, symbol_table));
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
EXPECT_EQ(CountIterable(dba->edges()), 1);
|
|
|
|
for (EdgeAccessor edge : dba->edges()) {
|
|
|
|
VertexAccessor from = edge.from();
|
|
|
|
EXPECT_EQ(from.Properties().size(), update ? 2 : 1);
|
|
|
|
if (update) {
|
|
|
|
ASSERT_EQ(from.PropsAt(prop_a).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(from.PropsAt(prop_a).Value<int64_t>(), 0);
|
|
|
|
}
|
|
|
|
ASSERT_EQ(from.PropsAt(prop_b).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(from.PropsAt(prop_b).Value<int64_t>(), 1);
|
|
|
|
|
|
|
|
EXPECT_EQ(edge.Properties().size(), update ? 2 : 1);
|
|
|
|
if (update) {
|
|
|
|
ASSERT_EQ(edge.PropsAt(prop_b).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(edge.PropsAt(prop_b).Value<int64_t>(), 1);
|
|
|
|
}
|
|
|
|
ASSERT_EQ(edge.PropsAt(prop_c).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(edge.PropsAt(prop_c).Value<int64_t>(), 2);
|
|
|
|
|
|
|
|
VertexAccessor to = edge.to();
|
|
|
|
EXPECT_EQ(to.Properties().size(), 1);
|
|
|
|
ASSERT_EQ(to.PropsAt(prop_c).type(), PropertyValue::Type::Int);
|
|
|
|
EXPECT_EQ(to.PropsAt(prop_c).Value<int64_t>(), 2);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
test_set_properties(true);
|
|
|
|
test_set_properties(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Interpreter, SetLabels) {
|
|
|
|
Dbms dbms;
|
|
|
|
auto dba = dbms.active();
|
|
|
|
|
|
|
|
auto label1 = dba->label("label1");
|
|
|
|
auto label2 = dba->label("label2");
|
|
|
|
auto label3 = dba->label("label3");
|
|
|
|
dba->insert_vertex().add_label(label1);
|
|
|
|
dba->insert_vertex().add_label(label1);
|
|
|
|
dba->advance_command();
|
|
|
|
|
|
|
|
AstTreeStorage storage;
|
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto label_set = std::make_shared<plan::SetLabels>(
|
2017-03-29 18:37:58 +08:00
|
|
|
n.op_, n.sym_, std::vector<GraphDbTypes::Label>{label2, label3});
|
2017-03-29 14:50:55 +08:00
|
|
|
EXPECT_EQ(2, PullAll(label_set, *dba, symbol_table));
|
|
|
|
|
|
|
|
for (VertexAccessor vertex : dba->vertices()) {
|
|
|
|
EXPECT_EQ(3, vertex.labels().size());
|
|
|
|
EXPECT_TRUE(vertex.has_label(label2));
|
|
|
|
EXPECT_TRUE(vertex.has_label(label3));
|
|
|
|
}
|
|
|
|
}
|