diff --git a/src/util/ExpressionUtils.cpp b/src/util/ExpressionUtils.cpp
index 6a99e2071ccc35bca96a5322356e7a5317989c1b..ebe3475cf3c5f6c90912468266ab0542b73db559 100644
--- a/src/util/ExpressionUtils.cpp
+++ b/src/util/ExpressionUtils.cpp
@@ -76,18 +76,125 @@ std::unique_ptr<InputPropertyExpression> ExpressionUtils::inputPropExpr(const st
 }
 
 std::unique_ptr<Expression>
-ExpressionUtils::pushOrs(const std::vector<std::unique_ptr<RelationalExpression>>& rels) {
+ExpressionUtils::pushOrs(const std::vector<std::unique_ptr<Expression>>& rels) {
+    return pushImpl(Expression::Kind::kLogicalOr, rels);
+}
+
+std::unique_ptr<Expression>
+ExpressionUtils::pushAnds(const std::vector<std::unique_ptr<Expression>>& rels) {
+    return pushImpl(Expression::Kind::kLogicalAnd, rels);
+}
+
+std::unique_ptr<Expression>
+ExpressionUtils::pushImpl(Expression::Kind kind,
+                          const std::vector<std::unique_ptr<Expression>>& rels) {
     DCHECK_GT(rels.size(), 1);
-    auto root = std::make_unique<LogicalExpression>(Expression::Kind::kLogicalOr);
+    DCHECK(kind == Expression::Kind::kLogicalOr || kind == Expression::Kind::kLogicalAnd);
+    auto root = std::make_unique<LogicalExpression>(kind);
     root->addOperand(rels[0]->clone().release());
     root->addOperand(rels[1]->clone().release());
     for (size_t i = 2; i < rels.size(); i++) {
-        auto l = std::make_unique<LogicalExpression>(Expression::Kind::kLogicalOr);
+        auto l = std::make_unique<LogicalExpression>(kind);
         l->addOperand(root->clone().release());
         l->addOperand(rels[i]->clone().release());
         root = std::move(l);
     }
     return root;
 }
+
+std::unique_ptr<Expression> ExpressionUtils::expandExpr(const Expression *expr) {
+    auto kind = expr->kind();
+    std::vector<std::unique_ptr<Expression>> target;
+    switch (kind) {
+        case Expression::Kind::kLogicalOr: {
+            const auto *logic = static_cast<const LogicalExpression*>(expr);
+            for (const auto& e : logic->operands()) {
+                if (e->kind() == Expression::Kind::kLogicalAnd) {
+                    target.emplace_back(expandImplAnd(e.get()));
+                } else {
+                    target.emplace_back(expandExpr(e.get()));
+                }
+            }
+            break;
+        }
+        case Expression::Kind::kLogicalAnd: {
+            target.emplace_back(expandImplAnd(expr));
+            break;
+        }
+        default: {
+            return expr->clone();
+        }
+    }
+    if (target.size() == 1) {
+        if (target[0]->kind() == Expression::Kind::kLogicalAnd) {
+            const auto *logic = static_cast<const LogicalExpression*>(target[0].get());
+            const auto& ops = logic->operands();
+            DCHECK_EQ(ops.size(), 2);
+            if (ops[0]->kind() == Expression::Kind::kLogicalOr ||
+                ops[1]->kind() == Expression::Kind::kLogicalOr) {
+                return expandExpr(target[0].get());
+            }
+        }
+        return std::move(target[0]);
+    }
+    return pushImpl(kind, target);
+}
+
+std::unique_ptr<Expression> ExpressionUtils::expandImplAnd(const Expression *expr) {
+    DCHECK(expr->kind() == Expression::Kind::kLogicalAnd);
+    const auto *logic = static_cast<const LogicalExpression*>(expr);
+    DCHECK_EQ(logic->operands().size(), 2);
+    std::vector<std::unique_ptr<Expression>> subL;
+    auto& ops = logic->operands();
+    if (ops[0]->kind() == Expression::Kind::kLogicalOr) {
+        auto target = expandImplOr(ops[0].get());
+        for (const auto& e : target) {
+            subL.emplace_back(e->clone().release());
+        }
+    } else {
+        subL.emplace_back(expandExpr(std::move(ops[0]).get()));
+    }
+    std::vector<std::unique_ptr<Expression>> subR;
+    if (ops[1]->kind() == Expression::Kind::kLogicalOr) {
+        auto target = expandImplOr(ops[1].get());
+        for (const auto& e : target) {
+            subR.emplace_back(e->clone().release());
+        }
+    } else {
+        subR.emplace_back(expandExpr(std::move(ops[1]).get()));
+    }
+
+    std::vector<std::unique_ptr<Expression>> target;
+    for (auto& le : subL) {
+        for (auto& re : subR) {
+            auto l = std::make_unique<LogicalExpression>(Expression::Kind::kLogicalAnd);
+            l->addOperand(le->clone().release());
+            l->addOperand(re->clone().release());
+            target.emplace_back(std::move(l));
+        }
+    }
+    if (target.size() == 1) {
+        return std::move(target[0]);
+    }
+    return pushImpl(Expression::Kind::kLogicalOr, target);
+}
+
+std::vector<std::unique_ptr<Expression>> ExpressionUtils::expandImplOr(const Expression *expr) {
+    DCHECK(expr->kind() == Expression::Kind::kLogicalOr);
+    const auto *logic = static_cast<const LogicalExpression*>(expr);
+    std::vector<std::unique_ptr<Expression>> exprs;
+    auto& ops = logic->operands();
+    for (const auto& op : ops) {
+        if (op->kind() == Expression::Kind::kLogicalOr) {
+            auto target = expandImplOr(op.get());
+            for (const auto& e : target) {
+                exprs.emplace_back(e->clone().release());
+            }
+        } else {
+            exprs.emplace_back(op->clone().release());
+        }
+    }
+    return exprs;
+}
 }   // namespace graph
 }   // namespace nebula
diff --git a/src/util/ExpressionUtils.h b/src/util/ExpressionUtils.h
index 982f8bcb41ce0d8cda2f9e21a01bd7775a57ea82..9e549863bd644129287c5cc7c1abb5003e0da41d 100644
--- a/src/util/ExpressionUtils.h
+++ b/src/util/ExpressionUtils.h
@@ -125,7 +125,19 @@ public:
     static std::unique_ptr<InputPropertyExpression> inputPropExpr(const std::string& prop);
 
     static std::unique_ptr<Expression> pushOrs(
-        const std::vector<std::unique_ptr<RelationalExpression>>& rels);
+        const std::vector<std::unique_ptr<Expression>>& rels);
+
+    static std::unique_ptr<Expression> pushAnds(
+        const std::vector<std::unique_ptr<Expression>>& rels);
+
+    static std::unique_ptr<Expression> pushImpl(
+        Expression::Kind kind, const std::vector<std::unique_ptr<Expression>>& rels);
+
+    static std::unique_ptr<Expression> expandExpr(const Expression *expr);
+
+    static std::unique_ptr<Expression> expandImplAnd(const Expression *expr);
+
+    static std::vector<std::unique_ptr<Expression>> expandImplOr(const Expression *expr);
 };
 
 }   // namespace graph
diff --git a/src/util/test/ExpressionUtilsTest.cpp b/src/util/test/ExpressionUtilsTest.cpp
index 989f0d622c70511ee9118ec03f39948a3329f422..658493960fe5d6efff19f694b6c22a8f46697926 100644
--- a/src/util/test/ExpressionUtilsTest.cpp
+++ b/src/util/test/ExpressionUtilsTest.cpp
@@ -9,6 +9,7 @@
 #include "common/expression/ConstantExpression.h"
 #include "common/expression/TypeCastingExpression.h"
 #include "util/ExpressionUtils.h"
+#include "parser/GQLParser.h"
 
 namespace nebula {
 namespace graph {
@@ -363,7 +364,7 @@ TEST_F(ExpressionUtilsTest, PullOrs) {
 }
 
 TEST_F(ExpressionUtilsTest, pushOrs) {
-    std::vector<std::unique_ptr<RelationalExpression>> rels;
+    std::vector<std::unique_ptr<Expression>> rels;
     for (int16_t i = 0; i < 5; i++) {
         auto r = std::make_unique<RelationalExpression>(
             Expression::Kind::kRelEQ,
@@ -381,5 +382,176 @@ TEST_F(ExpressionUtilsTest, pushOrs) {
     ASSERT_EQ(expected, t->toString());
 }
 
+TEST_F(ExpressionUtilsTest, pushAnds) {
+    std::vector<std::unique_ptr<Expression>> rels;
+    for (int16_t i = 0; i < 5; i++) {
+        auto r = std::make_unique<RelationalExpression>(
+            Expression::Kind::kRelEQ,
+            new LabelAttributeExpression(new LabelExpression(folly::stringPrintf("tag%d", i)),
+                                         new ConstantExpression(folly::stringPrintf("col%d", i))),
+            new ConstantExpression(Value(folly::stringPrintf("val%d", i))));
+        rels.emplace_back(std::move(r));
+    }
+    auto t = ExpressionUtils::pushAnds(rels);
+    auto expected = std::string("(((((tag0.col0==val0) AND "
+                                "(tag1.col1==val1)) AND "
+                                "(tag2.col2==val2)) AND "
+                                "(tag3.col3==val3)) AND "
+                                "(tag4.col4==val4))");
+    ASSERT_EQ(expected, t->toString());
+}
+
+std::unique_ptr<Expression> parse(const std::string& expr) {
+    std::string query = "LOOKUP on t1 WHERE " + expr;
+    GQLParser parser;
+    auto result = parser.parse(std::move(query));
+    CHECK(result.ok()) << result.status();
+    auto stmt = std::move(result).value();
+    auto *seq = static_cast<SequentialSentences *>(stmt.get());
+    auto *lookup = static_cast<LookupSentence *>(seq->sentences()[0]);
+    return lookup->whereClause()->filter()->clone();
+}
+
+TEST_F(ExpressionUtilsTest, expandExpression) {
+    {
+        auto filter = parse("t1.c1 == 1");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(t1.c1==1)";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 and t1.c2 == 2");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((t1.c1==1) AND (t1.c2==2))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 and t1.c2 == 2 and t1.c3 == 3");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((t1.c1==1) AND (t1.c2==2)) AND (t1.c3==3))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 or t1.c2 == 2");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((t1.c1==1) OR (t1.c2==2))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 or t1.c2 == 2 or t1.c3 == 3");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((t1.c1==1) OR (t1.c2==2)) OR (t1.c3==3))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 and t1.c2 == 2 or t1.c1 == 3");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((t1.c1==1) AND (t1.c2==2)) OR (t1.c1==3))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 or t1.c2 == 2 and t1.c1 == 3");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((t1.c1==1) OR ((t1.c2==2) AND (t1.c1==3)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and t1.c3 == 3");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((t1.c1==1) AND (t1.c3==3)) OR ((t1.c2==2) AND (t1.c3==3)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and t1.c3 == 3 or t1.c4 == 4");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((((t1.c1==1) AND (t1.c3==3)) OR "
+                        "((t1.c2==2) AND (t1.c3==3))) OR "
+                        "(t1.c4==4))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and (t1.c3 == 3 or t1.c4 == 4)");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((((t1.c1==1) AND (t1.c3==3)) OR "
+                        "((t1.c1==1) AND (t1.c4==4))) OR "
+                        "((t1.c2==2) AND (t1.c3==3))) OR "
+                        "((t1.c2==2) AND (t1.c4==4)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("(t1.c1 == 1 or t1.c2 == 2 or t1.c3 == 3 or t1.c4 == 4) "
+                            "and t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((((t1.c1==1) AND (t1.c5==5)) OR "
+                        "((t1.c2==2) AND (t1.c5==5))) OR "
+                        "((t1.c3==3) AND (t1.c5==5))) OR "
+                        "((t1.c4==4) AND (t1.c5==5)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and t1.c4 == 4 and t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((((t1.c1==1) AND (t1.c4==4)) AND (t1.c5==5)) OR "
+                        "(((t1.c2==2) AND (t1.c4==4)) AND (t1.c5==5)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 and (t1.c2 == 2 or t1.c4 == 4) and t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((((t1.c1==1) AND (t1.c2==2)) AND (t1.c5==5)) OR "
+                        "(((t1.c1==1) AND (t1.c4==4)) AND (t1.c5==5)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 and t1.c2 == 2 and (t1.c4 == 4 or t1.c5 == 5)");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((((t1.c1==1) AND (t1.c2==2)) AND (t1.c4==4)) OR "
+                        "(((t1.c1==1) AND (t1.c2==2)) AND (t1.c5==5)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("(t1.c1 == 1 or t1.c2 == 2) and "
+                            "(t1.c3 == 3 or t1.c4 == 4) and "
+                            "t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((((((t1.c1==1) AND (t1.c3==3)) AND (t1.c5==5)) OR "
+                        "(((t1.c1==1) AND (t1.c4==4)) AND (t1.c5==5))) OR "
+                        "(((t1.c2==2) AND (t1.c3==3)) AND (t1.c5==5))) OR "
+                        "(((t1.c2==2) AND (t1.c4==4)) AND (t1.c5==5)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c4 == 4 or (t1.c1 == 1 and (t1.c2 == 2 or t1.c3 == 3))");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((t1.c4==4) OR "
+                        "(((t1.c1==1) AND (t1.c2==2)) OR "
+                        "((t1.c1==1) AND (t1.c3==3))))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c4 == 4 or "
+                            "(t1.c1 == 1 and (t1.c2 == 2 or t1.c3 == 3)) or "
+                            "t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((t1.c4==4) OR "
+                        "(((t1.c1==1) AND (t1.c2==2)) OR ((t1.c1==1) AND (t1.c3==3)))) OR "
+                        "(t1.c5==5))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        auto filter = parse("t1.c1 == 1 and (t1.c2 == 2 or t1.c4) and t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "((((t1.c1==1) AND (t1.c2==2)) AND (t1.c5==5)) OR "
+                        "(((t1.c1==1) AND t1.c4) AND (t1.c5==5)))";
+        ASSERT_EQ(expected, target->toString());
+    }
+    {
+        // Invalid expression for index. don't need to expand.
+        auto filter = parse("t1.c1 == 1 and (t1.c2 == 2 or t1.c4) == true and t1.c5 == 5");
+        auto target = ExpressionUtils::expandExpr(filter.get());
+        auto expected = "(((t1.c1==1) AND (((t1.c2==2) OR t1.c4)==true)) AND (t1.c5==5))";
+        ASSERT_EQ(expected, target->toString());
+    }
+}
 }   // namespace graph
 }   // namespace nebula
diff --git a/src/validator/IndexScanValidator.cpp b/src/validator/IndexScanValidator.cpp
index 45c400ec8a49cc5d247149d3ed21372a59c03e80..42ea9877dae7e5822adb2a57ec00f88ae9c34c1e 100644
--- a/src/validator/IndexScanValidator.cpp
+++ b/src/validator/IndexScanValidator.cpp
@@ -158,7 +158,7 @@ IndexScanValidator::rewriteTSFilter(Expression* expr) {
         isEmptyResultSet_ = true;
         return Status::OK();
     }
-    std::vector<std::unique_ptr<RelationalExpression>> rels;
+    std::vector<std::unique_ptr<Expression>> rels;
     for (const auto& row : vRet.value()) {
         std::unique_ptr<RelationalExpression> r;
         if (isEdge_) {