This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 8805c9a8 feat(search): add support of command FT.EXPLAIN/EXPLAINSQL
(#2357)
8805c9a8 is described below
commit 8805c9a83cdb98dee72652187734252fa64b6ecc
Author: Twice <[email protected]>
AuthorDate: Sat Jun 8 13:56:29 2024 +0900
feat(search): add support of command FT.EXPLAIN/EXPLAINSQL (#2357)
---
src/commands/cmd_search.cc | 109 ++++++++++++++++++++++++++++++---------------
src/search/index_manager.h | 17 ++++++-
2 files changed, 89 insertions(+), 37 deletions(-)
diff --git a/src/commands/cmd_search.cc b/src/commands/cmd_search.cc
index a74f69a6..14569477 100644
--- a/src/commands/cmd_search.cc
+++ b/src/commands/cmd_search.cc
@@ -152,6 +152,20 @@ static void DumpQueryResult(const
std::vector<kqir::ExecutorContext::RowType> &r
}
}
+class CommandFTExplainSQL : public Commander {
+ Status Execute(Server *srv, Connection *conn, std::string *output) override {
+ const auto &sql = args_[1];
+
+ auto ir = GET_OR_RET(kqir::sql::ParseToIR(kqir::peg::string_input(sql,
"ft.explainsql")));
+
+ auto result = GET_OR_RET(srv->index_mgr.Explain(std::move(ir),
conn->GetNamespace()));
+
+ output->append(BulkString(result));
+
+ return Status::OK();
+ };
+};
+
class CommandFTSearchSQL : public Commander {
Status Execute(Server *srv, Connection *conn, std::string *output) override {
const auto &sql = args_[1];
@@ -166,50 +180,73 @@ class CommandFTSearchSQL : public Commander {
};
};
-class CommandFTSearch : public Commander {
- Status Parse(const std::vector<std::string> &args) override {
- CommandParser parser(args, 1);
+static StatusOr<std::unique_ptr<kqir::Node>> ParseRediSearchQuery(const
std::vector<std::string> &args) {
+ CommandParser parser(args, 1);
- auto index_name = GET_OR_RET(parser.TakeStr());
- auto query_str = GET_OR_RET(parser.TakeStr());
+ auto index_name = GET_OR_RET(parser.TakeStr());
+ auto query_str = GET_OR_RET(parser.TakeStr());
- auto index_ref = std::make_unique<kqir::IndexRef>(index_name);
- auto query = kqir::Node::MustAs<kqir::QueryExpr>(
-
GET_OR_RET(kqir::redis_query::ParseToIR(kqir::peg::string_input(query_str,
"ft.search"))));
+ auto index_ref = std::make_unique<kqir::IndexRef>(index_name);
+ auto query = kqir::Node::MustAs<kqir::QueryExpr>(
+
GET_OR_RET(kqir::redis_query::ParseToIR(kqir::peg::string_input(query_str,
"ft.search"))));
- auto select =
std::make_unique<kqir::SelectClause>(std::vector<std::unique_ptr<kqir::FieldRef>>{});
- std::unique_ptr<kqir::SortByClause> sort_by;
- std::unique_ptr<kqir::LimitClause> limit;
- while (parser.Good()) {
- if (parser.EatEqICase("RETURNS")) {
- auto count = GET_OR_RET(parser.TakeInt<size_t>());
+ auto select =
std::make_unique<kqir::SelectClause>(std::vector<std::unique_ptr<kqir::FieldRef>>{});
+ std::unique_ptr<kqir::SortByClause> sort_by;
+ std::unique_ptr<kqir::LimitClause> limit;
+ while (parser.Good()) {
+ if (parser.EatEqICase("RETURNS")) {
+ auto count = GET_OR_RET(parser.TakeInt<size_t>());
- for (size_t i = 0; i < count; ++i) {
- auto field = GET_OR_RET(parser.TakeStr());
- select->fields.push_back(std::make_unique<kqir::FieldRef>(field));
- }
- } else if (parser.EatEqICase("SORTBY")) {
+ for (size_t i = 0; i < count; ++i) {
auto field = GET_OR_RET(parser.TakeStr());
- auto order = kqir::SortByClause::ASC;
- if (parser.EatEqICase("ASC")) {
- // NOOP
- } else if (parser.EatEqICase("DESC")) {
- order = kqir::SortByClause::DESC;
- }
+ select->fields.push_back(std::make_unique<kqir::FieldRef>(field));
+ }
+ } else if (parser.EatEqICase("SORTBY")) {
+ auto field = GET_OR_RET(parser.TakeStr());
+ auto order = kqir::SortByClause::ASC;
+ if (parser.EatEqICase("ASC")) {
+ // NOOP
+ } else if (parser.EatEqICase("DESC")) {
+ order = kqir::SortByClause::DESC;
+ }
- sort_by = std::make_unique<kqir::SortByClause>(order,
std::make_unique<kqir::FieldRef>(field));
- } else if (parser.EatEqICase("LIMIT")) {
- auto offset = GET_OR_RET(parser.TakeInt<size_t>());
- auto count = GET_OR_RET(parser.TakeInt<size_t>());
+ sort_by = std::make_unique<kqir::SortByClause>(order,
std::make_unique<kqir::FieldRef>(field));
+ } else if (parser.EatEqICase("LIMIT")) {
+ auto offset = GET_OR_RET(parser.TakeInt<size_t>());
+ auto count = GET_OR_RET(parser.TakeInt<size_t>());
- limit = std::make_unique<kqir::LimitClause>(offset, count);
- } else {
- return parser.InvalidSyntax();
- }
+ limit = std::make_unique<kqir::LimitClause>(offset, count);
+ } else {
+ return parser.InvalidSyntax();
}
+ }
+
+ return std::make_unique<kqir::SearchExpr>(std::move(index_ref),
std::move(query), std::move(limit),
+ std::move(sort_by),
std::move(select));
+}
+
+class CommandFTExplain : public Commander {
+ Status Parse(const std::vector<std::string> &args) override {
+ ir_ = GET_OR_RET(ParseRediSearchQuery(args));
+ return Status::OK();
+ }
+
+ Status Execute(Server *srv, Connection *conn, std::string *output) override {
+ CHECK(ir_);
+ auto result = GET_OR_RET(srv->index_mgr.Explain(std::move(ir_),
conn->GetNamespace()));
- ir_ = std::make_unique<kqir::SearchExpr>(std::move(index_ref),
std::move(query), std::move(limit),
- std::move(sort_by),
std::move(select));
+ output->append(redis::SimpleString(result));
+
+ return Status::OK();
+ };
+
+ private:
+ std::unique_ptr<kqir::Node> ir_;
+};
+
+class CommandFTSearch : public Commander {
+ Status Parse(const std::vector<std::string> &args) override {
+ ir_ = GET_OR_RET(ParseRediSearchQuery(args));
return Status::OK();
}
@@ -293,6 +330,8 @@ class CommandFTDrop : public Commander {
REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandFTCreate>("ft.create", -2, "write
exclusive no-multi no-script", 0, 0, 0),
MakeCmdAttr<CommandFTSearchSQL>("ft.searchsql", 2,
"read-only", 0, 0, 0),
MakeCmdAttr<CommandFTSearch>("ft.search", -3,
"read-only", 0, 0, 0),
+ MakeCmdAttr<CommandFTExplainSQL>("ft.explainsql", 2,
"read-only", 0, 0, 0),
+ MakeCmdAttr<CommandFTExplain>("ft.explain", -3,
"read-only", 0, 0, 0),
MakeCmdAttr<CommandFTInfo>("ft.info", 2, "read-only",
0, 0, 0),
MakeCmdAttr<CommandFTList>("ft._list", 1, "read-only",
0, 0, 0),
MakeCmdAttr<CommandFTDrop>("ft.dropindex", 2, "write
exclusive no-multi no-script", 0, 0, 0));
diff --git a/src/search/index_manager.h b/src/search/index_manager.h
index 32e1a275..0d76765a 100644
--- a/src/search/index_manager.h
+++ b/src/search/index_manager.h
@@ -178,8 +178,8 @@ struct IndexManager {
return Status::OK();
}
- StatusOr<std::vector<kqir::ExecutorContext::RowType>>
Search(std::unique_ptr<kqir::Node> ir,
- const
std::string &ns) const {
+ StatusOr<std::unique_ptr<kqir::PlanOperator>>
GeneratePlan(std::unique_ptr<kqir::Node> ir,
+ const std::string
&ns) const {
kqir::SemaChecker sema_checker(index_map);
sema_checker.ns = ns;
@@ -191,6 +191,19 @@ struct IndexManager {
return {Status::NotOK, "failed to convert the query to plan operators"};
}
+ return plan_op;
+ }
+
+ StatusOr<std::string> Explain(std::unique_ptr<kqir::Node> ir, const
std::string &ns) const {
+ auto plan_op = GET_OR_RET(GeneratePlan(std::move(ir), ns));
+
+ return plan_op->Dump();
+ }
+
+ StatusOr<std::vector<kqir::ExecutorContext::RowType>>
Search(std::unique_ptr<kqir::Node> ir,
+ const
std::string &ns) const {
+ auto plan_op = GET_OR_RET(GeneratePlan(std::move(ir), ns));
+
kqir::ExecutorContext executor_ctx(plan_op.get(), storage);
std::vector<kqir::ExecutorContext::RowType> results;