diff --git a/src/parser/AdminSentences.cpp b/src/parser/AdminSentences.cpp
index d00285aee4f666b6d33d01ea1ec1dddf409da1f6..b138d57f9bc53c73667caee1cc1e0b26da667480 100644
--- a/src/parser/AdminSentences.cpp
+++ b/src/parser/AdminSentences.cpp
@@ -174,16 +174,39 @@ std::string ShowListenerSentence::toString() const {
 
 std::string AdminJobSentence::toString() const {
     switch (op_) {
-    case meta::cpp2::AdminJobOp::ADD:
-        return "add job";
-    case meta::cpp2::AdminJobOp::SHOW_All:
-        return "show jobs";
-    case meta::cpp2::AdminJobOp::SHOW:
-        return "show job";
-    case meta::cpp2::AdminJobOp::STOP:
-        return "stop job";
-    case meta::cpp2::AdminJobOp::RECOVER:
-        return "recover job";
+        case meta::cpp2::AdminJobOp::ADD: {
+            switch (cmd_) {
+                case meta::cpp2::AdminCmd::COMPACT:
+                    return paras_.empty() ? "SUBMIT JOB COMPACT"
+                        : folly::stringPrintf("SUBMIT JOB COMPACT %s", paras_[0].c_str());
+                case meta::cpp2::AdminCmd::FLUSH:
+                    return paras_.empty() ? "SUBMIT JOB FLUSH"
+                        : folly::stringPrintf("SUBMIT JOB FLUSH %s", paras_[0].c_str());
+                case meta::cpp2::AdminCmd::REBUILD_TAG_INDEX:
+                    return folly::stringPrintf("REBUILD TAG INDEX %s",
+                            folly::join(",", paras_).c_str());
+                case meta::cpp2::AdminCmd::REBUILD_EDGE_INDEX:
+                    return folly::stringPrintf("REBUILD EDGE INDEX %s",
+                            folly::join(",", paras_).c_str());
+                case meta::cpp2::AdminCmd::STATS:
+                    return paras_.empty() ? "SUBMIT JOB STATS"
+                        : folly::stringPrintf("SUBMIT JOB STATS %s", paras_[0].c_str());
+                case meta::cpp2::AdminCmd::DATA_BALANCE:
+                case meta::cpp2::AdminCmd::UNKNOWN:
+                    return folly::stringPrintf("Unsupported AdminCmd: %s",
+                            meta::cpp2::_AdminCmd_VALUES_TO_NAMES.at(cmd_));
+            }
+        }
+        case meta::cpp2::AdminJobOp::SHOW_All:
+            return "SHOW JOBS";
+        case meta::cpp2::AdminJobOp::SHOW:
+            CHECK_EQ(paras_.size(), 1U);
+            return folly::stringPrintf("SHOW JOB %s", paras_[0].c_str());
+        case meta::cpp2::AdminJobOp::STOP:
+            CHECK_EQ(paras_.size(), 1U);
+            return folly::stringPrintf("STOP JOB %s", paras_[0].c_str());
+        case meta::cpp2::AdminJobOp::RECOVER:
+            return "RECOVER JOB";
     }
     LOG(FATAL) << "Unknown job operation " << static_cast<uint8_t>(op_);
 }
@@ -204,6 +227,13 @@ void AdminJobSentence::addPara(const std::string& para) {
     paras_.emplace_back(para);
 }
 
+void AdminJobSentence::addPara(const NameLabelList& paras) {
+    const auto& labels = paras.labels();
+    std::for_each(labels.begin(), labels.end(), [this](const auto& para) {
+        paras_.emplace_back(*para);
+    });
+}
+
 std::string ShowStatsSentence::toString() const {
     return folly::stringPrintf("SHOW STATS");
 }
diff --git a/src/parser/AdminSentences.h b/src/parser/AdminSentences.h
index 70e25f0c3e2e83d698263542217d337db0cfc692..93bf5514f45c38f7a854e530da88de9780cd956e 100644
--- a/src/parser/AdminSentences.h
+++ b/src/parser/AdminSentences.h
@@ -593,6 +593,7 @@ public:
     }
 
     void addPara(const std::string& para);
+    void addPara(const NameLabelList& NameLabelList);
     std::string toString() const override;
     meta::cpp2::AdminJobOp getOp() const;
     meta::cpp2::AdminCmd getCmd() const;
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index da06ecfe5b11382a9efa03ac12184c95e2260d3f..23e59960f9bd9e895c7d83f05ed3c0ac46b03aae 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -2184,23 +2184,31 @@ describe_edge_index_sentence
     ;
 
 rebuild_tag_index_sentence
-    : KW_REBUILD KW_TAG KW_INDEX name_label {
+    : KW_REBUILD KW_TAG KW_INDEX name_label_list {
         auto sentence = new AdminJobSentence(meta::cpp2::AdminJobOp::ADD,
                                              meta::cpp2::AdminCmd::REBUILD_TAG_INDEX);
         sentence->addPara(*$4);
         delete $4;
         $$ = sentence;
     }
+    | KW_REBUILD KW_TAG KW_INDEX {
+        $$ = new AdminJobSentence(meta::cpp2::AdminJobOp::ADD,
+                                  meta::cpp2::AdminCmd::REBUILD_TAG_INDEX);
+    }
     ;
 
 rebuild_edge_index_sentence
-    : KW_REBUILD KW_EDGE KW_INDEX name_label {
+    : KW_REBUILD KW_EDGE KW_INDEX name_label_list {
         auto sentence = new AdminJobSentence(meta::cpp2::AdminJobOp::ADD,
                                              meta::cpp2::AdminCmd::REBUILD_EDGE_INDEX);
         sentence->addPara(*$4);
         delete $4;
         $$ = sentence;
     }
+    | KW_REBUILD KW_EDGE KW_INDEX {
+        $$ = new AdminJobSentence(meta::cpp2::AdminJobOp::ADD,
+                                  meta::cpp2::AdminCmd::REBUILD_EDGE_INDEX);
+    }
     ;
 
 add_group_sentence
diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp
index a77b4dab4b156dbcd066ca2b3a8e3f01733b0979..fe128a4977167ba13d7bc9f901188f0b3c2162dc 100644
--- a/src/parser/test/ParserTest.cpp
+++ b/src/parser/test/ParserTest.cpp
@@ -3090,4 +3090,31 @@ TEST(Parser, FullTextServiceTest) {
         ASSERT_FALSE(result.ok());
     }
 }
+
+TEST(Parser, JobTest) {
+    GQLParser parser;
+    auto checkTest = [&parser] (const std::string& query, const std::string expectedStr) {
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << query << ":" << result.status();
+        ASSERT_EQ(result.value()->toString(), expectedStr);
+    };
+    checkTest("SUBMIT JOB COMPACT", "SUBMIT JOB COMPACT");
+    checkTest("SUBMIT JOB COMPACT 111", "SUBMIT JOB COMPACT 111");
+    checkTest("SUBMIT JOB FLUSH", "SUBMIT JOB FLUSH");
+    checkTest("SUBMIT JOB FLUSH 111", "SUBMIT JOB FLUSH 111");
+    checkTest("SUBMIT JOB STATS", "SUBMIT JOB STATS");
+    checkTest("SUBMIT JOB STATS 111", "SUBMIT JOB STATS 111");
+    checkTest("SHOW JOBS", "SHOW JOBS");
+    checkTest("SHOW JOB 111", "SHOW JOB 111");
+    checkTest("STOP JOB 111", "STOP JOB 111");
+    checkTest("RECOVER JOB", "RECOVER JOB");
+    checkTest("REBUILD TAG INDEX name_index", "REBUILD TAG INDEX name_index");
+    checkTest("REBUILD EDGE INDEX name_index", "REBUILD EDGE INDEX name_index");
+    checkTest("REBUILD TAG INDEX", "REBUILD TAG INDEX ");
+    checkTest("REBUILD EDGE INDEX", "REBUILD EDGE INDEX ");
+    checkTest("REBUILD TAG INDEX name_index, age_index",
+            "REBUILD TAG INDEX name_index,age_index");
+    checkTest("REBUILD EDGE INDEX name_index, age_index",
+            "REBUILD EDGE INDEX name_index,age_index");
+}
 }   // namespace nebula
diff --git a/src/validator/AdminJobValidator.cpp b/src/validator/AdminJobValidator.cpp
index 97d9229e9507ea88131108d9a3e8cc0cce66e67e..20dd7741aa3ff557db368db6902d25a02d68fe83 100644
--- a/src/validator/AdminJobValidator.cpp
+++ b/src/validator/AdminJobValidator.cpp
@@ -21,25 +21,29 @@ Status AdminJobValidator::validateImpl() {
 
             if (cmd == meta::cpp2::AdminCmd::REBUILD_TAG_INDEX ||
                 cmd == meta::cpp2::AdminCmd::REBUILD_EDGE_INDEX) {
-                DCHECK_EQ(sentence_->getParas().size(), 2);
-
-                const auto &indexName = sentence_->getParas()[0];
                 auto ret = cmd == meta::cpp2::AdminCmd::REBUILD_TAG_INDEX
-                               ? qctx()->getMetaClient()->getTagIndexesFromCache(spaceId)
-                               : qctx()->getMetaClient()->getEdgeIndexesFromCache(spaceId);
+                           ? qctx()->indexMng()->getTagIndexes(spaceId)
+                           : qctx()->indexMng()->getEdgeIndexes(spaceId);
                 if (!ret.ok()) {
-                    return Status::SemanticError(
-                        "Index %s not found in space %s", indexName.c_str(), spaceName.c_str());
+                    return Status::SemanticError("Get index failed in space `%s': %s",
+                            spaceName.c_str(), ret.status().toString().c_str());
                 }
                 auto indexes = std::move(ret).value();
-                auto it = std::find_if(indexes.begin(),
-                                       indexes.end(),
-                                       [&indexName](std::shared_ptr<meta::cpp2::IndexItem>& item) {
-                                           return item->get_index_name() == indexName;
-                                       });
-                if (it == indexes.end()) {
-                    return Status::SemanticError(
-                        "Index %s not found in space %s", indexName.c_str(), spaceName.c_str());
+                const auto &paras = sentence_->getParas();
+                if (paras.size() == 1 && indexes.empty()) {
+                    return Status::SemanticError("Space `%s' without indexes", spaceName.c_str());
+                }
+                for (auto i = 0u; i < paras.size() - 1; i++) {
+                    const auto &indexName = paras[i];
+                    auto it = std::find_if(indexes.begin(), indexes.end(),
+                            [&indexName](std::shared_ptr<meta::cpp2::IndexItem>& item) {
+                                return item->get_index_name() == indexName;
+                            });
+                    if (it == indexes.end()) {
+                        return Status::SemanticError(
+                                "Index %s not found in space %s",
+                                indexName.c_str(), spaceName.c_str());
+                    }
                 }
             }
         }
diff --git a/tests/tck/features/index/Index.feature b/tests/tck/features/index/Index.feature
index b442776793b11bcb464c88337db7fd404f303b5f..d64df1ba912eecd7a1ead92f6e1f4c0e94ab41f9 100644
--- a/tests/tck/features/index/Index.feature
+++ b/tests/tck/features/index/Index.feature
@@ -646,7 +646,7 @@ Feature: IndexTest_Vid_String
       """
       SHOW EDGE INDEX STATUS;
       """
-    Then the result should be, in any order:
+    Then the result should include:
       | Name                | Index Status |
       | "edge_index_status" | "FINISHED"   |
     Then drop the used space
@@ -688,3 +688,235 @@ Feature: IndexTest_Vid_String
       """
     Then the execution should be successful
     Then drop the used space
+
+  Scenario: IndexTest rebuild all tag indexes by empty input
+    Given an empty graph
+    And create a space with following options:
+      | name     | rebuild_tag_space |
+      | vid_type | FIXED_STRING(10)  |
+    And having executed:
+      """
+      CREATE TAG id_tag(id int);
+      CREATE TAG name_tag(name string);
+      """
+    And wait 6 seconds
+    When executing query:
+      """
+      INSERT VERTEX id_tag(id) VALUES "100":(100), "200":(100);
+      INSERT VERTEX name_tag(name) VALUES "300":("100"), "400":("100");
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE TAG INDEX id_tag_index ON id_tag(id);
+      CREATE TAG INDEX name_tag_index ON name_tag(name(10));
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      REBUILD TAG INDEX;
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      SHOW TAG INDEX STATUS;
+      """
+    Then the result should include:
+      | Name                                | Index Status |
+      | "rebuild_tag_space_all_tag_indexes" | "FINISHED"   |
+    When executing query:
+      """
+      LOOKUP ON id_tag WHERE id_tag.id == 100
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | "100"    |
+      | "200"    |
+    When executing query:
+      """
+      LOOKUP ON name_tag WHERE name_tag.name == "100"
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | "300"    |
+      | "400"    |
+    Then drop the used space
+
+  Scenario: IndexTest rebuild all tag indexes by multi input
+    Given an empty graph
+    And create a space with following options:
+      | vid_type | FIXED_STRING(10) |
+    And having executed:
+      """
+      CREATE TAG id_tag(id int);
+      CREATE TAG name_tag(name string);
+      CREATE TAG age_tag(age int);
+      """
+    And wait 6 seconds
+    When executing query:
+      """
+      INSERT VERTEX id_tag(id) VALUES "100":(100), "200":(100);
+      INSERT VERTEX name_tag(name) VALUES "300":("100"), "400":("100");
+      INSERT VERTEX age_tag(age) VALUES "500":(8), "600":(8);
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE TAG INDEX id_tag_index ON id_tag(id);
+      CREATE TAG INDEX name_tag_index ON name_tag(name(10));
+      CREATE TAG INDEX age_tag_index ON age_tag(age);
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      REBUILD TAG INDEX id_tag_index, name_tag_index;
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      SHOW TAG INDEX STATUS;
+      """
+    Then the result should include:
+      | Name                          | Index Status |
+      | "id_tag_index,name_tag_index" | "FINISHED"   |
+    When executing query:
+      """
+      LOOKUP ON id_tag WHERE id_tag.id == 100
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | "100"    |
+      | "200"    |
+    When executing query:
+      """
+      LOOKUP ON name_tag WHERE name_tag.name == "100"
+      """
+    Then the result should be, in any order:
+      | VertexID |
+      | "300"    |
+      | "400"    |
+    When executing query:
+      """
+      LOOKUP ON age_tag WHERE age_tag.age == 8
+      """
+    Then the result should be, in any order:
+      | VertexID |
+    Then drop the used space
+
+  Scenario: IndexTest rebuild all edge indexes by empty input
+    Given an empty graph
+    And create a space with following options:
+      | name     | rebuild_edge_space |
+      | vid_type | FIXED_STRING(10)   |
+    And having executed:
+      """
+      CREATE EDGE id_edge(id int);
+      CREATE EDGE name_edge(name string);
+      """
+    And wait 6 seconds
+    When executing query:
+      """
+      INSERT EDGE id_edge(id) VALUES "100"->"200":(100);
+      INSERT EDGE name_edge(name) VALUES "300"->"400":("100");
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE EDGE INDEX id_edge_index ON id_edge(id);
+      CREATE EDGE INDEX name_edge_index ON name_edge(name(10));
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      REBUILD EDGE INDEX;
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      SHOW EDGE INDEX STATUS;
+      """
+    Then the result should include:
+      | Name                                  | Index Status |
+      | "rebuild_edge_space_all_edge_indexes" | "FINISHED"   |
+    When executing query:
+      """
+      LOOKUP ON id_edge WHERE id_edge.id == 100
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | "100"  | "200"  | 0       |
+    When executing query:
+      """
+      LOOKUP ON name_edge WHERE name_edge.name == "100"
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | "300"  | "400"  | 0       |
+    Then drop the used space
+
+  Scenario: IndexTest rebuild all edge indexes by multi input
+    Given an empty graph
+    And create a space with following options:
+      | vid_type | FIXED_STRING(20) |
+    And having executed:
+      """
+      CREATE EDGE id_edge(id int);
+      CREATE EDGE name_edge(name string);
+      CREATE EDGE age_edge(age int);
+      """
+    And wait 6 seconds
+    When executing query:
+      """
+      INSERT EDGE id_edge(id) VALUES "100"->"200":(100);
+      INSERT EDGE name_edge(name) VALUES "300"->"400":("100");
+      INSERT EDGE age_edge(age) VALUES "500"->"600":(8);
+      """
+    Then the execution should be successful
+    When executing query:
+      """
+      CREATE EDGE INDEX id_edge_index ON id_edge(id);
+      CREATE EDGE INDEX name_edge_index ON name_edge(name(10));
+      CREATE EDGE INDEX age_edge_index ON age_edge(age);
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      REBUILD EDGE INDEX id_edge_index,name_edge_index;
+      """
+    Then the execution should be successful
+    And wait 6 seconds
+    When executing query:
+      """
+      SHOW EDGE INDEX STATUS;
+      """
+    Then the result should include:
+      | Name                            | Index Status |
+      | "id_edge_index,name_edge_index" | "FINISHED"   |
+    When executing query:
+      """
+      LOOKUP ON id_edge WHERE id_edge.id == 100
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | "100"  | "200"  | 0       |
+    When executing query:
+      """
+      LOOKUP ON name_edge WHERE name_edge.name == "100"
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+      | "300"  | "400"  | 0       |
+    When executing query:
+      """
+      LOOKUP ON age_edge WHERE age_edge.age == 8
+      """
+    Then the result should be, in any order:
+      | SrcVID | DstVID | Ranking |
+    Then drop the used space