2017-04-11 21:11:48 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
#include "communication/result_stream_faker.hpp"
|
2018-10-05 18:37:23 +08:00
|
|
|
#include "database/single_node/graph_db.hpp"
|
2017-04-11 21:11:48 +08:00
|
|
|
#include "query/context.hpp"
|
|
|
|
#include "query/exceptions.hpp"
|
2017-04-13 16:01:16 +08:00
|
|
|
#include "query/plan/operator.hpp"
|
2017-04-11 21:11:48 +08:00
|
|
|
#include "query_plan_common.hpp"
|
|
|
|
|
|
|
|
using namespace query;
|
|
|
|
using namespace query::plan;
|
2017-08-21 21:44:35 +08:00
|
|
|
using query::test_common::ToList;
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
using query::test_common::ToMap;
|
2017-10-30 17:43:25 +08:00
|
|
|
using testing::UnorderedElementsAre;
|
2017-04-11 21:11:48 +08:00
|
|
|
|
|
|
|
TEST(QueryPlan, Accumulate) {
|
|
|
|
// simulate the following two query execution on an empty db
|
|
|
|
// CREATE ({x:0})-[:T]->({x:0})
|
|
|
|
// MATCH (n)--(m) SET n.x = n.x + 1, m.x = m.x + 1 RETURN n.x, m.x
|
|
|
|
// without accumulation we expected results to be [[1, 1], [2, 2]]
|
|
|
|
// with accumulation we expect them to be [[2, 2], [2, 2]]
|
|
|
|
|
|
|
|
auto check = [&](bool accumulate) {
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba_ptr = db.Access();
|
|
|
|
auto &dba = *dba_ptr;
|
2017-10-30 17:43:25 +08:00
|
|
|
auto prop = dba.Property("x");
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
auto v1 = dba.InsertVertex();
|
2017-04-11 21:11:48 +08:00
|
|
|
v1.PropsSet(prop, 0);
|
2017-10-30 17:43:25 +08:00
|
|
|
auto v2 = dba.InsertVertex();
|
2017-04-11 21:11:48 +08:00
|
|
|
v2.PropsSet(prop, 0);
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.InsertEdge(v1, v2, dba.EdgeType("T"));
|
|
|
|
dba.AdvanceCommand();
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-11 21:11:48 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
2018-05-15 19:10:15 +08:00
|
|
|
auto r_m =
|
|
|
|
MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
|
|
|
|
EdgeAtom::Direction::BOTH, {}, "m", false, GraphView::OLD);
|
2017-04-11 21:11:48 +08:00
|
|
|
|
|
|
|
auto one = LITERAL(1);
|
|
|
|
auto n_p = PROPERTY_LOOKUP("n", prop);
|
|
|
|
symbol_table[*n_p->expression_] = n.sym_;
|
|
|
|
auto set_n_p =
|
|
|
|
std::make_shared<plan::SetProperty>(r_m.op_, n_p, ADD(n_p, one));
|
|
|
|
auto m_p = PROPERTY_LOOKUP("m", prop);
|
|
|
|
symbol_table[*m_p->expression_] = r_m.node_sym_;
|
|
|
|
auto set_m_p =
|
|
|
|
std::make_shared<plan::SetProperty>(set_n_p, m_p, ADD(m_p, one));
|
|
|
|
|
|
|
|
std::shared_ptr<LogicalOperator> last_op = set_m_p;
|
|
|
|
if (accumulate) {
|
|
|
|
last_op = std::make_shared<Accumulate>(
|
|
|
|
last_op, std::vector<Symbol>{n.sym_, r_m.node_sym_});
|
|
|
|
}
|
|
|
|
|
|
|
|
auto n_p_ne = NEXPR("n.p", n_p);
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table[*n_p_ne] = symbol_table.CreateSymbol("n_p_ne", true);
|
2017-04-11 21:11:48 +08:00
|
|
|
auto m_p_ne = NEXPR("m.p", m_p);
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table[*m_p_ne] = symbol_table.CreateSymbol("m_p_ne", true);
|
2017-04-11 21:11:48 +08:00
|
|
|
auto produce = MakeProduce(last_op, n_p_ne, m_p_ne);
|
2017-10-30 17:43:25 +08:00
|
|
|
auto results = CollectProduce(produce.get(), symbol_table, dba);
|
2017-04-11 21:11:48 +08:00
|
|
|
std::vector<int> results_data;
|
2017-05-30 21:19:38 +08:00
|
|
|
for (const auto &row : results)
|
2017-04-11 21:11:48 +08:00
|
|
|
for (const auto &column : row)
|
|
|
|
results_data.emplace_back(column.Value<int64_t>());
|
|
|
|
if (accumulate)
|
|
|
|
EXPECT_THAT(results_data, testing::ElementsAre(2, 2, 2, 2));
|
|
|
|
else
|
|
|
|
EXPECT_THAT(results_data, testing::ElementsAre(1, 1, 2, 2));
|
|
|
|
};
|
|
|
|
|
|
|
|
check(false);
|
|
|
|
check(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(QueryPlan, AccumulateAdvance) {
|
|
|
|
// we simulate 'CREATE (n) WITH n AS n MATCH (m) RETURN m'
|
|
|
|
// to get correct results we need to advance the command
|
|
|
|
auto check = [&](bool advance) {
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba = db.Access();
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-11 21:11:48 +08:00
|
|
|
SymbolTable symbol_table;
|
2018-12-20 16:38:23 +08:00
|
|
|
NodeCreationInfo node;
|
|
|
|
node.symbol = symbol_table.CreateSymbol("n", true);
|
2018-08-30 19:31:50 +08:00
|
|
|
auto create = std::make_shared<CreateNode>(nullptr, node);
|
2017-04-11 21:11:48 +08:00
|
|
|
auto accumulate = std::make_shared<Accumulate>(
|
2018-12-20 16:38:23 +08:00
|
|
|
create, std::vector<Symbol>{node.symbol}, advance);
|
2017-04-11 21:11:48 +08:00
|
|
|
auto match = MakeScanAll(storage, symbol_table, "m", accumulate);
|
2018-07-26 15:08:21 +08:00
|
|
|
EXPECT_EQ(advance ? 1 : 0, PullAll(match.op_, *dba, symbol_table));
|
2017-04-11 21:11:48 +08:00
|
|
|
};
|
|
|
|
check(false);
|
|
|
|
check(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Produce> MakeAggregationProduce(
|
|
|
|
std::shared_ptr<LogicalOperator> input, SymbolTable &symbol_table,
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage &storage, const std::vector<Expression *> aggr_inputs,
|
2017-04-11 21:11:48 +08:00
|
|
|
const std::vector<Aggregation::Op> aggr_ops,
|
|
|
|
const std::vector<Expression *> group_by_exprs,
|
|
|
|
const std::vector<Symbol> remember) {
|
|
|
|
// prepare all the aggregations
|
|
|
|
std::vector<Aggregate::Element> aggregates;
|
|
|
|
std::vector<NamedExpression *> named_expressions;
|
|
|
|
|
|
|
|
auto aggr_inputs_it = aggr_inputs.begin();
|
|
|
|
for (auto aggr_op : aggr_ops) {
|
|
|
|
// TODO change this from using IDENT to using AGGREGATION
|
|
|
|
// once AGGREGATION is handled properly in ExpressionEvaluation
|
|
|
|
auto named_expr = NEXPR("", IDENT("aggregation"));
|
|
|
|
named_expressions.push_back(named_expr);
|
|
|
|
symbol_table[*named_expr->expression_] =
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table.CreateSymbol("aggregation", true);
|
|
|
|
symbol_table[*named_expr] =
|
|
|
|
symbol_table.CreateSymbol("named_expression", true);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// the key expression is only used in COLLECT_MAP
|
|
|
|
Expression *key_expr_ptr =
|
|
|
|
aggr_op == Aggregation::Op::COLLECT_MAP ? LITERAL("key") : nullptr;
|
|
|
|
aggregates.emplace_back(
|
|
|
|
Aggregate::Element{*aggr_inputs_it++, key_expr_ptr, aggr_op,
|
|
|
|
symbol_table[*named_expr->expression_]});
|
2017-04-11 21:11:48 +08:00
|
|
|
}
|
|
|
|
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// Produce will also evaluate group_by expressions and return them after the
|
|
|
|
// aggregations.
|
2017-04-11 21:11:48 +08:00
|
|
|
for (auto group_by_expr : group_by_exprs) {
|
|
|
|
auto named_expr = NEXPR("", group_by_expr);
|
|
|
|
named_expressions.push_back(named_expr);
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table[*named_expr] =
|
|
|
|
symbol_table.CreateSymbol("named_expression", true);
|
2017-04-11 21:11:48 +08:00
|
|
|
}
|
|
|
|
auto aggregation =
|
|
|
|
std::make_shared<Aggregate>(input, aggregates, group_by_exprs, remember);
|
|
|
|
return std::make_shared<Produce>(aggregation, named_expressions);
|
|
|
|
}
|
|
|
|
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
/** Test fixture for all the aggregation ops in one return. */
|
2017-05-30 15:37:24 +08:00
|
|
|
class QueryPlanAggregateOps : public ::testing::Test {
|
|
|
|
protected:
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
std::unique_ptr<database::GraphDbAccessor> dba_ptr{db.Access()};
|
|
|
|
database::GraphDbAccessor &dba{*dba_ptr};
|
2018-01-16 17:09:15 +08:00
|
|
|
storage::Property prop = dba.Property("prop");
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-11 21:11:48 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
2017-05-30 15:37:24 +08:00
|
|
|
void AddData() {
|
|
|
|
// setup is several nodes most of which have an int property set
|
|
|
|
// we will take the sum, avg, min, max and count
|
|
|
|
// we won't group by anything
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.InsertVertex().PropsSet(prop, 5);
|
|
|
|
dba.InsertVertex().PropsSet(prop, 7);
|
|
|
|
dba.InsertVertex().PropsSet(prop, 12);
|
2017-05-30 15:37:24 +08:00
|
|
|
// a missing property (null) gets ignored by all aggregations except
|
|
|
|
// COUNT(*)
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.InsertVertex();
|
|
|
|
dba.AdvanceCommand();
|
2017-05-30 15:37:24 +08:00
|
|
|
}
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2017-05-30 15:37:24 +08:00
|
|
|
auto AggregationResults(bool with_group_by,
|
|
|
|
std::vector<Aggregation::Op> ops = {
|
|
|
|
Aggregation::Op::COUNT, Aggregation::Op::COUNT,
|
|
|
|
Aggregation::Op::MIN, Aggregation::Op::MAX,
|
|
|
|
Aggregation::Op::SUM, Aggregation::Op::AVG,
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
Aggregation::Op::COLLECT_LIST,
|
|
|
|
Aggregation::Op::COLLECT_MAP}) {
|
2017-05-30 15:37:24 +08:00
|
|
|
// match all nodes and perform aggregations
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_p = PROPERTY_LOOKUP("n", prop);
|
|
|
|
symbol_table[*n_p->expression_] = n.sym_;
|
|
|
|
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
std::vector<Expression *> aggregation_expressions(ops.size(), n_p);
|
2017-05-30 15:37:24 +08:00
|
|
|
std::vector<Expression *> group_bys;
|
|
|
|
if (with_group_by) group_bys.push_back(n_p);
|
|
|
|
aggregation_expressions[0] = nullptr;
|
|
|
|
auto produce =
|
|
|
|
MakeAggregationProduce(n.op_, symbol_table, storage,
|
|
|
|
aggregation_expressions, ops, group_bys, {});
|
2017-10-30 17:43:25 +08:00
|
|
|
return CollectProduce(produce.get(), symbol_table, dba);
|
2017-05-30 15:37:24 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(QueryPlanAggregateOps, WithData) {
|
|
|
|
AddData();
|
|
|
|
auto results = AggregationResults(false);
|
2017-04-11 21:11:48 +08:00
|
|
|
|
|
|
|
ASSERT_EQ(results.size(), 1);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
ASSERT_EQ(results[0].size(), 8);
|
2017-05-06 23:57:39 +08:00
|
|
|
// count(*)
|
2017-04-11 21:11:48 +08:00
|
|
|
ASSERT_EQ(results[0][0].type(), TypedValue::Type::Int);
|
2017-05-06 23:57:39 +08:00
|
|
|
EXPECT_EQ(results[0][0].Value<int64_t>(), 4);
|
|
|
|
// count
|
2017-04-11 21:11:48 +08:00
|
|
|
ASSERT_EQ(results[0][1].type(), TypedValue::Type::Int);
|
2017-05-06 23:57:39 +08:00
|
|
|
EXPECT_EQ(results[0][1].Value<int64_t>(), 3);
|
|
|
|
// min
|
2017-04-11 21:11:48 +08:00
|
|
|
ASSERT_EQ(results[0][2].type(), TypedValue::Type::Int);
|
2017-05-06 23:57:39 +08:00
|
|
|
EXPECT_EQ(results[0][2].Value<int64_t>(), 5);
|
|
|
|
// max
|
2017-04-11 21:11:48 +08:00
|
|
|
ASSERT_EQ(results[0][3].type(), TypedValue::Type::Int);
|
2017-05-06 23:57:39 +08:00
|
|
|
EXPECT_EQ(results[0][3].Value<int64_t>(), 12);
|
|
|
|
// sum
|
|
|
|
ASSERT_EQ(results[0][4].type(), TypedValue::Type::Int);
|
|
|
|
EXPECT_EQ(results[0][4].Value<int64_t>(), 24);
|
2017-04-11 21:11:48 +08:00
|
|
|
// avg
|
2017-05-06 23:57:39 +08:00
|
|
|
ASSERT_EQ(results[0][5].type(), TypedValue::Type::Double);
|
|
|
|
EXPECT_FLOAT_EQ(results[0][5].Value<double>(), 24 / 3.0);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// collect list
|
2017-05-19 21:49:25 +08:00
|
|
|
ASSERT_EQ(results[0][6].type(), TypedValue::Type::List);
|
2017-08-21 21:44:35 +08:00
|
|
|
EXPECT_THAT(ToList<int64_t>(results[0][6]), UnorderedElementsAre(5, 7, 12));
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// collect map
|
|
|
|
ASSERT_EQ(results[0][7].type(), TypedValue::Type::Map);
|
|
|
|
auto map = ToMap<int64_t>(results[0][7]);
|
|
|
|
ASSERT_EQ(map.size(), 1);
|
|
|
|
EXPECT_EQ(map.begin()->first, "key");
|
|
|
|
EXPECT_FALSE(std::set<int>({5, 7, 12}).insert(map.begin()->second).second);
|
2017-04-11 21:11:48 +08:00
|
|
|
}
|
|
|
|
|
2017-05-30 15:37:24 +08:00
|
|
|
TEST_F(QueryPlanAggregateOps, WithoutDataWithGroupBy) {
|
|
|
|
{
|
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::COUNT});
|
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::SUM});
|
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::AVG});
|
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::MIN});
|
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::MAX});
|
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
{
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::COLLECT_LIST});
|
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
auto results = AggregationResults(true, {Aggregation::Op::COLLECT_MAP});
|
2017-05-30 15:37:24 +08:00
|
|
|
EXPECT_EQ(results.size(), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(QueryPlanAggregateOps, WithoutDataWithoutGroupBy) {
|
|
|
|
auto results = AggregationResults(false);
|
|
|
|
ASSERT_EQ(results.size(), 1);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
ASSERT_EQ(results[0].size(), 8);
|
2017-05-30 15:37:24 +08:00
|
|
|
// count(*)
|
|
|
|
ASSERT_EQ(results[0][0].type(), TypedValue::Type::Int);
|
|
|
|
EXPECT_EQ(results[0][0].Value<int64_t>(), 0);
|
|
|
|
// count
|
|
|
|
ASSERT_EQ(results[0][1].type(), TypedValue::Type::Int);
|
|
|
|
EXPECT_EQ(results[0][1].Value<int64_t>(), 0);
|
|
|
|
// min
|
|
|
|
EXPECT_TRUE(results[0][2].IsNull());
|
|
|
|
// max
|
|
|
|
EXPECT_TRUE(results[0][3].IsNull());
|
|
|
|
// sum
|
|
|
|
EXPECT_TRUE(results[0][4].IsNull());
|
|
|
|
// avg
|
|
|
|
EXPECT_TRUE(results[0][5].IsNull());
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// collect list
|
2017-05-30 15:37:24 +08:00
|
|
|
ASSERT_EQ(results[0][6].type(), TypedValue::Type::List);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
EXPECT_EQ(ToList<int64_t>(results[0][6]).size(), 0);
|
|
|
|
// collect map
|
|
|
|
ASSERT_EQ(results[0][7].type(), TypedValue::Type::Map);
|
|
|
|
EXPECT_EQ(ToMap<int64_t>(results[0][7]).size(), 0);
|
2017-05-30 15:37:24 +08:00
|
|
|
}
|
|
|
|
|
2017-04-11 21:11:48 +08:00
|
|
|
TEST(QueryPlan, AggregateGroupByValues) {
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// Tests that distinct groups are aggregated properly for values of all types.
|
|
|
|
// Also test the "remember" part of the Aggregation API as final results are
|
|
|
|
// obtained via a property lookup of a remembered node.
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba_ptr = db.Access();
|
|
|
|
auto &dba = *dba_ptr;
|
2017-04-11 21:11:48 +08:00
|
|
|
|
Clean-up TypedValue misuse
Summary:
In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future.
After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache.
Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`.
Reviewers: teon.banek, msantl
Reviewed By: teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
|
|
|
// a vector of PropertyValue to be set as property values on vertices
|
2017-04-11 21:11:48 +08:00
|
|
|
// most of them should result in a distinct group (commented where not)
|
Clean-up TypedValue misuse
Summary:
In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future.
After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache.
Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`.
Reviewers: teon.banek, msantl
Reviewed By: teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
|
|
|
std::vector<PropertyValue> group_by_vals;
|
2017-04-11 21:11:48 +08:00
|
|
|
group_by_vals.emplace_back(4);
|
|
|
|
group_by_vals.emplace_back(7);
|
|
|
|
group_by_vals.emplace_back(7.3);
|
|
|
|
group_by_vals.emplace_back(7.2);
|
|
|
|
group_by_vals.emplace_back("Johhny");
|
|
|
|
group_by_vals.emplace_back("Jane");
|
|
|
|
group_by_vals.emplace_back("1");
|
|
|
|
group_by_vals.emplace_back(true);
|
|
|
|
group_by_vals.emplace_back(false);
|
Clean-up TypedValue misuse
Summary:
In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future.
After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache.
Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`.
Reviewers: teon.banek, msantl
Reviewed By: teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
|
|
|
group_by_vals.emplace_back(std::vector<PropertyValue>{1});
|
|
|
|
group_by_vals.emplace_back(std::vector<PropertyValue>{1, 2});
|
|
|
|
group_by_vals.emplace_back(std::vector<PropertyValue>{2, 1});
|
|
|
|
group_by_vals.emplace_back(PropertyValue::Null);
|
2017-04-11 21:11:48 +08:00
|
|
|
// should NOT result in another group because 7.0 == 7
|
|
|
|
group_by_vals.emplace_back(7.0);
|
|
|
|
// should NOT result in another group
|
Clean-up TypedValue misuse
Summary:
In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future.
After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache.
Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`.
Reviewers: teon.banek, msantl
Reviewed By: teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
|
|
|
group_by_vals.emplace_back(std::vector<PropertyValue>{1, 2.0});
|
2017-04-11 21:11:48 +08:00
|
|
|
|
|
|
|
// generate a lot of vertices and set props on them
|
2017-10-30 17:43:25 +08:00
|
|
|
auto prop = dba.Property("prop");
|
2017-04-11 21:11:48 +08:00
|
|
|
for (int i = 0; i < 1000; ++i)
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.InsertVertex().PropsSet(prop, group_by_vals[i % group_by_vals.size()]);
|
|
|
|
dba.AdvanceCommand();
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-11 21:11:48 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// match all nodes and perform aggregations
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_p = PROPERTY_LOOKUP("n", prop);
|
|
|
|
symbol_table[*n_p->expression_] = n.sym_;
|
|
|
|
|
|
|
|
auto produce =
|
|
|
|
MakeAggregationProduce(n.op_, symbol_table, storage, {n_p},
|
|
|
|
{Aggregation::Op::COUNT}, {n_p}, {n.sym_});
|
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
auto results = CollectProduce(produce.get(), symbol_table, dba);
|
2017-04-11 21:11:48 +08:00
|
|
|
ASSERT_EQ(results.size(), group_by_vals.size() - 2);
|
|
|
|
TypedValue::unordered_set result_group_bys;
|
|
|
|
for (const auto &row : results) {
|
|
|
|
ASSERT_EQ(2, row.size());
|
|
|
|
result_group_bys.insert(row[1]);
|
|
|
|
}
|
|
|
|
ASSERT_EQ(result_group_bys.size(), group_by_vals.size() - 2);
|
2017-04-12 21:47:55 +08:00
|
|
|
EXPECT_TRUE(
|
|
|
|
std::is_permutation(group_by_vals.begin(), group_by_vals.end() - 2,
|
|
|
|
result_group_bys.begin(), TypedValue::BoolEqual{}));
|
2017-04-11 21:11:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(QueryPlan, AggregateMultipleGroupBy) {
|
|
|
|
// in this test we have 3 different properties that have different values
|
|
|
|
// for different records and assert that we get the correct combination
|
|
|
|
// of values in our groups
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba_ptr = db.Access();
|
|
|
|
auto &dba = *dba_ptr;
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
auto prop1 = dba.Property("prop1");
|
|
|
|
auto prop2 = dba.Property("prop2");
|
|
|
|
auto prop3 = dba.Property("prop3");
|
2017-04-11 21:11:48 +08:00
|
|
|
for (int i = 0; i < 2 * 3 * 5; ++i) {
|
2017-10-30 17:43:25 +08:00
|
|
|
auto v = dba.InsertVertex();
|
2017-04-11 21:11:48 +08:00
|
|
|
v.PropsSet(prop1, (bool)(i % 2));
|
|
|
|
v.PropsSet(prop2, i % 3);
|
|
|
|
v.PropsSet(prop3, "value" + std::to_string(i % 5));
|
|
|
|
}
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.AdvanceCommand();
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-11 21:11:48 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// match all nodes and perform aggregations
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_p1 = PROPERTY_LOOKUP("n", prop1);
|
|
|
|
auto n_p2 = PROPERTY_LOOKUP("n", prop2);
|
|
|
|
auto n_p3 = PROPERTY_LOOKUP("n", prop3);
|
|
|
|
symbol_table[*n_p1->expression_] = n.sym_;
|
|
|
|
symbol_table[*n_p2->expression_] = n.sym_;
|
|
|
|
symbol_table[*n_p3->expression_] = n.sym_;
|
|
|
|
|
|
|
|
auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p1},
|
|
|
|
{Aggregation::Op::COUNT},
|
|
|
|
{n_p1, n_p2, n_p3}, {n.sym_});
|
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
auto results = CollectProduce(produce.get(), symbol_table, dba);
|
2017-04-11 21:11:48 +08:00
|
|
|
EXPECT_EQ(results.size(), 2 * 3 * 5);
|
|
|
|
}
|
|
|
|
|
2017-04-13 22:47:11 +08:00
|
|
|
TEST(QueryPlan, AggregateNoInput) {
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba = db.Access();
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-13 22:47:11 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto two = LITERAL(2);
|
|
|
|
auto output = NEXPR("two", IDENT("two"));
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table[*output->expression_] = symbol_table.CreateSymbol("two", true);
|
2017-04-13 22:47:11 +08:00
|
|
|
|
|
|
|
auto produce = MakeAggregationProduce(nullptr, symbol_table, storage, {two},
|
2017-04-28 21:14:28 +08:00
|
|
|
{Aggregation::Op::COUNT}, {}, {});
|
2018-07-26 15:08:21 +08:00
|
|
|
auto results = CollectProduce(produce.get(), symbol_table, *dba);
|
2017-04-13 22:47:11 +08:00
|
|
|
EXPECT_EQ(1, results.size());
|
|
|
|
EXPECT_EQ(1, results[0].size());
|
|
|
|
EXPECT_EQ(TypedValue::Type::Int, results[0][0].type());
|
|
|
|
EXPECT_EQ(1, results[0][0].Value<int64_t>());
|
|
|
|
}
|
|
|
|
|
2017-04-12 21:47:55 +08:00
|
|
|
TEST(QueryPlan, AggregateCountEdgeCases) {
|
|
|
|
// tests for detected bugs in the COUNT aggregation behavior
|
|
|
|
// ensure that COUNT returns correctly for
|
|
|
|
// - 0 vertices in database
|
|
|
|
// - 1 vertex in database, property not set
|
|
|
|
// - 1 vertex in database, property set
|
|
|
|
// - 2 vertices in database, property set on one
|
|
|
|
// - 2 vertices in database, property set on both
|
|
|
|
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba_ptr = db.Access();
|
|
|
|
auto &dba = *dba_ptr;
|
2017-10-30 17:43:25 +08:00
|
|
|
auto prop = dba.Property("prop");
|
2017-04-12 21:47:55 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-12 21:47:55 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_p = PROPERTY_LOOKUP("n", prop);
|
|
|
|
symbol_table[*n_p->expression_] = n.sym_;
|
|
|
|
|
|
|
|
// returns -1 when there are no results
|
|
|
|
// otherwise returns MATCH (n) RETURN count(n.prop)
|
|
|
|
auto count = [&]() {
|
|
|
|
auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p},
|
|
|
|
{Aggregation::Op::COUNT}, {}, {});
|
2017-10-30 17:43:25 +08:00
|
|
|
auto results = CollectProduce(produce.get(), symbol_table, dba);
|
2017-04-12 21:47:55 +08:00
|
|
|
if (results.size() == 0) return -1L;
|
|
|
|
EXPECT_EQ(1, results.size());
|
|
|
|
EXPECT_EQ(1, results[0].size());
|
|
|
|
EXPECT_EQ(TypedValue::Type::Int, results[0][0].type());
|
|
|
|
return results[0][0].Value<int64_t>();
|
|
|
|
};
|
|
|
|
|
|
|
|
// no vertices yet in database
|
2017-05-30 15:37:24 +08:00
|
|
|
EXPECT_EQ(0, count());
|
2017-04-12 21:47:55 +08:00
|
|
|
|
|
|
|
// one vertex, no property set
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.InsertVertex();
|
|
|
|
dba.AdvanceCommand();
|
2017-04-12 21:47:55 +08:00
|
|
|
EXPECT_EQ(0, count());
|
|
|
|
|
|
|
|
// one vertex, property set
|
2017-10-30 17:43:25 +08:00
|
|
|
for (VertexAccessor va : dba.Vertices(false)) va.PropsSet(prop, 42);
|
|
|
|
dba.AdvanceCommand();
|
2017-04-12 21:47:55 +08:00
|
|
|
EXPECT_EQ(1, count());
|
|
|
|
|
|
|
|
// two vertices, one with property set
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.InsertVertex();
|
|
|
|
dba.AdvanceCommand();
|
2017-04-12 21:47:55 +08:00
|
|
|
EXPECT_EQ(1, count());
|
|
|
|
|
|
|
|
// two vertices, both with property set
|
2017-10-30 17:43:25 +08:00
|
|
|
for (VertexAccessor va : dba.Vertices(false)) va.PropsSet(prop, 42);
|
|
|
|
dba.AdvanceCommand();
|
2017-04-12 21:47:55 +08:00
|
|
|
EXPECT_EQ(2, count());
|
|
|
|
}
|
|
|
|
|
2017-04-14 23:14:14 +08:00
|
|
|
TEST(QueryPlan, AggregateFirstValueTypes) {
|
|
|
|
// testing exceptions that get emitted by the first-value
|
|
|
|
// type check
|
|
|
|
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba_ptr = db.Access();
|
|
|
|
auto &dba = *dba_ptr;
|
2017-04-14 23:14:14 +08:00
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
auto v1 = dba.InsertVertex();
|
|
|
|
auto prop_string = dba.Property("string");
|
2017-04-14 23:14:14 +08:00
|
|
|
v1.PropsSet(prop_string, "johhny");
|
2017-10-30 17:43:25 +08:00
|
|
|
auto prop_int = dba.Property("int");
|
2017-04-14 23:14:14 +08:00
|
|
|
v1.PropsSet(prop_int, 12);
|
2017-10-30 17:43:25 +08:00
|
|
|
dba.AdvanceCommand();
|
2017-04-14 23:14:14 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-14 23:14:14 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_prop_string = PROPERTY_LOOKUP("n", prop_string);
|
|
|
|
symbol_table[*n_prop_string->expression_] = n.sym_;
|
|
|
|
auto n_prop_int = PROPERTY_LOOKUP("n", prop_int);
|
|
|
|
symbol_table[*n_prop_int->expression_] = n.sym_;
|
|
|
|
auto n_id = n_prop_string->expression_;
|
|
|
|
|
|
|
|
auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
|
|
|
|
auto produce = MakeAggregationProduce(n.op_, symbol_table, storage,
|
|
|
|
{expression}, {aggr_op}, {}, {});
|
2017-10-30 17:43:25 +08:00
|
|
|
CollectProduce(produce.get(), symbol_table, dba);
|
2017-04-14 23:14:14 +08:00
|
|
|
};
|
|
|
|
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// everything except for COUNT and COLLECT fails on a Vertex
|
2017-04-14 23:14:14 +08:00
|
|
|
aggregate(n_id, Aggregation::Op::COUNT);
|
2017-06-12 21:12:31 +08:00
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::MIN), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::MAX), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::AVG), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::SUM), QueryRuntimeException);
|
2017-04-14 23:14:14 +08:00
|
|
|
|
|
|
|
// on strings AVG and SUM fail
|
|
|
|
aggregate(n_prop_string, Aggregation::Op::COUNT);
|
|
|
|
aggregate(n_prop_string, Aggregation::Op::MIN);
|
|
|
|
aggregate(n_prop_string, Aggregation::Op::MAX);
|
2017-04-28 21:14:28 +08:00
|
|
|
EXPECT_THROW(aggregate(n_prop_string, Aggregation::Op::AVG),
|
2017-06-12 21:12:31 +08:00
|
|
|
QueryRuntimeException);
|
2017-04-28 21:14:28 +08:00
|
|
|
EXPECT_THROW(aggregate(n_prop_string, Aggregation::Op::SUM),
|
2017-06-12 21:12:31 +08:00
|
|
|
QueryRuntimeException);
|
2017-04-14 23:14:14 +08:00
|
|
|
|
|
|
|
// on ints nothing fails
|
|
|
|
aggregate(n_prop_int, Aggregation::Op::COUNT);
|
|
|
|
aggregate(n_prop_int, Aggregation::Op::MIN);
|
|
|
|
aggregate(n_prop_int, Aggregation::Op::MAX);
|
|
|
|
aggregate(n_prop_int, Aggregation::Op::AVG);
|
|
|
|
aggregate(n_prop_int, Aggregation::Op::SUM);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
aggregate(n_prop_int, Aggregation::Op::COLLECT_LIST);
|
|
|
|
aggregate(n_prop_int, Aggregation::Op::COLLECT_MAP);
|
2017-04-14 23:14:14 +08:00
|
|
|
}
|
|
|
|
|
2017-04-11 21:11:48 +08:00
|
|
|
TEST(QueryPlan, AggregateTypes) {
|
|
|
|
// testing exceptions that can get emitted by an aggregation
|
|
|
|
// does not check all combinations that can result in an exception
|
|
|
|
// (that logic is defined and tested by TypedValue)
|
|
|
|
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba_ptr = db.Access();
|
|
|
|
auto &dba = *dba_ptr;
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2017-10-30 17:43:25 +08:00
|
|
|
auto p1 = dba.Property("p1"); // has only string props
|
|
|
|
dba.InsertVertex().PropsSet(p1, "string");
|
|
|
|
dba.InsertVertex().PropsSet(p1, "str2");
|
|
|
|
auto p2 = dba.Property("p2"); // combines int and bool
|
|
|
|
dba.InsertVertex().PropsSet(p2, 42);
|
|
|
|
dba.InsertVertex().PropsSet(p2, true);
|
|
|
|
dba.AdvanceCommand();
|
2017-04-11 21:11:48 +08:00
|
|
|
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-11 21:11:48 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
auto n = MakeScanAll(storage, symbol_table, "n");
|
|
|
|
auto n_p1 = PROPERTY_LOOKUP("n", p1);
|
|
|
|
symbol_table[*n_p1->expression_] = n.sym_;
|
|
|
|
auto n_p2 = PROPERTY_LOOKUP("n", p2);
|
|
|
|
symbol_table[*n_p2->expression_] = n.sym_;
|
|
|
|
|
|
|
|
auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
|
|
|
|
auto produce = MakeAggregationProduce(n.op_, symbol_table, storage,
|
|
|
|
{expression}, {aggr_op}, {}, {});
|
2017-10-30 17:43:25 +08:00
|
|
|
CollectProduce(produce.get(), symbol_table, dba);
|
2017-04-11 21:11:48 +08:00
|
|
|
};
|
|
|
|
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// everything except for COUNT and COLLECT fails on a Vertex
|
2017-04-11 21:11:48 +08:00
|
|
|
auto n_id = n_p1->expression_;
|
|
|
|
aggregate(n_id, Aggregation::Op::COUNT);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
aggregate(n_id, Aggregation::Op::COLLECT_LIST);
|
|
|
|
aggregate(n_id, Aggregation::Op::COLLECT_MAP);
|
2017-06-12 21:12:31 +08:00
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::MIN), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::MAX), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::AVG), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_id, Aggregation::Op::SUM), QueryRuntimeException);
|
2017-04-11 21:11:48 +08:00
|
|
|
|
|
|
|
// on strings AVG and SUM fail
|
|
|
|
aggregate(n_p1, Aggregation::Op::COUNT);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
aggregate(n_p1, Aggregation::Op::COLLECT_LIST);
|
|
|
|
aggregate(n_p1, Aggregation::Op::COLLECT_MAP);
|
2017-04-11 21:11:48 +08:00
|
|
|
aggregate(n_p1, Aggregation::Op::MIN);
|
|
|
|
aggregate(n_p1, Aggregation::Op::MAX);
|
2017-06-12 21:12:31 +08:00
|
|
|
EXPECT_THROW(aggregate(n_p1, Aggregation::Op::AVG), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_p1, Aggregation::Op::SUM), QueryRuntimeException);
|
2017-04-11 21:11:48 +08:00
|
|
|
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
// combination of int and bool, everything except COUNT and COLLECT fails
|
2017-04-11 21:11:48 +08:00
|
|
|
aggregate(n_p2, Aggregation::Op::COUNT);
|
Collect Map added
Summary:
Tests are on the way. Please first comment if you're OK with this implementation, some points are discussable.
What works now:
```
bash:MEMGRAPH_ROOT/build/>./tests/manual/console 10
MG>MATCH (n) RETURN COLLECT("age_" + n.age, n.height)
+-----------------------------------------------------------------------------------------------------------------------------------+
| COLLECT("age_" + n.age, n.height) |
+-----------------------------------------------------------------------------------------------------------------------------------+
| {age_10: 176, age_13: 180, age_24: 172, age_25: 179, age_32: 123, age_33: 186, age_37: 147, age_43: 162, age_49: 126, age_6: 170} |
+-----------------------------------------------------------------------------------------------------------------------------------+
```
Reviewers: mislav.bradac, teon.banek, buda
Reviewed By: mislav.bradac, buda
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D695
2017-08-23 16:43:45 +08:00
|
|
|
aggregate(n_p2, Aggregation::Op::COLLECT_LIST);
|
|
|
|
aggregate(n_p2, Aggregation::Op::COLLECT_MAP);
|
2017-06-12 21:12:31 +08:00
|
|
|
EXPECT_THROW(aggregate(n_p2, Aggregation::Op::MIN), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_p2, Aggregation::Op::MAX), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_p2, Aggregation::Op::AVG), QueryRuntimeException);
|
|
|
|
EXPECT_THROW(aggregate(n_p2, Aggregation::Op::SUM), QueryRuntimeException);
|
2017-04-11 21:11:48 +08:00
|
|
|
}
|
2017-04-28 21:14:28 +08:00
|
|
|
|
|
|
|
TEST(QueryPlan, Unwind) {
|
2018-10-09 17:09:10 +08:00
|
|
|
database::GraphDb db;
|
2018-07-26 15:08:21 +08:00
|
|
|
auto dba = db.Access();
|
2018-05-22 22:45:52 +08:00
|
|
|
AstStorage storage;
|
2017-04-28 21:14:28 +08:00
|
|
|
SymbolTable symbol_table;
|
|
|
|
|
|
|
|
// UNWIND [ [1, true, "x"], [], ["bla"] ] AS x UNWIND x as y RETURN x, y
|
Clean-up TypedValue misuse
Summary:
In a bunch of places `TypedValue` was used where `PropertyValue` should be. A lot of times it was only because `TypedValue` serialization code could be reused for `PropertyValue`, only without providing callbacks for `VERTEX`, `EDGE` and `PATH`. So first I wrote separate serialization code for `PropertyValue` and put it into storage folder. Then I fixed all the places where `TypedValue` was incorrectly used instead of `PropertyValue`. I also disabled implicit `TypedValue` to `PropertyValue` conversion in hopes of preventing misuse in the future.
After that, I wrote code for `VertexAccessor` and `EdgeAccessor` serialization and put it into `storage` folder because it was almost duplicated in distributed BFS and pull produce RPC messages. On the sender side, some subset of records (old or new or both) is serialized, and on the reciever side, records are deserialized and immediately put into transaction cache.
Then I rewrote the `TypedValue` serialization functions (`SaveCapnpTypedValue` and `LoadCapnpTypedValue`) to not take callbacks for `VERTEX`, `EDGE` and `PATH`, but use accessor serialization functions instead. That means that any code that wants to use `TypedValue` serialization must hold a reference to `GraphDbAccessor` and `DataManager`, so that should make clients reconsider if they really want to use `TypedValue` instead of `PropertyValue`.
Reviewers: teon.banek, msantl
Reviewed By: teon.banek
Subscribers: pullbot
Differential Revision: https://phabricator.memgraph.io/D1598
2018-09-13 18:12:07 +08:00
|
|
|
auto input_expr = storage.Create<PrimitiveLiteral>(std::vector<PropertyValue>{
|
|
|
|
std::vector<PropertyValue>{1, true, "x"}, std::vector<PropertyValue>{},
|
|
|
|
std::vector<PropertyValue>{"bla"}});
|
2017-04-28 21:14:28 +08:00
|
|
|
|
2017-05-12 17:37:22 +08:00
|
|
|
auto x = symbol_table.CreateSymbol("x", true);
|
2017-05-03 17:07:07 +08:00
|
|
|
auto unwind_0 = std::make_shared<plan::Unwind>(nullptr, input_expr, x);
|
2017-04-28 21:14:28 +08:00
|
|
|
auto x_expr = IDENT("x");
|
|
|
|
symbol_table[*x_expr] = x;
|
2017-05-12 17:37:22 +08:00
|
|
|
auto y = symbol_table.CreateSymbol("y", true);
|
2017-05-03 17:07:07 +08:00
|
|
|
auto unwind_1 = std::make_shared<plan::Unwind>(unwind_0, x_expr, y);
|
2017-04-28 21:14:28 +08:00
|
|
|
|
|
|
|
auto x_ne = NEXPR("x", x_expr);
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table[*x_ne] = symbol_table.CreateSymbol("x_ne", true);
|
2017-04-28 21:14:28 +08:00
|
|
|
auto y_ne = NEXPR("y", IDENT("y"));
|
|
|
|
symbol_table[*y_ne->expression_] = y;
|
2017-05-12 17:37:22 +08:00
|
|
|
symbol_table[*y_ne] = symbol_table.CreateSymbol("y_ne", true);
|
2017-04-28 21:14:28 +08:00
|
|
|
auto produce = MakeProduce(unwind_1, x_ne, y_ne);
|
|
|
|
|
2018-07-26 15:08:21 +08:00
|
|
|
auto results = CollectProduce(produce.get(), symbol_table, *dba);
|
2017-04-28 21:14:28 +08:00
|
|
|
ASSERT_EQ(4, results.size());
|
|
|
|
const std::vector<int> expected_x_card{3, 3, 3, 1};
|
|
|
|
auto expected_x_card_it = expected_x_card.begin();
|
|
|
|
const std::vector<TypedValue> expected_y{1, true, "x", "bla"};
|
|
|
|
auto expected_y_it = expected_y.begin();
|
|
|
|
for (const auto &row : results) {
|
|
|
|
ASSERT_EQ(2, row.size());
|
|
|
|
ASSERT_EQ(row[0].type(), TypedValue::Type::List);
|
|
|
|
EXPECT_EQ(row[0].Value<std::vector<TypedValue>>().size(),
|
|
|
|
*expected_x_card_it);
|
|
|
|
EXPECT_EQ(row[1].type(), expected_y_it->type());
|
|
|
|
expected_x_card_it++;
|
|
|
|
expected_y_it++;
|
|
|
|
}
|
|
|
|
}
|