diff --git a/src/optimizer/OptimizerUtils.cpp b/src/optimizer/OptimizerUtils.cpp
index c5592026219dad1b9f35cbca9ca248f304edd75a..42c6305d98983ecaadd0e23580ead60c06b075f2 100644
--- a/src/optimizer/OptimizerUtils.cpp
+++ b/src/optimizer/OptimizerUtils.cpp
@@ -20,10 +20,10 @@ Value OptimizerUtils::boundValue(const meta::cpp2::ColumnDef& col,
             return boundValueWithLT(col, v);
         }
         case BoundValueOperator::MAX : {
-            return boundValueWithMax(col, v);
+            return boundValueWithMax(col);
         }
         case BoundValueOperator::MIN : {
-            return boundValueWithMin(col, v);
+            return boundValueWithMin(col);
         }
     }
     return Value::kNullBadType;
@@ -51,10 +51,7 @@ Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const V
                     return Value(0.0);
                 }
             }
-            return v.getFloat() + 0.0000000000000001;
-        }
-        case Value::Type::BOOL: {
-            return v;
+            return v.getFloat() + kEpsilon;
         }
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length ||
@@ -162,6 +159,7 @@ Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const V
             return Value(dt);
         }
         case Value::Type::__EMPTY__:
+        case Value::Type::BOOL:
         case Value::Type::NULLVALUE:
         case Value::Type::VERTEX:
         case Value::Type::EDGE:
@@ -199,10 +197,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V
             } else if (v.getFloat() == 0.0) {
                 return Value(-std::numeric_limits<double_t>::min());
             }
-            return v.getFloat() - 0.0000000000000001;
-        }
-        case Value::Type::BOOL: {
-            return v;
+            return v.getFloat() - kEpsilon;
         }
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length || col.get_type().get_type_length() == nullptr) {
@@ -311,6 +306,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V
             return Value(dt);
         }
         case Value::Type::__EMPTY__:
+        case Value::Type::BOOL:
         case Value::Type::NULLVALUE:
         case Value::Type::VERTEX:
         case Value::Type::EDGE:
@@ -328,7 +324,7 @@ Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const V
     return Value::kNullBadType;
 }
 
-Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v) {
+Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col) {
     auto type = SchemaUtil::propTypeToValueType(col.get_type().get_type());
     switch (type) {
         case Value::Type::INT : {
@@ -337,9 +333,6 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const
         case Value::Type::FLOAT : {
             return Value(std::numeric_limits<double>::max());
         }
-        case Value::Type::BOOL: {
-            return v;
-        }
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length ||
                 col.get_type().get_type_length() == nullptr) {
@@ -374,6 +367,7 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const
             return Value(dt);
         }
         case Value::Type::__EMPTY__:
+        case Value::Type::BOOL:
         case Value::Type::NULLVALUE:
         case Value::Type::VERTEX:
         case Value::Type::EDGE:
@@ -391,7 +385,7 @@ Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const
     return Value::kNullBadType;
 }
 
-Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v) {
+Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col) {
     auto type = SchemaUtil::propTypeToValueType(col.get_type().get_type());
     switch (type) {
         case Value::Type::INT : {
@@ -400,9 +394,6 @@ Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const
         case Value::Type::FLOAT : {
             return Value(-std::numeric_limits<double>::max());
         }
-        case Value::Type::BOOL: {
-            return v;
-        }
         case Value::Type::STRING : {
             if (!col.type.__isset.type_length ||
                 col.get_type().get_type_length() == nullptr) {
@@ -420,6 +411,7 @@ Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const
             return Value(DateTime());
         }
         case Value::Type::__EMPTY__:
+        case Value::Type::BOOL:
         case Value::Type::NULLVALUE:
         case Value::Type::VERTEX:
         case Value::Type::EDGE:
diff --git a/src/optimizer/OptimizerUtils.h b/src/optimizer/OptimizerUtils.h
index 8870a436419c2d9e9bc4bb4ee01c08d2c1621d55..98e1200907450f53f8e7a4513fd372c5a42f7c4e 100644
--- a/src/optimizer/OptimizerUtils.h
+++ b/src/optimizer/OptimizerUtils.h
@@ -33,11 +33,13 @@ public:
 
     static Value boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v);
 
-    static Value boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v);
+    static Value boundValueWithMax(const meta::cpp2::ColumnDef& col);
 
-    static Value boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v);
+    static Value boundValueWithMin(const meta::cpp2::ColumnDef& col);
 
     static Value normalizeValue(const meta::cpp2::ColumnDef& col, const Value& v);
+
+    static constexpr double kEpsilon = 0.0000000000000001;
 };
 
 }  // namespace graph
diff --git a/src/optimizer/rule/IndexScanRule.cpp b/src/optimizer/rule/IndexScanRule.cpp
index 2c6e599a5896c2d8249e949da8cd5a7c979164f2..9aedb52f1f553434e8baa0376a00de33eb202f0e 100644
--- a/src/optimizer/rule/IndexScanRule.cpp
+++ b/src/optimizer/rule/IndexScanRule.cpp
@@ -173,6 +173,10 @@ Status IndexScanRule::appendColHint(std::vector<IndexColumnHint>& hints,
             begin = OptimizerUtils::normalizeValue(col, item.value_);
             break;
         }
+        // because only type for bool is true/false, which can not satisify [start, end)
+        if (col.get_type().get_type() == meta::cpp2::PropertyType::BOOL) {
+            return Status::SemanticError("Range scan for bool type is illegal");
+        }
         NG_RETURN_IF_ERROR(boundValue(item, col, begin, end));
     }
 
diff --git a/src/optimizer/rule/IndexScanRule.h b/src/optimizer/rule/IndexScanRule.h
index 4e0f0a5c24449e5a8406e0b65eee4e7f4d86f12d..1fcffd8285dfbdc2e652f372e4719f9cdf76a01f 100644
--- a/src/optimizer/rule/IndexScanRule.h
+++ b/src/optimizer/rule/IndexScanRule.h
@@ -29,6 +29,7 @@ using IndexQueryCtx = std::unique_ptr<std::vector<IndexQueryContext>>;
 class IndexScanRule final : public OptRule {
     FRIEND_TEST(IndexScanRuleTest, BoundValueTest);
     FRIEND_TEST(IndexScanRuleTest, IQCtxTest);
+    FRIEND_TEST(IndexScanRuleTest, BoundValueRangeTest);
 
 public:
     const Pattern& pattern() const override;
diff --git a/src/optimizer/test/IndexBoundValueTest.cpp b/src/optimizer/test/IndexBoundValueTest.cpp
index dad22ee3f24314cbd5906847ea5c175ad94b81c4..3e63fa62a227f302f59f1ef0d71775d562787f76 100644
--- a/src/optimizer/test/IndexBoundValueTest.cpp
+++ b/src/optimizer/test/IndexBoundValueTest.cpp
@@ -71,30 +71,6 @@ TEST(IndexBoundValueTest, StringTest) {
     }
 }
 
-TEST(IndexBoundValueTest, BoolTest) {
-    meta::cpp2::ColumnDef col;
-    {
-        meta::cpp2::ColumnTypeDef typeDef;
-        typeDef.set_type(meta::cpp2::PropertyType::BOOL);
-        col.set_type(std::move(typeDef));
-    }
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MIN, Value(true)).getBool());
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MAX, Value(true)).getBool());
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(true)).getBool());
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(true)).getBool());
-
-    EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::MIN, Value(false)).getBool());
-    EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::MAX, Value(false)).getBool());
-    EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(false)).getBool());
-    EXPECT_FALSE(OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(false)).getBool());
-
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MIN, Value(NullType::__NULL__)).isNull());
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::MAX, Value(NullType::__NULL__)).isNull());
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::GREATER_THAN,
-                                           Value(NullType::__NULL__)).isNull());
-    EXPECT_TRUE(OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(NullType::__NULL__)).isNull());
-}
-
 TEST(IndexBoundValueTest, IntTest) {
     meta::cpp2::ColumnDef col;
     {
@@ -345,3 +321,10 @@ TEST(IndexBoundValueTest, DateTimeTest) {
 
 }   // namespace graph
 }   // namespace nebula
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    folly::init(&argc, &argv, true);
+    google::SetStderrLogging(google::INFO);
+    return RUN_ALL_TESTS();
+}
diff --git a/src/optimizer/test/IndexScanRuleTest.cpp b/src/optimizer/test/IndexScanRuleTest.cpp
index f215e76b3ab8b01f3a35f56b6d67c26da402623c..b0ced7f6d3df2603a52f342eb6bd7ce73c7ac569 100644
--- a/src/optimizer/test/IndexScanRuleTest.cpp
+++ b/src/optimizer/test/IndexScanRuleTest.cpp
@@ -242,5 +242,264 @@ TEST(IndexScanRuleTest, IQCtxTest) {
     }
 }
 
+TEST(IndexScanRuleTest, BoundValueRangeTest) {
+    auto* inst = std::move(IndexScanRule::kInstance).get();
+    auto* instance = static_cast<IndexScanRule*>(inst);
+
+    {
+        meta::cpp2::ColumnDef col;
+        col.set_name("col_int");
+        col.type.set_type(meta::cpp2::PropertyType::INT64);
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_int < 2
+            IndexScanRule::FilterItems items;
+            items.addItem("col_int", RelationalExpression::Kind::kRelLT, Value(2L));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_int", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(std::numeric_limits<int64_t>::min(), hint.begin_value);
+            EXPECT_EQ(2, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_int <= 2
+            IndexScanRule::FilterItems items;
+            items.addItem("col_int", RelationalExpression::Kind::kRelLE, Value(2L));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_int", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(std::numeric_limits<int64_t>::min(), hint.begin_value);
+            EXPECT_EQ(3, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_int > 2
+            IndexScanRule::FilterItems items;
+            items.addItem("col_int", RelationalExpression::Kind::kRelGT, Value(2L));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_int", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(3, hint.begin_value);
+            EXPECT_EQ(std::numeric_limits<int64_t>::max(), hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_int >= 2
+            IndexScanRule::FilterItems items;
+            items.addItem("col_int", RelationalExpression::Kind::kRelGE, Value(2L));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_int", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(2, hint.begin_value);
+            EXPECT_EQ(std::numeric_limits<int64_t>::max(), hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_int > 2 and col_int < 5
+            IndexScanRule::FilterItems items;
+            items.addItem("col_int", RelationalExpression::Kind::kRelGT, Value(2L));
+            items.addItem("col_int", RelationalExpression::Kind::kRelLT, Value(5L));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_int", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(3, hint.begin_value);
+            EXPECT_EQ(5, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_int >= 2 and col_int <= 5
+            IndexScanRule::FilterItems items;
+            items.addItem("col_int", RelationalExpression::Kind::kRelGE, Value(2L));
+            items.addItem("col_int", RelationalExpression::Kind::kRelLE, Value(5L));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_int", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(2, hint.begin_value);
+            EXPECT_EQ(6, hint.end_value);
+        }
+    }
+    {
+        meta::cpp2::ColumnDef col;
+        col.set_name("col_bool");
+        col.type.set_type(meta::cpp2::PropertyType::BOOL);
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_bool < true
+            IndexScanRule::FilterItems items;
+            items.addItem("col_bool", RelationalExpression::Kind::kRelLT, Value(true));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_FALSE(status.ok());
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_bool >= false
+            IndexScanRule::FilterItems items;
+            items.addItem("col_bool", RelationalExpression::Kind::kRelGE, Value(false));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_FALSE(status.ok());
+        }
+    }
+    {
+        meta::cpp2::ColumnDef col;
+        col.set_name("col_double");
+        col.type.set_type(meta::cpp2::PropertyType::DOUBLE);
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_double < 1.0
+            IndexScanRule::FilterItems items;
+            items.addItem("col_double", RelationalExpression::Kind::kRelLT, Value(1.0));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_double", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(-std::numeric_limits<double>::max(), hint.begin_value);
+            EXPECT_EQ(1, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_double >= 1.0
+            IndexScanRule::FilterItems items;
+            items.addItem("col_double", RelationalExpression::Kind::kRelGE, Value(1.0));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_double", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(1, hint.begin_value);
+            EXPECT_EQ(std::numeric_limits<double>::max(), hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // 1.0 < col_double <= 5.0
+            IndexScanRule::FilterItems items;
+            items.addItem("col_double", RelationalExpression::Kind::kRelGT, Value(1.0));
+            items.addItem("col_double", RelationalExpression::Kind::kRelLE, Value(5.0));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_double", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(1 + OptimizerUtils::kEpsilon, hint.begin_value);
+            EXPECT_EQ(5 - OptimizerUtils::kEpsilon, hint.end_value);
+        }
+    }
+    {
+        meta::cpp2::ColumnDef col;
+        size_t len = 10;
+        col.set_name("col_str");
+        col.type.set_type(meta::cpp2::PropertyType::STRING);
+        col.type.set_type_length(len);
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_str < "ccc"
+            IndexScanRule::FilterItems items;
+            items.addItem("col_str", RelationalExpression::Kind::kRelLT, Value("ccc"));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_str", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ(std::string(len, '\0'), hint.begin_value);
+            EXPECT_EQ("ccc", hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // col_str > "ccc"
+            IndexScanRule::FilterItems items;
+            items.addItem("col_str", RelationalExpression::Kind::kRelGT, Value("ccc"));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_str", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            std::string begin = std::string(3, 'c').append(6, 0x00).append(1, 0x01);
+            std::string end = std::string(len, static_cast<char>(0xFF));
+            EXPECT_EQ(begin, hint.begin_value);
+            EXPECT_EQ(end, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // "aaa" < col_str < "ccc"
+            IndexScanRule::FilterItems items;
+            items.addItem("col_str", RelationalExpression::Kind::kRelGT, Value("aaa"));
+            items.addItem("col_str", RelationalExpression::Kind::kRelLT, Value("ccc"));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_str", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            std::string begin = std::string(3, 'a').append(6, 0x00).append(1, 0x01);
+            std::string end = "ccc";
+            EXPECT_EQ(begin, hint.begin_value);
+            EXPECT_EQ(end, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // "aaa" <= col_str <= "ccc"
+            IndexScanRule::FilterItems items;
+            items.addItem("col_str", RelationalExpression::Kind::kRelGE, Value("aaa"));
+            items.addItem("col_str", RelationalExpression::Kind::kRelLE, Value("ccc"));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_str", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            std::string begin = "aaa";
+            std::string end = std::string(3, 'c').append(6, 0x00).append(1, 0x01);
+            EXPECT_EQ(begin, hint.begin_value);
+            EXPECT_EQ(end, hint.end_value);
+        }
+        {
+            std::vector<storage::cpp2::IndexColumnHint> hints;
+            // "aaa" <= col_str < "ccc"
+            IndexScanRule::FilterItems items;
+            items.addItem("col_str", RelationalExpression::Kind::kRelGE, Value("aaa"));
+            items.addItem("col_str", RelationalExpression::Kind::kRelLT, Value("ccc"));
+            auto status = instance->appendColHint(hints, items, col);
+            EXPECT_TRUE(status.ok());
+            EXPECT_EQ(1, hints.size());
+            const auto& hint = hints[0];
+            EXPECT_EQ("col_str", hint.column_name);
+            EXPECT_EQ(storage::cpp2::ScanType::RANGE, hint.scan_type);
+            EXPECT_EQ("aaa", hint.begin_value);
+            EXPECT_EQ("ccc", hint.end_value);
+        }
+    }
+}
+
 }   // namespace opt
 }   // namespace nebula
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    folly::init(&argc, &argv, true);
+    google::SetStderrLogging(google::INFO);
+    return RUN_ALL_TESTS();
+}