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;

Reply via email to