Implement Range function

Reviewers: teon.banek, buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D358
This commit is contained in:
Mislav Bradac 2017-05-10 15:17:43 +02:00
parent aeacdfc631
commit f82bda6c0c
2 changed files with 66 additions and 0 deletions

View File

@ -282,6 +282,39 @@ TypedValue Labels(const std::vector<TypedValue> &args,
}
}
TypedValue Range(const std::vector<TypedValue> &args, GraphDbAccessor &) {
if (args.size() != 2U && args.size() != 3U) {
throw QueryRuntimeException("range requires two or three arguments");
}
bool has_null = false;
auto check_type = [&](const TypedValue &t) {
if (t.IsNull()) {
has_null = true;
} else if (t.type() != TypedValue::Type::Int) {
throw QueryRuntimeException("range called with incompatible type");
}
};
std::for_each(args.begin(), args.end(), check_type);
if (has_null) return TypedValue::Null;
auto lbound = args[0].Value<int64_t>();
auto rbound = args[1].Value<int64_t>();
int64_t step = args.size() == 3U ? args[2].Value<int64_t>() : 1;
if (step == 0) {
throw QueryRuntimeException("step argument in range can't be zero");
}
std::vector<TypedValue> list;
if (lbound <= rbound && step > 0) {
for (auto i = lbound; i <= rbound; i += step) {
list.push_back(i);
}
} else if (lbound >= rbound && step < 0) {
for (auto i = lbound; i >= rbound; i += step) {
list.push_back(i);
}
}
return list;
}
TypedValue Tail(const std::vector<TypedValue> &args, GraphDbAccessor &) {
if (args.size() != 1U) {
throw QueryRuntimeException("tail requires one argument");
@ -421,6 +454,7 @@ NameToFunction(const std::string &function_name) {
if (function_name == "TYPE") return Type;
if (function_name == "KEYS") return Keys;
if (function_name == "LABELS") return Labels;
if (function_name == "RANGE") return Range;
if (function_name == "TAIL") return Tail;
if (function_name == "ABS") return Abs;
if (function_name == "CEIL") return Ceil;

View File

@ -820,6 +820,38 @@ TEST(ExpressionEvaluator, FunctionLabels) {
ASSERT_THROW(EvaluateFunction("LABELS", {2}), QueryRuntimeException);
}
TEST(ExpressionEvaluator, FunctionRange) {
EXPECT_THROW(EvaluateFunction("RANGE", {}), QueryRuntimeException);
EXPECT_TRUE(EvaluateFunction("RANGE", {1, 2, TypedValue::Null}).IsNull());
EXPECT_THROW(EvaluateFunction("RANGE", {1, TypedValue::Null, 1.3}),
QueryRuntimeException);
auto to_int_list = [](const TypedValue &t) {
std::vector<int64_t> list;
for (auto x : t.Value<std::vector<TypedValue>>()) {
list.push_back(x.Value<int64_t>());
}
return list;
};
EXPECT_THROW(EvaluateFunction("RANGE", {1, 2, 0}), QueryRuntimeException);
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {1, 3})),
ElementsAre(1, 2, 3));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {-1, 5, 2})),
ElementsAre(-1, 1, 3, 5));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {2, 10, 3})),
ElementsAre(2, 5, 8));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {2, 2, 2})),
ElementsAre(2));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {3, 0, 5})), ElementsAre());
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {5, 1, -2})),
ElementsAre(5, 3, 1));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {6, 1, -2})),
ElementsAre(6, 4, 2));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {2, 2, -3})),
ElementsAre(2));
EXPECT_THAT(to_int_list(EvaluateFunction("RANGE", {-2, 4, -1})),
ElementsAre());
}
TEST(ExpressionEvaluator, FunctionKeys) {
ASSERT_THROW(EvaluateFunction("KEYS", {}), QueryRuntimeException);
ASSERT_EQ(EvaluateFunction("KEYS", {TypedValue::Null}).type(),