diff --git a/src/optimizer/OptimizerUtils.cpp b/src/optimizer/OptimizerUtils.cpp
index 7f261c1604d943679746ac71607c8787a841f59b..c5592026219dad1b9f35cbca9ca248f304edd75a 100644
--- a/src/optimizer/OptimizerUtils.cpp
+++ b/src/optimizer/OptimizerUtils.cpp
@@ -26,7 +26,7 @@ Value OptimizerUtils::boundValue(const meta::cpp2::ColumnDef& col,
             return boundValueWithMin(col, v);
         }
     }
-    return Value(NullType::BAD_TYPE);
+    return Value::kNullBadType;
 }
 
 Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const Value& v) {
@@ -59,7 +59,7 @@ Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const V
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length ||
                 col.get_type().get_type_length() == nullptr) {
-                return Value(NullType::BAD_TYPE);
+                return Value::kNullBadType;
             }
             std::vector<unsigned char> bytes(v.getStr().begin(), v.getStr().end());
             bytes.resize(*col.get_type().get_type_length());
@@ -172,11 +172,11 @@ Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const V
         case Value::Type::PATH: {
             DLOG(FATAL) << "Not supported value type " << type
                         << "for index.";
-            return Value(NullType::BAD_TYPE);
+            return Value::kNullBadType;
         }
     }
     DLOG(FATAL) << "Unknown value type " << static_cast<int>(type);
-    return Value(NullType::BAD_TYPE);
+    return Value::kNullBadType;
 }
 
 Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v) {
@@ -206,7 +206,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V
         }
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length || col.get_type().get_type_length() == nullptr) {
-                return Value(NullType::BAD_TYPE);
+                return Value::kNullBadType;
             }
             std::vector<unsigned char> bytes(v.getStr().begin(), v.getStr().end());
             bytes.resize(*col.get_type().get_type_length());
@@ -321,11 +321,11 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V
         case Value::Type::PATH: {
             DLOG(FATAL) << "Not supported value type " << type
                         << "for index.";
-            return Value(NullType::BAD_TYPE);
+            return Value::kNullBadType;
         }
     }
     DLOG(FATAL) << "Unknown value type " << static_cast<int>(type);
-    return Value(NullType::BAD_TYPE);
+    return Value::kNullBadType;
 }
 
 Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v) {
@@ -343,7 +343,7 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length ||
                 col.get_type().get_type_length() == nullptr) {
-                return Value(NullType::BAD_TYPE);
+                return Value::kNullBadType;
             }
             return Value(std::string(*col.get_type().get_type_length(), '\377'));
         }
@@ -384,11 +384,11 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const
         case Value::Type::PATH: {
             DLOG(FATAL) << "Not supported value type " << type
                         << "for index.";
-            return Value(NullType::BAD_TYPE);
+            return Value::kNullBadType;
         }
     }
     DLOG(FATAL) << "Unknown value type " << static_cast<int>(type);
-    return Value(NullType::BAD_TYPE);
+    return Value::kNullBadType;
 }
 
 Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v) {
@@ -406,7 +406,7 @@ Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length ||
                 col.get_type().get_type_length() == nullptr) {
-                return Value(NullType::BAD_TYPE);
+                return Value::kNullBadType;
             }
             return Value(std::string(*col.get_type().get_type_length(), '\0'));
         }
@@ -430,11 +430,55 @@ Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const
         case Value::Type::PATH: {
             DLOG(FATAL) << "Not supported value type " << type
                         << "for index.";
-            return Value(NullType::BAD_TYPE);
+            return Value::kNullBadType;
         }
     }
     DLOG(FATAL) << "Unknown value type " << static_cast<int>(type);
-    return Value(NullType::BAD_TYPE);
+    return Value::kNullBadType;
+}
+
+Value OptimizerUtils::normalizeValue(const meta::cpp2::ColumnDef& col, const Value& v) {
+    auto type = SchemaUtil::propTypeToValueType(col.get_type().get_type());
+    switch (type) {
+        case Value::Type::INT:
+        case Value::Type::FLOAT:
+        case Value::Type::BOOL:
+        case Value::Type::DATE:
+        case Value::Type::TIME:
+        case Value::Type::DATETIME: {
+            return v;
+        }
+        case Value::Type::STRING : {
+            if (!col.type.__isset.type_length ||
+                col.get_type().get_type_length() == nullptr) {
+                return Value::kNullBadType;
+            }
+            auto len = static_cast<size_t>(*col.get_type().get_type_length());
+            if (v.getStr().size() > len) {
+                return Value(v.getStr().substr(0, len));
+            } else {
+                std::string s;
+                s.reserve(len);
+                s.append(v.getStr()).append(len - v.getStr().size(), '\0');
+                return Value(std::move(s));
+            }
+        }
+        case Value::Type::__EMPTY__:
+        case Value::Type::NULLVALUE:
+        case Value::Type::VERTEX:
+        case Value::Type::EDGE:
+        case Value::Type::LIST:
+        case Value::Type::SET:
+        case Value::Type::MAP:
+        case Value::Type::DATASET:
+        case Value::Type::PATH: {
+            DLOG(FATAL) << "Not supported value type " << type
+                        << "for index.";
+            return Value::kNullBadType;
+        }
+    }
+    DLOG(FATAL) << "Unknown value type " << static_cast<int>(type);
+    return Value::kNullBadType;;
 }
 
 }  // namespace graph
diff --git a/src/optimizer/OptimizerUtils.h b/src/optimizer/OptimizerUtils.h
index 60f526b51c69831431f2aa50f678fac0cfc7fa3c..8870a436419c2d9e9bc4bb4ee01c08d2c1621d55 100644
--- a/src/optimizer/OptimizerUtils.h
+++ b/src/optimizer/OptimizerUtils.h
@@ -36,6 +36,8 @@ public:
     static Value boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v);
 
     static Value boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v);
+
+    static Value normalizeValue(const meta::cpp2::ColumnDef& col, const Value& v);
 };
 
 }  // namespace graph
diff --git a/src/optimizer/rule/IndexScanRule.cpp b/src/optimizer/rule/IndexScanRule.cpp
index cf250255dcc6d48e38489c8d8534b96afa54dc10..9a50f3d55186bde68e53e533a0aee9a49ea99a04 100644
--- a/src/optimizer/rule/IndexScanRule.cpp
+++ b/src/optimizer/rule/IndexScanRule.cpp
@@ -76,8 +76,9 @@ Status IndexScanRule::createIQCWithLogicAnd(IndexQueryCtx &iqctx,
     if (index == nullptr) {
         return Status::IndexNotFound("No valid index found");
     }
-
-    return appendIQCtx(index, items, iqctx);
+    auto in = static_cast<const IndexScan *>(groupNode->node());
+    const auto& filter = in->queryContext()->begin()->get_filter();
+    return appendIQCtx(index, items, iqctx, filter);
 }
 
 Status IndexScanRule::createIQCWithLogicOR(IndexQueryCtx &iqctx,
@@ -95,9 +96,19 @@ Status IndexScanRule::createIQCWithLogicOR(IndexQueryCtx &iqctx,
     return Status::OK();
 }
 
+size_t IndexScanRule::hintCount(const FilterItems& items) const noexcept {
+    std::unordered_set<std::string> hintCols;
+    for (const auto& i : items.items) {
+        hintCols.emplace(i.col_);
+    }
+    return hintCols.size();
+}
+
 Status IndexScanRule::appendIQCtx(const IndexItem& index,
                                   const FilterItems& items,
-                                  IndexQueryCtx &iqctx) const {
+                                  IndexQueryCtx &iqctx,
+                                  const std::string& filter) const {
+    auto hc = hintCount(items);
     auto fields = index->get_fields();
     IndexQueryContext ctx;
     decltype(ctx.column_hints) hints;
@@ -112,18 +123,26 @@ Status IndexScanRule::appendIQCtx(const IndexItem& index,
             found = true;
         }
         if (!found) break;
-        // TODO (sky) : rewrite filter expr. NE expr should be add filter expr .
         auto it = std::find_if(filterItems.items.begin(), filterItems.items.end(),
                                [](const auto &ite) {
                                    return ite.relOP_ == RelationalExpression::Kind::kRelNE;
                                });
         if (it != filterItems.items.end()) {
+            // TODO (sky) : rewrite filter expr. NE expr should be add filter expr .
+            ctx.set_filter(filter);
             break;
         }
         NG_RETURN_IF_ERROR(appendColHint(hints, filterItems, field));
+        hc--;
+        if (filterItems.items.begin()->relOP_ != RelationalExpression::Kind::kRelEQ) {
+            break;
+        }
     }
     ctx.set_index_id(index->get_index_id());
-    // TODO (sky) : rewrite expr and set filter
+    if (hc > 0) {
+        // TODO (sky) : rewrite expr and set filter
+        ctx.set_filter(filter);
+    }
     ctx.set_column_hints(std::move(hints));
     iqctx->emplace_back(std::move(ctx));
     return Status::OK();
@@ -131,7 +150,7 @@ Status IndexScanRule::appendIQCtx(const IndexItem& index,
 
 #define CHECK_BOUND_VALUE(v, name)                                                                 \
     do {                                                                                           \
-        if (v == Value(NullType::BAD_TYPE)) {                                                      \
+        if (v == Value::kNullBadType) {                                                      \
             LOG(ERROR) << "Get bound value error. field : "  << name;                              \
             return Status::Error("Get bound value error. field : %s", name.c_str());               \
         }                                                                                          \
@@ -151,7 +170,7 @@ Status IndexScanRule::appendColHint(std::vector<IndexColumnHint>& hints,
                 return Status::SemanticError();
             }
             isRangeScan = false;
-            begin = item.value_;
+            begin = OptimizerUtils::normalizeValue(col, item.value_);
             break;
         }
         NG_RETURN_IF_ERROR(boundValue(item, col, begin, end));
diff --git a/src/optimizer/rule/IndexScanRule.h b/src/optimizer/rule/IndexScanRule.h
index 8ba09e34ac5521719a5cac76fadcf3140b539934..4e0f0a5c24449e5a8406e0b65eee4e7f4d86f12d 100644
--- a/src/optimizer/rule/IndexScanRule.h
+++ b/src/optimizer/rule/IndexScanRule.h
@@ -119,7 +119,8 @@ private:
 
     Status appendIQCtx(const IndexItem& index,
                        const FilterItems& items,
-                       IndexQueryCtx &iqctx) const;
+                       IndexQueryCtx &iqctx,
+                       const std::string& filter = "") const;
 
     Status appendColHint(std::vector<IndexColumnHint>& hitns,
                          const FilterItems& items,
@@ -129,6 +130,8 @@ private:
                       const meta::cpp2::ColumnDef& col,
                       Value& begin, Value& end) const;
 
+    size_t hintCount(const FilterItems& items) const noexcept;
+
     bool isEdge(const OptGroupNode *groupNode) const;
 
     int32_t schemaId(const OptGroupNode *groupNode) const;
diff --git a/src/optimizer/test/IndexScanRuleTest.cpp b/src/optimizer/test/IndexScanRuleTest.cpp
index 947340a18aaaab64b2aa47af9e0d8603d9710126..f215e76b3ab8b01f3a35f56b6d67c26da402623c 100644
--- a/src/optimizer/test/IndexScanRuleTest.cpp
+++ b/src/optimizer/test/IndexScanRuleTest.cpp
@@ -144,7 +144,7 @@ TEST(IndexScanRuleTest, IQCtxTest) {
             ASSERT_TRUE(ret.ok());
 
             ASSERT_EQ(1, iqctx->size());
-            ASSERT_EQ(5, (iqctx.get()->begin())->get_column_hints().size());
+            ASSERT_EQ(1, (iqctx.get()->begin())->get_column_hints().size());
             ASSERT_EQ(1, (iqctx.get()->begin())->get_index_id());
             ASSERT_EQ("", (iqctx.get()->begin())->get_filter());
             const auto& colHints = (iqctx.get()->begin())->get_column_hints();
@@ -155,43 +155,16 @@ TEST(IndexScanRuleTest, IQCtxTest) {
                 ASSERT_EQ(Value(2L), hint.get_begin_value());
                 ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
             }
-            {
-                auto hint = colHints[1];
-                ASSERT_EQ("col1", hint.get_column_name());
-                ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
-                ASSERT_EQ(Value(0L), hint.get_begin_value());
-                ASSERT_EQ(Value(3L), hint.get_end_value());
-            }
-            {
-                auto hint = colHints[2];
-                ASSERT_EQ("col2", hint.get_column_name());
-                ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
-                ASSERT_EQ(Value(4L), hint.get_begin_value());
-                ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
-            }
-            {
-                auto hint = colHints[3];
-                ASSERT_EQ("col3", hint.get_column_name());
-                ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
-                ASSERT_EQ(Value(std::numeric_limits<int64_t>::min()), hint.get_begin_value());
-                ASSERT_EQ(Value(4L), hint.get_end_value());
-            }
-            {
-                auto hint = colHints[4];
-                ASSERT_EQ("col4", hint.get_column_name());
-                ASSERT_EQ(storage::cpp2::ScanType::PREFIX, hint.get_scan_type());
-                ASSERT_EQ(Value(4L), hint.get_begin_value());
-            }
         }
 
-        // setup FilterItems col0 > 1 and col1 <= 2 and col1 > -1 and col2 != 3
+        // setup FilterItems col0 == 1 and col1 <= 2 and col1 > -1 and col2 != 3
         //                   and col3 < 4
         // only expect col0 and col1 exits in column hints.
         // col2 and col3 should be filter in storage layer.
         {
             items.items.clear();
             iqctx.get()->clear();
-            items.addItem("col0", RelationalExpression::Kind::kRelGT, Value(1L));
+            items.addItem("col0", RelationalExpression::Kind::kRelEQ, Value(1L));
             items.addItem("col1", RelationalExpression::Kind::kRelLE, Value(2L));
             items.addItem("col1", RelationalExpression::Kind::kRelGT, Value(-1L));
             items.addItem("col2", RelationalExpression::Kind::kRelNE, Value(3L));
@@ -208,9 +181,8 @@ TEST(IndexScanRuleTest, IQCtxTest) {
             {
                 auto hint = colHints[0];
                 ASSERT_EQ("col0", hint.get_column_name());
-                ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
-                ASSERT_EQ(Value(2L), hint.get_begin_value());
-                ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
+                ASSERT_EQ(storage::cpp2::ScanType::PREFIX, hint.get_scan_type());
+                ASSERT_EQ(Value(1L), hint.get_begin_value());
             }
             {
                 auto hint = colHints[1];
@@ -220,6 +192,53 @@ TEST(IndexScanRuleTest, IQCtxTest) {
                 ASSERT_EQ(Value(3L), hint.get_end_value());
             }
         }
+        // setup FilterItems col0 == 1 and col1 == 2 and col2 == -1 and col3 > 3
+        //                   and col4 < 4
+        // only expect col0, col1, col2 and col3 exits in column hints.
+        // col4 should be filter in storage layer.
+        {
+            items.items.clear();
+            iqctx.get()->clear();
+            items.addItem("col0", RelationalExpression::Kind::kRelEQ, Value(1L));
+            items.addItem("col1", RelationalExpression::Kind::kRelEQ, Value(2L));
+            items.addItem("col2", RelationalExpression::Kind::kRelEQ, Value(-1L));
+            items.addItem("col3", RelationalExpression::Kind::kRelGT, Value(3L));
+            items.addItem("col4", RelationalExpression::Kind::kRelLT, Value(4L));
+
+            auto ret = instance->appendIQCtx(index, items, iqctx, "col4 < 4");
+            ASSERT_TRUE(ret.ok());
+
+            ASSERT_EQ(1, iqctx->size());
+            ASSERT_EQ(4, (iqctx.get()->begin())->get_column_hints().size());
+            ASSERT_EQ(1, (iqctx.get()->begin())->get_index_id());
+            ASSERT_EQ("col4 < 4", (iqctx.get()->begin())->get_filter());
+            const auto& colHints = (iqctx.get()->begin())->get_column_hints();
+            {
+                auto hint = colHints[0];
+                ASSERT_EQ("col0", hint.get_column_name());
+                ASSERT_EQ(storage::cpp2::ScanType::PREFIX, hint.get_scan_type());
+                ASSERT_EQ(Value(1L), hint.get_begin_value());
+            }
+            {
+                auto hint = colHints[1];
+                ASSERT_EQ("col1", hint.get_column_name());
+                ASSERT_EQ(storage::cpp2::ScanType::PREFIX, hint.get_scan_type());
+                ASSERT_EQ(Value(2L), hint.get_begin_value());
+            }
+            {
+                auto hint = colHints[2];
+                ASSERT_EQ("col2", hint.get_column_name());
+                ASSERT_EQ(storage::cpp2::ScanType::PREFIX, hint.get_scan_type());
+                ASSERT_EQ(Value(-1L), hint.get_begin_value());
+            }
+            {
+                auto hint = colHints[3];
+                ASSERT_EQ("col3", hint.get_column_name());
+                ASSERT_EQ(storage::cpp2::ScanType::RANGE, hint.get_scan_type());
+                ASSERT_EQ(Value(4L), hint.get_begin_value());
+                ASSERT_EQ(Value(std::numeric_limits<int64_t>::max()), hint.get_end_value());
+            }
+        }
     }
 }
 
diff --git a/src/parser/MaintainSentences.cpp b/src/parser/MaintainSentences.cpp
index a690a4733e97e72bb0bc916824e0b95cd4a7cefd..30d1353c3baf7e2b2a2d4e0dd6c1601e21f6fd79 100644
--- a/src/parser/MaintainSentences.cpp
+++ b/src/parser/MaintainSentences.cpp
@@ -238,9 +238,15 @@ std::string CreateTagIndexSentence::toString() const {
     buf += " ON ";
     buf += *tagName_;
     buf += " (";
-    std::string columns;
-    folly::join(", ", this->columns(), columns);
-    buf += columns;
+    std::vector<std::string> fieldDefs;
+    for (const auto& field : this->fields()) {
+        std::string f = field.get_name();
+        if (field.__isset.type_length) f + "(" +field.get_type_length() + ")";
+        fieldDefs.emplace_back(std::move(f));
+    }
+    std::string fields;
+    folly::join(", ", fieldDefs, fields);
+    buf += fields;
     buf += ")";
     return buf;
 }
@@ -254,9 +260,15 @@ std::string CreateEdgeIndexSentence::toString() const {
     buf += " ON ";
     buf += *edgeName_;
     buf += " (";
-    std::string columns;
-    folly::join(", ", this->columns(), columns);
-    buf += columns;
+    std::vector<std::string> fieldDefs;
+    for (const auto& field : this->fields()) {
+        std::string f = field.get_name();
+        if (field.__isset.type_length) f + "(" +field.get_type_length() + ")";
+        fieldDefs.emplace_back(std::move(f));
+    }
+    std::string fields;
+    folly::join(", ", fieldDefs, fields);
+    buf += fields;
     buf += ")";
     return buf;
 }
diff --git a/src/parser/MaintainSentences.h b/src/parser/MaintainSentences.h
index 7667daa04f171b82d024849f2a7a0ae738083cf3..6597f602f87b21cb30d751ba997d106aa839015d 100644
--- a/src/parser/MaintainSentences.h
+++ b/src/parser/MaintainSentences.h
@@ -483,16 +483,37 @@ private:
 };
 
 
+class IndexFieldList final {
+public:
+    IndexFieldList() = default;
+
+    void addField(std::unique_ptr<meta::cpp2::IndexFieldDef> field) {
+        fields_.emplace_back(std::move(field));
+    }
+
+    std::vector<meta::cpp2::IndexFieldDef*> fields() const {
+        std::vector<meta::cpp2::IndexFieldDef*> result;
+        result.resize(fields_.size());
+        auto get = [] (auto &ptr) { return ptr.get(); };
+        std::transform(fields_.begin(), fields_.end(), result.begin(), get);
+        return result;
+    }
+
+private:
+    std::vector<std::unique_ptr<meta::cpp2::IndexFieldDef>> fields_;
+};
+
+
 class CreateTagIndexSentence final : public CreateSentence {
 public:
     CreateTagIndexSentence(std::string *indexName,
                            std::string *tagName,
-                           ColumnNameList *columns,
+                           IndexFieldList *fields,
                            bool ifNotExists)
         : CreateSentence(ifNotExists) {
         indexName_.reset(indexName);
         tagName_.reset(tagName);
-        columns_.reset(columns);
+        fields_.reset(fields);
         kind_ = Kind::kCreateTagIndex;
     }
 
@@ -506,19 +527,19 @@ public:
         return tagName_.get();
     }
 
-    std::vector<std::string> columns() const {
-        std::vector<std::string> result;
-        auto columnNames = columns_->columnNames();
-        result.resize(columnNames.size());
+    std::vector<meta::cpp2::IndexFieldDef> fields() const {
+        std::vector<meta::cpp2::IndexFieldDef> result;
+        auto fields = fields_->fields();
+        result.resize(fields.size());
         auto get = [] (auto ptr) { return *ptr; };
-        std::transform(columnNames.begin(), columnNames.end(), result.begin(), get);
+        std::transform(fields.begin(), fields.end(), result.begin(), get);
         return result;
     }
 
 private:
     std::unique_ptr<std::string>                indexName_;
     std::unique_ptr<std::string>                tagName_;
-    std::unique_ptr<ColumnNameList>             columns_;
+    std::unique_ptr<IndexFieldList>             fields_;
 };
 
 
@@ -526,12 +547,12 @@ class CreateEdgeIndexSentence final : public CreateSentence {
 public:
     CreateEdgeIndexSentence(std::string *indexName,
                             std::string *edgeName,
-                            ColumnNameList *columns,
+                            IndexFieldList *fields,
                             bool ifNotExists)
         : CreateSentence(ifNotExists) {
         indexName_.reset(indexName);
         edgeName_.reset(edgeName);
-        columns_.reset(columns);
+        fields_.reset(fields);
         kind_ = Kind::kCreateEdgeIndex;
     }
 
@@ -545,19 +566,19 @@ public:
         return edgeName_.get();
     }
 
-    std::vector<std::string> columns() const {
-        std::vector<std::string> result;
-        auto columnNames = columns_->columnNames();
-        result.resize(columnNames.size());
+    std::vector<meta::cpp2::IndexFieldDef> fields() const {
+        std::vector<meta::cpp2::IndexFieldDef> result;
+        auto fields = fields_->fields();
+        result.resize(fields.size());
         auto get = [] (auto ptr) { return *ptr; };
-        std::transform(columnNames.begin(), columnNames.end(), result.begin(), get);
+        std::transform(fields.begin(), fields.end(), result.begin(), get);
         return result;
     }
 
 private:
     std::unique_ptr<std::string>                indexName_;
     std::unique_ptr<std::string>                edgeName_;
-    std::unique_ptr<ColumnNameList>             columns_;
+    std::unique_ptr<IndexFieldList>             fields_;
 };
 
 
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index 7ad5b791930e4f41947874a6b0c9eb520ec25e78..d7969cf1e027d5c20c7d564f89bf18fd4feb599d 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -115,6 +115,8 @@ static constexpr size_t MAX_ABS_INTEGER = 9223372036854775808ULL;
     ReadingClause                          *reading_clause;
     MatchClauseList                        *match_clause_list;
     MatchStepRange                         *match_step_range;
+    nebula::meta::cpp2::IndexFieldDef      *index_field;
+    nebula::IndexFieldList                 *index_field_list;
 }
 
 /* destructors */
@@ -261,6 +263,9 @@ static constexpr size_t MAX_ABS_INTEGER = 9223372036854775808ULL;
 %type <role_type_clause> role_type_clause
 %type <acl_item_clause> acl_item_clause
 
+%type <index_field> index_field
+%type <index_field_list> index_field_list
+
 %type <sentence> maintain_sentence
 %type <sentence> create_space_sentence describe_space_sentence drop_space_sentence
 %type <sentence> create_tag_sentence create_edge_sentence
@@ -1638,14 +1643,46 @@ drop_edge_sentence
     }
     ;
 
+index_field
+    : name_label {
+        $$ = new meta::cpp2::IndexFieldDef();
+        $$->set_name($1->c_str());
+        delete $1;
+    }
+    | name_label L_PAREN INTEGER R_PAREN {
+        if ($3 > std::numeric_limits<int16_t>::max()) {
+            throw nebula::GraphParser::syntax_error(@3, "Out of range:");
+        }
+        $$ = new meta::cpp2::IndexFieldDef();
+        $$->set_name($1->c_str());
+        $$->set_type_length($3);
+        delete $1;
+    }
+    ;
+
+index_field_list
+    : index_field {
+        $$ = new IndexFieldList();
+        std::unique_ptr<meta::cpp2::IndexFieldDef> field;
+        field.reset($1);
+        $$->addField(std::move(field));
+    }
+    | index_field_list COMMA index_field {
+        $$ = $1;
+        std::unique_ptr<meta::cpp2::IndexFieldDef> field;
+        field.reset($3);
+        $$->addField(std::move(field));
+    }
+    ;
+
 create_tag_index_sentence
-    : KW_CREATE KW_TAG KW_INDEX opt_if_not_exists name_label KW_ON name_label L_PAREN column_name_list R_PAREN {
+    : KW_CREATE KW_TAG KW_INDEX opt_if_not_exists name_label KW_ON name_label L_PAREN index_field_list R_PAREN {
         $$ = new CreateTagIndexSentence($5, $7, $9, $4);
     }
     ;
 
 create_edge_index_sentence
-    : KW_CREATE KW_EDGE KW_INDEX opt_if_not_exists name_label KW_ON name_label L_PAREN column_name_list R_PAREN {
+    : KW_CREATE KW_EDGE KW_INDEX opt_if_not_exists name_label KW_ON name_label L_PAREN index_field_list R_PAREN {
         $$ = new CreateEdgeIndexSentence($5, $7, $9, $4);
     }
     ;
diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp
index 6d1a60034370861c12da7e0a184432304ad709d3..e8e3e72b1d889dbdae18bccdb52739ad28aa2769 100644
--- a/src/parser/test/ParserTest.cpp
+++ b/src/parser/test/ParserTest.cpp
@@ -504,6 +504,42 @@ TEST(Parser, IndexOperation) {
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
+    {
+        GQLParser parser;
+        std::string query = "CREATE EDGE INDEX IF NOT EXISTS std_index ON t1(c1(10), c2(20))";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "CREATE EDGE INDEX IF NOT EXISTS std_index ON t1(c1, c2(20))";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "CREATE EDGE INDEX IF NOT EXISTS std_index ON t1(c1(10), c2)";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "CREATE TAG INDEX IF NOT EXISTS std_index ON t1(c1(10), c2(20))";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "CREATE TAG INDEX IF NOT EXISTS std_index ON t1(c1, c2(20))";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "CREATE TAG INDEX IF NOT EXISTS std_index ON t1(c1(10), c2)";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
     {
         GQLParser parser;
         std::string query = "DROP TAG INDEX name_index";
diff --git a/src/planner/Maintain.cpp b/src/planner/Maintain.cpp
index 69bbc7399c7356c37000816efc36425d28ab6244..65a968ddbc368a6c055b03c5f1ed00940a8ce2e5 100644
--- a/src/planner/Maintain.cpp
+++ b/src/planner/Maintain.cpp
@@ -48,8 +48,12 @@ std::unique_ptr<cpp2::PlanNodeDescription> CreateIndexNode::explain() const {
     auto desc = SingleInputNode::explain();
     addDescription("schemaName", schemaName_, desc.get());
     addDescription("indexName", indexName_, desc.get());
-    addDescription("fields", folly::toJson(util::toJson(fields_)), desc.get());
-    addDescription("ifNotExists", util::toJson(ifNotExists_), desc.get());
+    std::vector<std::string> fields;
+    for (const auto& field : fields_) {
+        fields.emplace_back(field.get_name());
+    }
+    addDescription("fields", folly::toJson(util::toJson(fields)), desc.get());
+    addDescription("ifNotExists", folly::to<std::string>(ifNotExists_), desc.get());
     return desc;
 }
 
diff --git a/src/planner/Maintain.h b/src/planner/Maintain.h
index 5c87532cb1a4e9ed790d62df48bcb675c9f664e9..c25f820e70aba578c5acd3f50d9591c081cfbb73 100644
--- a/src/planner/Maintain.h
+++ b/src/planner/Maintain.h
@@ -327,7 +327,7 @@ protected:
                     Kind kind,
                     std::string schemaName,
                     std::string indexName,
-                    std::vector<std::string> fields,
+                    std::vector<meta::cpp2::IndexFieldDef> fields,
                     bool ifNotExists)
         : SingleInputNode(qctx, kind, input),
           schemaName_(std::move(schemaName)),
@@ -344,7 +344,7 @@ public:
         return indexName_;
     }
 
-    const std::vector<std::string>& getFields() const {
+    const std::vector<meta::cpp2::IndexFieldDef>& getFields() const {
         return fields_;
     }
 
@@ -355,10 +355,10 @@ public:
     std::unique_ptr<cpp2::PlanNodeDescription> explain() const override;
 
 protected:
-    std::string schemaName_;
-    std::string indexName_;
-    std::vector<std::string> fields_;
-    bool ifNotExists_;
+    std::string                             schemaName_;
+    std::string                             indexName_;
+    std::vector<meta::cpp2::IndexFieldDef>  fields_;
+    bool                                    ifNotExists_;
 };
 
 class CreateTagIndex final : public CreateIndexNode {
@@ -367,7 +367,7 @@ public:
                                 PlanNode* input,
                                 std::string tagName,
                                 std::string indexName,
-                                std::vector<std::string> fields,
+                                std::vector<meta::cpp2::IndexFieldDef> fields,
                                 bool ifNotExists) {
         return qctx->objPool()->add(new CreateTagIndex(
             qctx, input, std::move(tagName), std::move(indexName), std::move(fields), ifNotExists));
@@ -378,7 +378,7 @@ private:
                    PlanNode* input,
                    std::string tagName,
                    std::string indexName,
-                   std::vector<std::string> fields,
+                   std::vector<meta::cpp2::IndexFieldDef> fields,
                    bool ifNotExists)
         : CreateIndexNode(qctx,
                           input,
@@ -395,7 +395,7 @@ public:
                                  PlanNode* input,
                                  std::string edgeName,
                                  std::string indexName,
-                                 std::vector<std::string> fields,
+                                 std::vector<meta::cpp2::IndexFieldDef> fields,
                                  bool ifNotExists) {
         return qctx->objPool()->add(new CreateEdgeIndex(qctx,
                                                         input,
@@ -410,7 +410,7 @@ private:
                     PlanNode* input,
                     std::string edgeName,
                     std::string indexName,
-                    std::vector<std::string> fields,
+                    std::vector<meta::cpp2::IndexFieldDef> fields,
                     bool ifNotExists)
         : CreateIndexNode(qctx,
                           input,
diff --git a/src/validator/MaintainValidator.cpp b/src/validator/MaintainValidator.cpp
index d8fe4ee92a8ee9deeeecd0fdabdc90c436a02ae0..2cab5e537ea5662a98b079d8b4c900cb40e8c4c9 100644
--- a/src/validator/MaintainValidator.cpp
+++ b/src/validator/MaintainValidator.cpp
@@ -7,7 +7,6 @@
 #include "common/base/Base.h"
 #include "common/charset/Charset.h"
 #include "common/expression/ConstantExpression.h"
-
 #include "parser/MaintainSentences.h"
 #include "planner/Maintain.h"
 #include "planner/Query.h"
@@ -303,61 +302,46 @@ Status DropEdgeValidator::toPlan() {
 }
 
 Status CreateTagIndexValidator::validateImpl() {
-    auto sentence = static_cast<CreateTagIndexSentence *>(sentence_);
-    tagName_ = *sentence->tagName();
-    indexName_ = *sentence->indexName();
-    fields_ = sentence->columns();
+    auto sentence = static_cast<CreateTagIndexSentence*>(sentence_);
+    name_ = *sentence->tagName();
+    index_ = *sentence->indexName();
+    fields_ = sentence->fields();
     ifNotExist_ = sentence->isIfNotExist();
-
-    auto status = Status::OK();
-    do {
-        auto tagStatus = qctx_->schemaMng()->toTagID(space_.id, tagName_);
-        NG_RETURN_IF_ERROR(tagStatus);
-
-        auto schema_ = qctx_->schemaMng()->getTagSchema(space_.id, tagStatus.value());
-        if (schema_ == nullptr) {
-            return Status::SemanticError("No schema found for '%s'", tagName_.c_str());
-        }
-
-        status = IndexUtil::validateColumns(fields_);
-        if (!status.ok()) {
-            VLOG(1) << status;
-            break;
-        }
-    } while (false);
     // TODO(darion) Save the index
-    return status;
+    return Status::OK();
 }
 
 Status CreateTagIndexValidator::toPlan() {
-    auto *doNode = CreateTagIndex::make(qctx_, nullptr, tagName_, indexName_, fields_, ifNotExist_);
+    auto sentence = static_cast<CreateTagIndexSentence*>(sentence_);
+    auto *doNode = CreateTagIndex::make(qctx_,
+                                        nullptr,
+                                       *sentence->tagName(),
+                                       *sentence->indexName(),
+                                        sentence->fields(),
+                                        sentence->isIfNotExist());
     root_ = doNode;
     tail_ = root_;
     return Status::OK();
 }
 
 Status CreateEdgeIndexValidator::validateImpl() {
-    auto sentence = static_cast<CreateEdgeIndexSentence *>(sentence_);
-    edgeName_ = *sentence->edgeName();
-    indexName_ = *sentence->indexName();
-    fields_ = sentence->columns();
+    auto sentence = static_cast<CreateEdgeIndexSentence*>(sentence_);
+    name_ = *sentence->edgeName();
+    index_ = *sentence->indexName();
+    fields_ = sentence->fields();
     ifNotExist_ = sentence->isIfNotExist();
-
-    auto edgeStatus = qctx_->schemaMng()->toEdgeType(space_.id, edgeName_);
-    NG_RETURN_IF_ERROR(edgeStatus);
-    auto schema_ = qctx_->schemaMng()->getEdgeSchema(space_.id, edgeStatus.value());
-    if (schema_ == nullptr) {
-        return Status::SemanticError("No schema found for '%s'", edgeName_.c_str());
-    }
-
-    NG_RETURN_IF_ERROR(IndexUtil::validateColumns(fields_));
     // TODO(darion) Save the index
     return Status::OK();
 }
 
 Status CreateEdgeIndexValidator::toPlan() {
-    auto *doNode =
-        CreateEdgeIndex::make(qctx_, nullptr, edgeName_, indexName_, fields_, ifNotExist_);
+    auto sentence = static_cast<CreateEdgeIndexSentence*>(sentence_);
+    auto *doNode = CreateEdgeIndex::make(qctx_,
+                                         nullptr,
+                                        *sentence->edgeName(),
+                                        *sentence->indexName(),
+                                         sentence->fields(),
+                                         sentence->isIfNotExist());
     root_ = doNode;
     tail_ = root_;
     return Status::OK();
diff --git a/src/validator/MaintainValidator.h b/src/validator/MaintainValidator.h
index 41931badfe26a94ac4af267e2f698a900352e24d..7824ea315e934ab258497a1f0c2ca97ebc0d6f3f 100644
--- a/src/validator/MaintainValidator.h
+++ b/src/validator/MaintainValidator.h
@@ -196,10 +196,10 @@ private:
     Status toPlan() override;
 
 private:
-    std::string                      tagName_;
-    std::string                      indexName_;
-    std::vector<std::string>         fields_;
-    bool                             ifNotExist_;
+    std::string                                    name_;
+    std::string                                    index_;
+    std::vector<meta::cpp2::IndexFieldDef>         fields_;
+    bool                                           ifNotExist_;
 };
 
 class CreateEdgeIndexValidator final : public Validator {
@@ -213,10 +213,10 @@ private:
     Status toPlan() override;
 
 private:
-    std::string                      edgeName_;
-    std::string                      indexName_;
-    std::vector<std::string>         fields_;
-    bool                             ifNotExist_;
+    std::string                                    name_;
+    std::string                                    index_;
+    std::vector<meta::cpp2::IndexFieldDef>         fields_;
+    bool                                           ifNotExist_;
 };
 
 class DropTagIndexValidator final : public Validator {
diff --git a/src/visitor/CollectAllExprsVisitor.h b/src/visitor/CollectAllExprsVisitor.h
index 5ea8b05f6a086162f68ee5b2fa64bd56b19e0b45..d86b715b29d581f611313a0c7c9f57c0bdaaa937 100644
--- a/src/visitor/CollectAllExprsVisitor.h
+++ b/src/visitor/CollectAllExprsVisitor.h
@@ -55,6 +55,8 @@ private:
     void visit(AttributeExpression* expr) override;
     void visit(VertexExpression* expr) override;
     void visit(EdgeExpression* expr) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression *) override {};
 
     void visitBinaryExpr(BinaryExpression* expr) override;
     void collectExpr(const Expression* expr);
diff --git a/src/visitor/DeducePropsVisitor.h b/src/visitor/DeducePropsVisitor.h
index ae0ecea539b7cab5610558d50fcde2b0e6a9d393..7d11bfd4d00c28ccec301d8cf183375f9066fc1d 100644
--- a/src/visitor/DeducePropsVisitor.h
+++ b/src/visitor/DeducePropsVisitor.h
@@ -122,6 +122,8 @@ private:
     void visit(ConstantExpression* expr) override;
     void visit(VertexExpression* expr) override;
     void visit(EdgeExpression* expr) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression*) override {};
 
     void visitEdgePropExpr(PropertyExpression* expr);
     void reportError(const Expression* expr);
diff --git a/src/visitor/DeduceTypeVisitor.h b/src/visitor/DeduceTypeVisitor.h
index 30601bcc4d695d8f894abd69cd262e6e1de68723..deaddcf638dc4fec31fc801f48ac926208bce1b8 100644
--- a/src/visitor/DeduceTypeVisitor.h
+++ b/src/visitor/DeduceTypeVisitor.h
@@ -73,6 +73,8 @@ private:
     // vertex/edge expression
     void visit(VertexExpression *expr) override;
     void visit(EdgeExpression *expr) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression *) override {};
 
     void visitVertexPropertyExpr(PropertyExpression *expr);
 
diff --git a/src/visitor/EvaluableExprVisitor.h b/src/visitor/EvaluableExprVisitor.h
index b77f05a0bd9115be638e5fd74dc7cd22b31477b8..1861f004378c4057631dc934641f7cce88ab2c52 100644
--- a/src/visitor/EvaluableExprVisitor.h
+++ b/src/visitor/EvaluableExprVisitor.h
@@ -89,6 +89,11 @@ private:
         isEvaluable_ = false;
     }
 
+    // TODO : CaseExpression
+    void visit(CaseExpression *) override {
+        isEvaluable_ = false;
+    }
+
     bool isEvaluable_{true};
 };
 
diff --git a/src/visitor/ExtractFilterExprVisitor.h b/src/visitor/ExtractFilterExprVisitor.h
index bf8c1d3afd9f922f458738ff96a718478ab1f971..9f8e96e16587975fa4d7c45f513c564e4b72f8eb 100644
--- a/src/visitor/ExtractFilterExprVisitor.h
+++ b/src/visitor/ExtractFilterExprVisitor.h
@@ -46,6 +46,8 @@ private:
     void visit(VertexExpression *) override;
     void visit(EdgeExpression *) override;
     void visit(LogicalExpression *) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression *) override {};
 
     bool canBePushed_{true};
     std::unique_ptr<Expression> remainedExpr_;
diff --git a/src/visitor/ExtractPropExprVisitor.h b/src/visitor/ExtractPropExprVisitor.h
index d5c521f9c59ebe090149972f2d69d6642fbd96e7..d01351dfa9c26ae9a99338a3f821a00f44060f9e 100644
--- a/src/visitor/ExtractPropExprVisitor.h
+++ b/src/visitor/ExtractPropExprVisitor.h
@@ -60,6 +60,8 @@ private:
     void visit(EdgeExpression *) override;
     // binary expression
     void visit(SubscriptExpression *) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression *) override {};
 
     void visitVertexEdgePropExpr(PropertyExpression *);
     void visitPropertyExpr(PropertyExpression *);
diff --git a/src/visitor/FindAnyExprVisitor.h b/src/visitor/FindAnyExprVisitor.h
index 580ec33872cafd581fe73c4315e4cd019f8edbd6..23727a461f058136d51cf3a49cc750285668b767 100644
--- a/src/visitor/FindAnyExprVisitor.h
+++ b/src/visitor/FindAnyExprVisitor.h
@@ -55,6 +55,8 @@ private:
     void visit(LabelExpression* expr) override;
     void visit(VertexExpression* expr) override;
     void visit(EdgeExpression* expr) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression*) override {};
 
     void visitBinaryExpr(BinaryExpression* expr) override;
 
diff --git a/src/visitor/FoldConstantExprVisitor.h b/src/visitor/FoldConstantExprVisitor.h
index a99ae13abc00e57e37dd4e7f29c52305b678d04a..04be0cad2ac06b4b7cdfdbb65ff49783e4213ac7 100644
--- a/src/visitor/FoldConstantExprVisitor.h
+++ b/src/visitor/FoldConstantExprVisitor.h
@@ -53,6 +53,8 @@ public:
     // vertex/edge expression
     void visit(VertexExpression *expr) override;
     void visit(EdgeExpression *expr) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression*) override {};
 
     void visitBinaryExpr(BinaryExpression *expr);
     Expression *fold(Expression *expr) const;
diff --git a/src/visitor/RewriteInputPropVisitor.h b/src/visitor/RewriteInputPropVisitor.h
index 6e4500c51316e613fe30ce1b75b941a027abfc4d..cac274e23b16ab0a36f66f78b6f2ebb687b3223e 100644
--- a/src/visitor/RewriteInputPropVisitor.h
+++ b/src/visitor/RewriteInputPropVisitor.h
@@ -68,6 +68,7 @@ private:
     // vertex/edge expression
     void visit(VertexExpression *) override;
     void visit(EdgeExpression *) override;
+    void visit(CaseExpression*) override {};
 
 
     void visitBinaryExpr(BinaryExpression *expr);
diff --git a/src/visitor/RewriteLabelAttrVisitor.h b/src/visitor/RewriteLabelAttrVisitor.h
index 03020503527e8375cca344b500f25eecda02104e..a6b5795defd68baef69dfe4b81ff05678b13f417 100644
--- a/src/visitor/RewriteLabelAttrVisitor.h
+++ b/src/visitor/RewriteLabelAttrVisitor.h
@@ -50,6 +50,8 @@ private:
     void visit(EdgeDstIdExpression *) override {}
     void visit(VertexExpression *) override {}
     void visit(EdgeExpression *) override {}
+    // TODO : CaseExpression
+    void visit(CaseExpression *) override {}
 
     void visitBinaryExpr(BinaryExpression *expr) override;
 
diff --git a/src/visitor/RewriteMatchLabelVisitor.h b/src/visitor/RewriteMatchLabelVisitor.h
index fc83b490811a0db9710f0df24dbe39a11e495426..af14ec081262b9e871f804b66afade07ab076566 100644
--- a/src/visitor/RewriteMatchLabelVisitor.h
+++ b/src/visitor/RewriteMatchLabelVisitor.h
@@ -58,6 +58,8 @@ private:
     void visit(EdgeDstIdExpression*) override {}
     void visit(VertexExpression*) override {}
     void visit(EdgeExpression*) override {}
+    // TODO : CaseExpression
+    void visit(CaseExpression*) override {}
 
     void visitBinaryExpr(BinaryExpression *) override;
 
diff --git a/src/visitor/RewriteSymExprVisitor.h b/src/visitor/RewriteSymExprVisitor.h
index 53a29592946f73dcd5de8ba6b0ab308632daed66..40b44d9f6910c8f4105f3e6c0303e4eb56fcc2eb 100644
--- a/src/visitor/RewriteSymExprVisitor.h
+++ b/src/visitor/RewriteSymExprVisitor.h
@@ -64,6 +64,8 @@ public:
     // vertex/edge expression
     void visit(VertexExpression *expr) override;
     void visit(EdgeExpression *expr) override;
+    // TODO : CaseExpression
+    void visit(CaseExpression*) override {};
 
 private:
     void visitBinaryExpr(BinaryExpression *expr);
diff --git a/tests/data/nba.ngql b/tests/data/nba.ngql
index d994dd8992ed664b4d992820912c5f84dadad56a..a1e4402fcab3fbef4ddc127a95aaf574f6ebde48 100644
--- a/tests/data/nba.ngql
+++ b/tests/data/nba.ngql
@@ -14,11 +14,11 @@ CREATE EDGE IF NOT EXISTS teammate(start_year int, end_year int);
 --END
 
 --DDL
-CREATE TAG INDEX IF NOT EXISTS player_name_index ON player(name);
+CREATE TAG INDEX IF NOT EXISTS player_name_index ON player(name(64));
 
 CREATE TAG INDEX IF NOT EXISTS player_age_index ON player(age);
 
-CREATE TAG INDEX IF NOT EXISTS team_name_index ON team(name);
+CREATE TAG INDEX IF NOT EXISTS team_name_index ON team(name(64));
 
 --END