Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add concat & concat_ws #2540

Merged
merged 4 commits into from
Aug 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/common/function/FunctionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ FunctionManager &FunctionManager::instance() {
return instance;
}

std::unordered_map<std::string, Value::Type> FunctionManager::variadicFunReturnType_ = {
{"concat", Value::Type::STRING},
{"concat_ws", Value::Type::STRING},
{"cos_similarity", Value::Type::FLOAT},
};

std::unordered_map<std::string, std::vector<TypeSignature>> FunctionManager::typeSignature_ = {
{"abs",
{TypeSignature({Value::Type::INT}, Value::Type::INT),
Expand Down Expand Up @@ -303,6 +309,9 @@ StatusOr<Value::Type> FunctionManager::getReturnType(const std::string &funcName
const std::vector<Value::Type> &argsType) {
auto func = funcName;
std::transform(func.begin(), func.end(), func.begin(), ::tolower);
if (variadicFunReturnType_.find(func) != variadicFunReturnType_.end()) {
return variadicFunReturnType_[func];
}
auto iter = typeSignature_.find(func);
if (iter == typeSignature_.end()) {
return Status::Error("Function `%s' not defined", funcName.c_str());
Expand Down Expand Up @@ -2145,6 +2154,91 @@ FunctionManager::FunctionManager() {
return ds.rows[rowIndex][colIndex];
};
}
{
auto &attr = functions_["concat"];
attr.minArity_ = 1;
attr.maxArity_ = INT64_MAX;
attr.isPure_ = true;
attr.body_ = [](const auto &args) -> Value {
std::stringstream os;
yixinglu marked this conversation as resolved.
Show resolved Hide resolved
for (size_t i = 0; i < args.size(); ++i) {
switch (args[i].get().type()) {
case Value::Type::NULLVALUE: {
return Value::kNullValue;
}
case Value::Type::BOOL: {
os << (args[i].get().getBool() ? "true" : "false");
break;
}
case Value::Type::INT: {
os << args[i].get().getInt();
break;
}
case Value::Type::FLOAT: {
os << args[i].get().getFloat();
break;
}
case Value::Type::STRING: {
os << args[i].get().getStr();
break;
}
case Value::Type::DATETIME: {
os << args[i].get().getDateTime();
break;
}
case Value::Type::DATE: {
os << args[i].get().getDate();
break;
}
case Value::Type::TIME: {
os << args[i].get().getTime();
break;
}
default: {
return Value::kNullBadData;
}
}
}
return os.str();
};
}
{
auto &attr = functions_["concat_ws"];
attr.minArity_ = 2;
attr.maxArity_ = INT64_MAX;
attr.isPure_ = true;
attr.body_ = [](const auto &args) -> Value {
if (args[0].get().isNull() || !args[0].get().isStr()) {
return Value::kNullValue;
}
std::vector<std::string> result;
result.reserve(args.size() - 1);
for (size_t i = 1; i < args.size(); ++i) {
switch (args[i].get().type()) {
case Value::Type::NULLVALUE: {
continue;
}
case Value::Type::BOOL:
case Value::Type::INT:
case Value::Type::FLOAT:
case Value::Type::DATE:
case Value::Type::DATETIME:
case Value::Type::TIME: {
result.emplace_back(args[i].get().toString());
break;
}
case Value::Type::STRING: {
result.emplace_back(args[i].get().getStr());
break;
}
default: {
return Value::kNullBadData;
}
}
}
return folly::join(args[0].get().getStr(), result);
};
}
} // NOLINT

// static
Expand Down
3 changes: 3 additions & 0 deletions src/common/function/FunctionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class FunctionManager final {

static std::unordered_map<std::string, std::vector<TypeSignature>> typeSignature_;

// the return type of a variadic function
static std::unordered_map<std::string, Value::Type> variadicFunReturnType_;

std::unordered_map<std::string, FunctionAttributes> functions_;
};

Expand Down
22 changes: 22 additions & 0 deletions src/common/function/test/FunctionManagerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ TEST_F(FunctionManagerTest, testNull) {
TEST_FUNCTION(substr, std::vector<Value>({"hello", 2, Value::kNullValue}), Value::kNullBadType);
TEST_FUNCTION(substr, std::vector<Value>({"hello", -1, 10}), Value::kNullBadData);
TEST_FUNCTION(substr, std::vector<Value>({"hello", 1, -2}), Value::kNullBadData);
TEST_FUNCTION(concat, std::vector<Value>({"hello", Value::kNullValue, -2}), Value::kNullValue);
TEST_FUNCTION(concat, args_["nullvalue"], Value::kNullValue);
TEST_FUNCTION(concat_ws, std::vector<Value>({Value::kNullValue, 1, 2}), Value::kNullValue);
TEST_FUNCTION(concat_ws, std::vector<Value>({1, 1, 2}), Value::kNullValue);
}

TEST_F(FunctionManagerTest, functionCall) {
Expand Down Expand Up @@ -308,6 +312,24 @@ TEST_F(FunctionManagerTest, functionCall) {
TEST_FUNCTION(toString, args_["datetime"], "1984-10-11T12:31:14.341");
TEST_FUNCTION(toString, args_["nullvalue"], Value::kNullValue);
}
{
Time time(9, 39, 21, 12);
Date date(2021, 10, 31);
DateTime dateTime(2021, 10, 31, 8, 5, 34, 29);
TEST_FUNCTION(concat, std::vector<Value>({"hello", 1, "world"}), "hello1world");
TEST_FUNCTION(concat, std::vector<Value>({true, 2, date}), "true22021-10-31");
TEST_FUNCTION(concat, std::vector<Value>({true, dateTime}), "true2021-10-31T08:05:34.29");
TEST_FUNCTION(concat, std::vector<Value>({2.3, time}), "2.309:39:21.000012");
TEST_FUNCTION(concat, args_["two"], "24");
TEST_FUNCTION(concat_ws, std::vector<Value>({",", 1}), "1");
TEST_FUNCTION(concat_ws, std::vector<Value>({"@", 1, "world"}), "1@world");
TEST_FUNCTION(concat_ws,
std::vector<Value>({"AB", 1, true, Value::kNullValue, "world"}),
"1ABtrueABworld");
TEST_FUNCTION(concat_ws,
std::vector<Value>({".", 1, true, Value::kNullValue, "world", time}),
"1.true.world.09:39:21.000012");
}
{
TEST_FUNCTION(toBoolean, args_["int"], Value::kNullBadType);
TEST_FUNCTION(toBoolean, args_["float"], Value::kNullBadType);
Expand Down
85 changes: 83 additions & 2 deletions tests/tck/features/expression/FunctionCall.feature
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ Feature: Function Call Expression
Scenario: date related
When executing query:
"""
YIELD timestamp("2000-10-10T10:00:00") AS a, date() AS b, time() AS c,
datetime() AS d
YIELD timestamp("2000-10-10T10:00:00") AS a, date() AS b, time() AS c, datetime() AS d
"""
Then the result should be, in any order:
| a | b | c | d |
Expand All @@ -40,3 +39,85 @@ Feature: Function Call Expression
Then the result should be, in any order:
| dt |
| '2019-03-02T22:00:30.000000' |

Scenario: concat
When executing query:
"""
GO FROM "Tim Duncan" over like YIELD concat(like._src, $^.player.age, $$.player.name, like.likeness) AS A
"""
Then the result should be, in any order:
| A |
| "Tim Duncan42Manu Ginobili95" |
| "Tim Duncan42Tony Parker95" |
When executing query:
"""
GO FROM "Tim Duncan" over like YIELD concat(like._src, $^.player.age, null, like.likeness) AS A
"""
Then the result should be, in any order:
| A |
| NULL |
| NULL |
When executing query:
"""
MATCH (a:player)-[b:serve]-(c:team{name: "Lakers"})
WHERE a.age > 45
RETURN concat(a.name,c.name)
"""
Then the result should be, in any order:
| concat(a.name,c.name) |
| "Shaquile O'NealLakers" |
When executing query:
"""
MATCH (a:player)-[b:serve]-(c:team{name: "Lakers"})
WHERE a.age > 45
RETURN concat(a.name, "hello")
"""
Then the result should be, in any order:
| concat(a.name,"hello") |
| "Shaquile O'Nealhello" |

Scenario: concat_ws
When executing query:
"""
GO FROM "Tim Duncan" over like YIELD concat_ws("-",like._src, $^.player.age, $$.player.name, like.likeness) AS A
"""
Then the result should be, in any order:
| A |
| "Tim Duncan-42-Manu Ginobili-95" |
| "Tim Duncan-42-Tony Parker-95" |
When executing query:
"""
MATCH (a:player)-[b:serve]-(c:team{name: "Lakers"})
WHERE a.age > 45
RETURN concat_ws("@",a.name, "hello", b.likeness, c.name) as result
"""
Then the result should be, in any order:
| result |
| "Shaquile O'Neal@hello@Lakers" |
When executing query:
"""
MATCH (a:player)-[b:serve]-(c:team{name: "Lakers"})
WHERE a.age > 45
RETURN concat_ws("@",a.name, NULL, "hello", b.likeness, c.name) as result
"""
Then the result should be, in any order:
| result |
| "Shaquile O'Neal@hello@Lakers" |
When executing query:
"""
MATCH (a:player)-[b:serve]-(c:team{name: "Lakers"})
WHERE a.age > 45
RETURN concat_ws(1,a.name, NULL, "hello", b.likeness, c.name) as result
"""
Then the result should be, in any order:
| result |
| NULL |
When executing query:
"""
MATCH (a:player)-[b:serve]-(c:team{name: "Lakers"})
WHERE a.age > 45
RETURN concat_ws(NULL ,a.name, NULL, "hello", b.likeness, c.name) as result
"""
Then the result should be, in any order:
| result |
| NULL |