Skip to content

Commit

Permalink
Added some built-in functions for string operations (#841)
Browse files Browse the repository at this point in the history
  • Loading branch information
yaphet authored and dutor committed Sep 2, 2019
1 parent e4cb9c4 commit 3c0b36f
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 0 deletions.
159 changes: 159 additions & 0 deletions src/common/filter/FunctionManager.cpp
Expand Up @@ -243,6 +243,165 @@ FunctionManager::FunctionManager() {
return static_cast<int64_t>(::strcasecmp(left.c_str(), right.c_str()));
};
}
{
auto &attr = functions_["lower"];
attr.minArity_ = 1;
attr.maxArity_ = 1;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c){ return std::tolower(c);});
return value;
};
}
{
auto &attr = functions_["upper"];
attr.minArity_ = 1;
attr.maxArity_ = 1;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c){ return std::toupper(c);});
return value;
};
}
{
auto &attr = functions_["length"];
attr.minArity_ = 1;
attr.maxArity_ = 1;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
return static_cast<int64_t>(value.length());
};
}
{
auto &attr = functions_["trim"];
attr.minArity_ = 1;
attr.maxArity_ = 1;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
value.erase(0, value.find_first_not_of(" "));
value.erase(value.find_last_not_of(" ") + 1);
return value;
};
}
{
auto &attr = functions_["ltrim"];
attr.minArity_ = 1;
attr.maxArity_ = 1;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
value.erase(0, value.find_first_not_of(" "));
return value;
};
}
{
auto &attr = functions_["rtrim"];
attr.minArity_ = 1;
attr.maxArity_ = 1;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
value.erase(value.find_last_not_of(" ") + 1);
return value;
};
}
{
auto &attr = functions_["left"];
attr.minArity_ = 2;
attr.maxArity_ = 2;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
auto length = Expression::asInt(args[1]);
if (length < 0) {
length = 0;
}
return value.substr(0, length);
};
}
{
auto &attr = functions_["right"];
attr.minArity_ = 2;
attr.maxArity_ = 2;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
auto length = Expression::asInt(args[1]);
if (length <= 0) {
length = 0;
}
return value.substr(value.size() - length);
};
}
{
auto &attr = functions_["lpad"];
attr.minArity_ = 3;
attr.maxArity_ = 3;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
size_t size = Expression::asInt(args[1]);

if (size < 0) {
return std::string("");
} else if (size < value.size()) {
return value.substr(0, static_cast<int32_t>(size));
} else {
auto extra = Expression::asString(args[2]);
size -= value.size();
std::stringstream stream;
while (size > extra.size()) {
stream << extra;
size -= extra.size();
}
stream << extra.substr(0, size);
stream << value;
return stream.str();
}
};
}
{
auto &attr = functions_["rpad"];
attr.minArity_ = 3;
attr.maxArity_ = 3;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
size_t size = Expression::asInt(args[1]);
if (size < 0) {
return std::string("");
} else if (size < value.size()) {
return value.substr(0, static_cast<int32_t>(size));
} else {
auto extra = Expression::asString(args[2]);
std::stringstream stream;
stream << value;
size -= value.size();
while (size > extra.size()) {
stream << extra;
size -= extra.size();
}
stream << extra.substr(0, size);
return stream.str();
}
};
}
{
auto &attr = functions_["substr"];
attr.minArity_ = 3;
attr.maxArity_ = 3;
attr.body_ = [] (const auto &args) {
auto value = Expression::asString(args[0]);
auto start = Expression::asInt(args[1]);
auto length = Expression::asInt(args[2]);
if (static_cast<size_t>(std::abs(start)) > value.size() ||
length <= 0 || start == 0) {
return std::string("");
}

if (start > 0) {
return value.substr(start - 1, length);
} else {
return value.substr(value.size() + start, length);
}
};
}
{
// 64bit signed hash value
auto &attr = functions_["hash"];
Expand Down
83 changes: 83 additions & 0 deletions src/common/filter/test/ExpressionTest.cpp
Expand Up @@ -615,6 +615,89 @@ TEST_F(ExpressionTest, FunctionCall) {
TEST_EXPR(0, LT, strcasecmp("HelLo", "hell"), Int);
TEST_EXPR(0, GT, strcasecmp("HelLo", "World"), Int);

TEST_EXPR(5, EQ, length("hello"), Int);
TEST_EXPR(0, EQ, length(""), Int);

#undef TEST_EXPR
}

TEST_F(ExpressionTest, StringFunctionCall) {
GQLParser parser;
#define TEST_EXPR(expected, op, expr_arg, type) \
do { \
std::string query = "GO FROM 1 OVER follow WHERE " #expr_arg; \
auto parsed = parser.parse(query); \
ASSERT_TRUE(parsed.ok()) << parsed.status(); \
auto *expr = getFilterExpr(parsed.value().get()); \
ASSERT_NE(nullptr, expr); \
auto decoded = Expression::decode(Expression::encode(expr)); \
ASSERT_TRUE(decoded.ok()) << decoded.status(); \
auto ctx = std::make_unique<ExpressionContext>(); \
decoded.value()->setContext(ctx.get()); \
auto status = decoded.value()->prepare(); \
ASSERT_TRUE(status.ok()) << status; \
auto value = decoded.value()->eval(); \
ASSERT_TRUE(value.ok()); \
auto v = value.value(); \
ASSERT_TRUE(Expression::is##type(v)); \
if (#type == std::string("String")) { \
if (#op != std::string("EQ")) { \
ASSERT_##op(expected, Expression::as##type(v)); \
} else { \
ASSERT_EQ(expected, Expression::as##type(v)); \
} \
} else { \
ASSERT_##op(expected, Expression::as##type(v)); \
} \
} while (false)

TEST_EXPR("hello", EQ, lower("HelLo"), String);
TEST_EXPR("hello", EQ, lower("HELLO"), String);
TEST_EXPR("hello", EQ, lower("hello"), String);

TEST_EXPR("HELLO", EQ, upper("HelLo"), String);
TEST_EXPR("HELLO", EQ, upper("HELLO"), String);
TEST_EXPR("HELLO", EQ, upper("hello"), String);

TEST_EXPR("hello", EQ, trim(" hello "), String);
TEST_EXPR("hello", EQ, trim(" hello"), String);
TEST_EXPR("hello", EQ, trim("hello "), String);

TEST_EXPR("hello ", EQ, ltrim(" hello "), String);
TEST_EXPR("hello", EQ, ltrim(" hello"), String);
TEST_EXPR("hello ", EQ, ltrim("hello "), String);

TEST_EXPR(" hello", EQ, rtrim(" hello "), String);
TEST_EXPR(" hello", EQ, rtrim(" hello"), String);
TEST_EXPR("hello", EQ, rtrim("hello "), String);

TEST_EXPR("hello", EQ, left("hello world", 5), String);
TEST_EXPR("", EQ, left("hello world", 0), String);
TEST_EXPR("", EQ, left("hello world", -1), String);

TEST_EXPR("world", EQ, right("hello world", 5), String);
TEST_EXPR("", EQ, right("hello world", 0), String);
TEST_EXPR("", EQ, right("hello world", -1), String);

TEST_EXPR("111Hello", EQ, lpad("Hello", 8, "1"), String);
TEST_EXPR("wewHello", EQ, lpad("Hello", 8, "we"), String);
TEST_EXPR("Hell", EQ, lpad("Hello", 4, "1"), String);
TEST_EXPR("", EQ, lpad("Hello", 0, "1"), String);

TEST_EXPR("Hello111", EQ, rpad("Hello", 8, "1"), String);
TEST_EXPR("Hellowew", EQ, rpad("Hello", 8, "we"), String);
TEST_EXPR("Hell", EQ, rpad("Hello", 4, "1"), String);
TEST_EXPR("", EQ, rpad("Hello", 0, "1"), String);

TEST_EXPR("1", EQ, substr("123", 1, 1), String);
TEST_EXPR("", EQ, substr("123", 1, 0), String);
TEST_EXPR("", EQ, substr("123", 1, -1), String);
TEST_EXPR("3", EQ, substr("123", -1, 1), String);
TEST_EXPR("", EQ, substr("123", -1, 0), String);
TEST_EXPR("", EQ, substr("123", -1, -1), String);
TEST_EXPR("", EQ, substr("123", 5, 1), String);
TEST_EXPR("", EQ, substr("123", -5, 1), String);

#undef TEST_EXPR
}

Expand Down

0 comments on commit 3c0b36f

Please sign in to comment.