diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9bd8c5f70c649d373edee068071e6892cdd49c39..df9d2a9ac4d65b929c16fa6a3ba488290704c179 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,3 +14,4 @@ nebula_add_subdirectory(context)
 nebula_add_subdirectory(scheduler)
 nebula_add_subdirectory(visitor)
 nebula_add_subdirectory(mock)
+nebula_add_subdirectory(optimizer)
diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt
index e4acc4d64ebe3daa24bf5f49f544ee444ae39a31..b4a05c3487bd678935dc6359927fbc3938ebf0c4 100644
--- a/src/daemons/CMakeLists.txt
+++ b/src/daemons/CMakeLists.txt
@@ -22,6 +22,7 @@ nebula_add_executable(
         $<TARGET_OBJECTS:scheduler_obj>
         $<TARGET_OBJECTS:idgenerator_obj>
         $<TARGET_OBJECTS:context_obj>
+        $<TARGET_OBJECTS:optimizer_obj>
         $<TARGET_OBJECTS:graph_flags_obj>
         $<TARGET_OBJECTS:graph_auth_obj>
         $<TARGET_OBJECTS:common_time_function_obj>
diff --git a/src/optimizer/CMakeLists.txt b/src/optimizer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a38351553cedf9f9ff698e876cf324d0bf9163e6
--- /dev/null
+++ b/src/optimizer/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2020 vesoft inc. All rights reserved.
+#
+# This source code is licensed under Apache 2.0 License,
+# attached with Common Clause Condition 1.0, found in the LICENSES directory.
+
+nebula_add_library(
+    optimizer_obj OBJECT
+    OptimizerUtils.cpp
+)
+
+nebula_add_subdirectory(test)
diff --git a/src/optimizer/OptimizerUtils.cpp b/src/optimizer/OptimizerUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9452af61793446af1294600b6008022720409de5
--- /dev/null
+++ b/src/optimizer/OptimizerUtils.cpp
@@ -0,0 +1,324 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#include "optimizer/OptimizerUtils.h"
+namespace nebula {
+namespace graph {
+
+Value OptimizerUtils::boundValue(const meta::cpp2::ColumnDef& col,
+                                 BoundValueOperator op,
+                                 const Value& v) {
+    switch (op) {
+        case BoundValueOperator::GREATER_THAN : {
+            return boundValueWithGT(col, v);
+        }
+        case BoundValueOperator::LESS_THAN : {
+            return boundValueWithLT(col, v);
+        }
+        case BoundValueOperator::MAX : {
+            return boundValueWithMax(col, v);
+        }
+        case BoundValueOperator::MIN : {
+            return boundValueWithMin(col, v);
+        }
+    }
+    return Value(NullType::BAD_TYPE);
+}
+
+Value OptimizerUtils::boundValueWithGT(const meta::cpp2::ColumnDef& col, const Value& v) {
+    auto type = SchemaUtil::propTypeToValueType(col.get_type());
+    switch (type) {
+        case Value::Type::INT : {
+            if (v.getInt() == std::numeric_limits<int64_t>::max()) {
+                return v;
+            } else {
+                return v + 1;
+            }
+        }
+        case Value::Type::FLOAT : {
+            if (v.getFloat() > 0.0) {
+                if (v.getFloat() == std::numeric_limits<double_t>::max()) {
+                    return v;
+                }
+            } else if (v.getFloat() == 0.0) {
+                return Value(std::numeric_limits<double_t>::min());
+            } else {
+                if (v.getFloat() == -std::numeric_limits<double_t>::min()) {
+                    return Value(0.0);
+                }
+            }
+            return v.getFloat() + 0.0000000000000001;
+        }
+        case Value::Type::BOOL: {
+            return v;
+        }
+        case Value::Type::STRING : {
+            if (!col.__isset.type_length || col.get_type_length() == nullptr) {
+                return Value(NullType::BAD_TYPE);
+            }
+            std::vector<unsigned char> bytes(v.getStr().begin(), v.getStr().end());
+            bytes.resize(*col.get_type_length());
+            for (size_t i = bytes.size();; i--) {
+                if (i > 0) {
+                    if (bytes[i-1]++ != 255) break;
+                } else {
+                    return Value(std::string(*col.get_type_length(), '\377'));
+                }
+            }
+            return Value(std::string(bytes.begin(), bytes.end()));
+        }
+        case Value::Type::DATE : {
+            if (Date(std::numeric_limits<int16_t>::max(), 12, 31) == v.getDate()) {
+                return v.getDate();
+            } else if (Date() == v.getDate()) {
+                return Date(0, 1, 2);
+            }
+            auto d = v.getDate();
+            if (d.day < 31) {
+                d.day += 1;
+            } else {
+                d.day = 1;
+                if (d.month < 12) {
+                    d.month += 1;
+                } else {
+                    d.month = 1;
+                    if (d.year < std::numeric_limits<int16_t>::max()) {
+                        d.year += 1;
+                    } else {
+                        return v.getDate();
+                    }
+                }
+            }
+            return Value(d);
+        }
+        case Value::Type::DATETIME : {
+            auto dt = v.getDateTime();
+            // Ignore the time zone.
+            if (dt.microsec < std::numeric_limits<int32_t>::max()) {
+                dt.microsec = dt.microsec + 1;
+            } else {
+                dt.microsec = 1;
+                if (dt.sec < 60) {
+                    dt.sec += 1;
+                } else {
+                    dt.sec = 1;
+                    if (dt.minute < 60) {
+                        dt.minute += 1;
+                    } else {
+                        dt.minute = 1;
+                        if (dt.hour < 24) {
+                            dt.hour += 1;
+                        } else {
+                            dt.hour = 1;
+                            if (dt.day < 31) {
+                                dt.day += 1;
+                            } else {
+                                dt.day = 1;
+                                if (dt.month < 12) {
+                                    dt.month += 1;
+                                } else {
+                                    dt.month = 1;
+                                    if (dt.year < std::numeric_limits<int16_t>::max()) {
+                                        dt.year += 1;
+                                    } else {
+                                        return v.getDateTime();
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return Value(dt);
+        }
+        default : {
+            return Value(NullType::BAD_TYPE);
+        }
+    }
+}
+
+Value OptimizerUtils::boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v) {
+    auto type = SchemaUtil::propTypeToValueType(col.get_type());
+    switch (type) {
+        case Value::Type::INT : {
+            if (v.getInt() == std::numeric_limits<int64_t>::min()) {
+                return v;
+            } else {
+                return v - 1;
+            }
+        }
+        case Value::Type::FLOAT : {
+            if (v.getFloat() < 0.0) {
+                if (v.getFloat() == -std::numeric_limits<double_t>::max()) {
+                    return v;
+                } else if (v.getFloat() == -std::numeric_limits<double_t>::min()) {
+                    return Value(0.0);
+                }
+            } 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;
+        }
+        case Value::Type::STRING : {
+            if (!col.__isset.type_length || col.get_type_length() == nullptr) {
+                return Value(NullType::BAD_TYPE);
+            }
+            std::vector<unsigned char> bytes(v.getStr().begin(), v.getStr().end());
+            bytes.resize(*col.get_type_length());
+            for (size_t i = bytes.size();; i--) {
+                if (i > 0) {
+                    if (bytes[i-1]-- != 0) break;
+                } else {
+                    return Value(std::string(*col.get_type_length(), '\0'));
+                }
+            }
+            return Value(std::string(bytes.begin(), bytes.end()));
+        }
+        case Value::Type::DATE : {
+            if (Date() == v.getDate()) {
+                return v.getDate();
+            }
+            auto d = v.getDate();
+            if (d.day > 1) {
+                d.day -= 1;
+            } else {
+                d.day = 31;
+                if (d.month > 1) {
+                    d.month -= 1;
+                } else {
+                    d.month = 12;
+                    if (d.year > 1) {
+                        d.year -= 1;
+                    } else {
+                        return v.getDate();
+                    }
+                }
+            }
+            return Value(d);
+        }
+        case Value::Type::DATETIME : {
+            if (DateTime() == v.getDateTime()) {
+                return v.getDateTime();
+            }
+            auto dt = v.getDateTime();
+            if (dt.microsec > 1) {
+                dt.microsec -= 1;
+            } else {
+                dt.microsec = std::numeric_limits<int32_t>::max();
+                if (dt.sec > 1) {
+                    dt.sec -= 1;
+                } else {
+                    dt.sec = 60;
+                    if (dt.minute > 1) {
+                        dt.minute -= 1;
+                    } else {
+                        dt.minute = 60;
+                        if (dt.hour > 1) {
+                            dt.hour -= 1;
+                        } else {
+                            dt.hour = 24;
+                            if (dt.day > 1) {
+                                dt.day -= 1;
+                            } else {
+                                dt.day = 31;
+                                if (dt.month > 1) {
+                                    dt.month -= 1;
+                                } else {
+                                    dt.month = 12;
+                                    if (dt.year > 1) {
+                                        dt.year -= 1;
+                                    } else {
+                                        return v.getDateTime();
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return Value(dt);
+        }
+        default :
+            return Value(NullType::BAD_TYPE);
+    }
+}
+
+Value OptimizerUtils::boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v) {
+    auto type = SchemaUtil::propTypeToValueType(col.get_type());
+    switch (type) {
+        case Value::Type::INT : {
+            return Value(std::numeric_limits<int64_t>::max());
+        }
+        case Value::Type::FLOAT : {
+            return Value(std::numeric_limits<double>::max());
+        }
+        case Value::Type::BOOL: {
+            return v;
+        }
+        case Value::Type::STRING : {
+            if (!col.__isset.type_length || col.get_type_length() == nullptr) {
+                return Value(NullType::BAD_TYPE);
+            }
+            return Value(std::string(*col.get_type_length(), '\377'));
+        }
+        case Value::Type::DATE : {
+            Date d;
+            d.year = std::numeric_limits<int16_t>::max();
+            d.month = 12;
+            d.day = 31;
+            return Value(d);
+        }
+        case Value::Type::DATETIME : {
+            DateTime dt;
+            dt.year = std::numeric_limits<int16_t>::max();
+            dt.month = 12;
+            dt.day = 31;
+            dt.hour = 24;
+            dt.minute = 60;
+            dt.sec = 60;
+            dt.microsec = std::numeric_limits<int32_t>::max();
+            dt.timezone = std::numeric_limits<int32_t>::max();
+            return Value(dt);
+        }
+        default :
+            return Value(NullType::BAD_TYPE);
+    }
+}
+
+Value OptimizerUtils::boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v) {
+    auto type = SchemaUtil::propTypeToValueType(col.get_type());
+    switch (type) {
+        case Value::Type::INT : {
+            return Value(std::numeric_limits<int64_t>::min());
+        }
+        case Value::Type::FLOAT : {
+            return Value(-std::numeric_limits<double>::max());
+        }
+        case Value::Type::BOOL: {
+            return v;
+        }
+        case Value::Type::STRING : {
+            if (!col.__isset.type_length || col.get_type_length() == nullptr) {
+                return Value(NullType::BAD_TYPE);
+            }
+            return Value(std::string(*col.get_type_length(), '\0'));
+        }
+        case Value::Type::DATE : {
+            return Value(Date());
+        }
+        case Value::Type::DATETIME : {
+            return Value(DateTime());
+        }
+        default :
+            return Value(NullType::BAD_TYPE);
+    }
+}
+
+}  // namespace graph
+}  // namespace nebula
diff --git a/src/optimizer/OptimizerUtils.h b/src/optimizer/OptimizerUtils.h
new file mode 100644
index 0000000000000000000000000000000000000000..60f526b51c69831431f2aa50f678fac0cfc7fa3c
--- /dev/null
+++ b/src/optimizer/OptimizerUtils.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+#ifndef NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_
+#define NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_
+
+#include "common/base/Base.h"
+#include "util/SchemaUtil.h"
+#include <common/interface/gen-cpp2/meta_types.h>
+
+namespace nebula {
+namespace graph {
+
+class OptimizerUtils {
+public:
+    enum class BoundValueOperator {
+        GREATER_THAN = 0,
+        LESS_THAN,
+        MAX,
+        MIN,
+    };
+
+public:
+    OptimizerUtils() = delete;
+
+    static Value boundValue(const meta::cpp2::ColumnDef& col,
+                            BoundValueOperator op,
+                            const Value& v = Value());
+
+    static Value boundValueWithGT(const meta::cpp2::ColumnDef& col, const Value& v);
+
+    static Value boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v);
+
+    static Value boundValueWithMax(const meta::cpp2::ColumnDef& col, const Value& v);
+
+    static Value boundValueWithMin(const meta::cpp2::ColumnDef& col, const Value& v);
+};
+
+}  // namespace graph
+}  // namespace nebula
+#endif  // NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_
diff --git a/src/optimizer/test/CMakeLists.txt b/src/optimizer/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..771f9f97ef4cd98e320dccb247fadb8500835249
--- /dev/null
+++ b/src/optimizer/test/CMakeLists.txt
@@ -0,0 +1,48 @@
+nebula_add_test(
+    NAME
+        index_bound_value_test
+    SOURCES
+        IndexBoundValueTest.cpp
+    OBJECTS
+        $<TARGET_OBJECTS:common_base_obj>
+        $<TARGET_OBJECTS:common_concurrent_obj>
+        $<TARGET_OBJECTS:common_datatypes_obj>
+        $<TARGET_OBJECTS:common_expression_obj>
+        $<TARGET_OBJECTS:common_function_manager_obj>
+        $<TARGET_OBJECTS:common_time_obj>
+        $<TARGET_OBJECTS:common_time_function_obj>
+        $<TARGET_OBJECTS:common_meta_thrift_obj>
+        $<TARGET_OBJECTS:common_meta_client_obj>
+        $<TARGET_OBJECTS:common_meta_obj>
+        $<TARGET_OBJECTS:common_storage_thrift_obj>
+        $<TARGET_OBJECTS:common_graph_thrift_obj>
+        $<TARGET_OBJECTS:common_conf_obj>
+        $<TARGET_OBJECTS:common_fs_obj>
+        $<TARGET_OBJECTS:common_thrift_obj>
+        $<TARGET_OBJECTS:common_common_thrift_obj>
+        $<TARGET_OBJECTS:common_thread_obj>
+        $<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
+        $<TARGET_OBJECTS:common_charset_obj>
+        $<TARGET_OBJECTS:common_encryption_obj>
+        $<TARGET_OBJECTS:common_http_client_obj>
+        $<TARGET_OBJECTS:common_process_obj>
+        $<TARGET_OBJECTS:common_agg_function_obj>
+        $<TARGET_OBJECTS:idgenerator_obj>
+        $<TARGET_OBJECTS:expr_visitor_obj>
+        $<TARGET_OBJECTS:session_obj>
+        $<TARGET_OBJECTS:graph_auth_obj>
+        $<TARGET_OBJECTS:graph_flags_obj>
+        $<TARGET_OBJECTS:util_obj>
+        $<TARGET_OBJECTS:planner_obj>
+        $<TARGET_OBJECTS:parser_obj>
+        $<TARGET_OBJECTS:context_obj>
+        $<TARGET_OBJECTS:validator_obj>
+        $<TARGET_OBJECTS:optimizer_obj>
+    LIBRARIES
+        proxygenhttpserver
+        proxygenlib
+        ${THRIFT_LIBRARIES}
+        wangle
+        gtest
+        gtest_main
+)
diff --git a/src/optimizer/test/IndexBoundValueTest.cpp b/src/optimizer/test/IndexBoundValueTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ce8ce19b285d78c6a50c599d7afbfbe90f6ea60
--- /dev/null
+++ b/src/optimizer/test/IndexBoundValueTest.cpp
@@ -0,0 +1,278 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#include <gtest/gtest.h>
+#include "optimizer/OptimizerUtils.h"
+
+namespace nebula {
+namespace graph {
+
+using OP = OptimizerUtils::BoundValueOperator;
+
+TEST(IndexBoundValueTest, StringTest) {
+    meta::cpp2::ColumnDef col;
+    {
+        col.set_type(meta::cpp2::PropertyType::FIXED_STRING);
+        col.set_type_length(8);
+    }
+    std::vector<unsigned char> max = {255, 255, 255, 255, 255, 255, 255, 255};
+    std::vector<unsigned char> min = {'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'};
+    auto maxStr = std::string(max.begin(), max.end());
+    auto minStr = std::string(min.begin(), min.end());
+    EXPECT_EQ(maxStr, OptimizerUtils::boundValue(col, OP::MAX, Value("aa")).getStr());
+    EXPECT_EQ(maxStr, OptimizerUtils::boundValue(col, OP::MAX, Value(maxStr)).getStr());
+    EXPECT_EQ(minStr, OptimizerUtils::boundValue(col, OP::MIN, Value("aa")).getStr());
+    EXPECT_EQ(minStr, OptimizerUtils::boundValue(col, OP::MIN, Value("")).getStr());
+
+    std::string retVal = OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value("aa")).getStr();
+    std::vector<unsigned char> expected = {'a', 'a', '\0', '\0', '\0', '\0', '\0', '\001'};
+    EXPECT_EQ(std::string(expected.begin(), expected.end()), retVal);
+
+    EXPECT_EQ(maxStr, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxStr)).getStr());
+
+    retVal = OptimizerUtils::boundValue(col, OP::LESS_THAN, Value("aa")).getStr();
+    expected = {'a', '`', 255, 255, 255, 255, 255, 255};
+    EXPECT_EQ(std::string(expected.begin(), expected.end()), retVal);
+
+    retVal = OptimizerUtils::boundValue(col, OP::LESS_THAN, Value("")).getStr();
+    EXPECT_EQ(minStr, retVal);
+
+    retVal = OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(minStr)).getStr();
+    EXPECT_EQ(minStr, retVal);
+
+    {
+        auto actual = "ABCDEFGHIJKLMN";
+        auto expectGT = "ABCDEFGI";
+        auto expectLT = "ABCDEFGG";
+        EXPECT_EQ(expectGT,
+                  OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actual)).getStr());
+        EXPECT_EQ(expectLT,
+                  OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(actual)).getStr());
+    }
+    {
+        std::vector<unsigned char> act = {255, 255, 255, 254, 255, 255, 255, 255};
+        std::vector<unsigned char> exp = {255, 255, 255, 255, 0, 0, 0, 0};
+        auto actStr = std::string(act.begin(), act.end());
+        auto expStr = std::string(exp.begin(), exp.end());
+        EXPECT_EQ(expStr,
+                  OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actStr)).getStr());
+    }
+    {
+        std::vector<unsigned char> act = {255, 255, 255, 0, 0, 0, 0, 0};
+        std::vector<unsigned char> exp = {255, 255, 254, 255, 255, 255, 255, 255};
+        auto actStr = std::string(act.begin(), act.end());
+        auto expStr = std::string(exp.begin(), exp.end());
+        EXPECT_EQ(expStr, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(actStr)).getStr());
+    }
+}
+
+TEST(IndexBoundValueTest, BoolTest) {
+    meta::cpp2::ColumnDef col;
+    {
+        col.set_type(meta::cpp2::PropertyType::BOOL);
+    }
+    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;
+    {
+        col.set_type(meta::cpp2::PropertyType::INT64);
+    }
+    auto maxInt = std::numeric_limits<int64_t>::max();
+    auto minInt = std::numeric_limits<int64_t>::min();
+    EXPECT_EQ(maxInt, OptimizerUtils::boundValue(col, OP::MAX, Value(maxInt)).getInt());
+    EXPECT_EQ(maxInt, OptimizerUtils::boundValue(col, OP::MAX, Value(1L)).getInt());
+    EXPECT_EQ(minInt, OptimizerUtils::boundValue(col, OP::MIN, Value(minInt)).getInt());
+    EXPECT_EQ(minInt, OptimizerUtils::boundValue(col, OP::MIN, Value(1L)).getInt());
+
+    EXPECT_EQ(minInt, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(minInt)).getInt());
+    EXPECT_EQ(maxInt, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxInt)).getInt());
+    EXPECT_EQ(minInt + 1,
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(minInt)).getInt());
+    EXPECT_EQ(maxInt - 1,
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(maxInt)).getInt());
+
+    EXPECT_EQ(1, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(0L)).getInt());
+    EXPECT_EQ(-1, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(0L)).getInt());
+
+    EXPECT_EQ(6, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(5L)).getInt());
+    EXPECT_EQ(4, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(5L)).getInt());
+}
+
+TEST(IndexBoundValueTest, DoubleTest) {
+    meta::cpp2::ColumnDef col;
+    {
+        col.set_type(meta::cpp2::PropertyType::DOUBLE);
+    }
+    auto maxDouble = std::numeric_limits<double_t>::max();
+    auto minDouble = std::numeric_limits<double_t>::min();
+    EXPECT_EQ(maxDouble, OptimizerUtils::boundValue(col, OP::MAX, Value(maxDouble)).getFloat());
+    EXPECT_EQ(maxDouble, OptimizerUtils::boundValue(col, OP::MAX, Value(1.1)).getFloat());
+    EXPECT_EQ(-maxDouble, OptimizerUtils::boundValue(col, OP::MIN, Value(minDouble)).getFloat());
+    EXPECT_EQ(-maxDouble, OptimizerUtils::boundValue(col, OP::MIN, Value(1.1)).getFloat());
+
+    EXPECT_EQ(0.0,
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(-minDouble)).getFloat());
+    EXPECT_EQ(maxDouble - 0.0000000000000001,
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(maxDouble)).getFloat());
+    EXPECT_EQ(-minDouble, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(0.0)).getFloat());
+
+    EXPECT_EQ(5.1 - 0.0000000000000001,
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(5.1)));
+
+    EXPECT_EQ(-(5.1 + 0.0000000000000001),
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(-5.1)));
+
+    EXPECT_EQ(maxDouble,
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxDouble)).getFloat());
+    EXPECT_EQ(0.0,
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(-minDouble)).getFloat());
+
+    EXPECT_EQ(minDouble, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(0.0)).getFloat());
+
+    EXPECT_EQ(5.1 + 0.0000000000000001,
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(5.1)).getFloat());
+
+    EXPECT_EQ(-(5.1 - 0.0000000000000001),
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(-5.1)).getFloat());
+}
+
+TEST(IndexBoundValueTest, DateTest) {
+    meta::cpp2::ColumnDef col;
+    {
+        col.set_type(meta::cpp2::PropertyType::DATE);
+    }
+    auto maxYear = std::numeric_limits<int16_t>::max();
+    EXPECT_EQ(Date(maxYear, 12, 31),
+              OptimizerUtils::boundValue(col, OP::MAX, Value(Date())));
+    EXPECT_EQ(Date(),
+              OptimizerUtils::boundValue(col, OP::MIN, Value(Date(maxYear, 12, 31))));
+    EXPECT_EQ(Date(2021, 1, 1),
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date(2020, 12, 31))));
+    EXPECT_EQ(Date(maxYear, 12, 31),
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date(maxYear, 12, 31))));
+    EXPECT_EQ(Date(2020, 1, 2),
+              OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date(2020, 1, 1))));
+    EXPECT_EQ(Date(0, 1, 2), OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(Date())));
+    EXPECT_EQ(Date(), OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(Date())));
+    EXPECT_EQ(Date(2019, 12, 30),
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(Date(2019, 12, 31))));
+    EXPECT_EQ(Date(2018, 12, 31),
+              OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(Date(2019, 1, 1))));
+}
+
+TEST(IndexBoundValueTest, DateTimeTest) {
+    meta::cpp2::ColumnDef col;
+    {
+        col.set_type(meta::cpp2::PropertyType::DATETIME);
+    }
+    DateTime maxDT;
+    {
+        maxDT.microsec = std::numeric_limits<int32_t>::max();
+        maxDT.sec = 60;
+        maxDT.minute = 60;
+        maxDT.hour = 24;
+        maxDT.day = 31;
+        maxDT.month = 12;
+        maxDT.year = std::numeric_limits<int16_t>::max();
+        maxDT.timezone = std::numeric_limits<int32_t>::max();
+    }
+
+    DateTime minDT = DateTime();
+
+    EXPECT_EQ(maxDT, OptimizerUtils::boundValue(col, OP::MAX, Value(maxDT)).getDateTime());
+    EXPECT_EQ(minDT, OptimizerUtils::boundValue(col, OP::MIN, Value(maxDT)).getDateTime());
+    EXPECT_EQ(maxDT, OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(maxDT)).getDateTime());
+
+    {
+        DateTime actual, expect;
+        actual.microsec = std::numeric_limits<int32_t>::max();
+        actual.sec = 60;
+        actual.minute = 60;
+        actual.hour = 24;
+        actual.day = 31;
+        actual.month = 12;
+        actual.year = 2020;
+        actual.timezone = 1;
+
+        expect.microsec = 1;
+        expect.sec = 1;
+        expect.minute = 1;
+        expect.hour = 1;
+        expect.day = 1;
+        expect.month = 1;
+        expect.year = 2021;
+        expect.timezone = 1;
+        EXPECT_EQ(expect,
+                  OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actual)).getDateTime());
+    }
+    {
+        DateTime actual, expect;
+        actual.microsec = std::numeric_limits<int32_t>::max();
+        actual.sec = 34;
+        actual.minute = 60;
+        actual.hour = 24;
+        actual.day = 31;
+        actual.month = 12;
+        actual.year = 2020;
+        actual.timezone = 1;
+
+        expect.microsec = 1;
+        expect.sec = 35;
+        expect.minute = 60;
+        expect.hour = 24;
+        expect.day = 31;
+        expect.month = 12;
+        expect.year = 2020;
+        expect.timezone = 1;
+        EXPECT_EQ(expect,
+                  OptimizerUtils::boundValue(col, OP::GREATER_THAN, Value(actual)).getDateTime());
+    }
+    {
+        DateTime expect = DateTime();
+        EXPECT_EQ(expect, OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(expect)));
+    }
+    {
+        DateTime actual, expect;
+        actual.microsec = std::numeric_limits<int32_t>::max();
+        actual.sec = 34;
+        actual.minute = 60;
+        actual.hour = 24;
+        actual.day = 31;
+        actual.month = 12;
+        actual.year = 2020;
+        actual.timezone = 1;
+
+        expect.microsec = std::numeric_limits<int32_t>::max() - 1;
+        expect.sec = 34;
+        expect.minute = 60;
+        expect.hour = 24;
+        expect.day = 31;
+        expect.month = 12;
+        expect.year = 2020;
+        expect.timezone = 1;
+        EXPECT_EQ(expect,
+                  OptimizerUtils::boundValue(col, OP::LESS_THAN, Value(actual)).getDateTime());
+    }
+}
+
+}   // namespace graph
+}   // namespace nebula