diff --git a/core/src/main/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizer.java b/core/src/main/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizer.java
index 5c115f0db8..359742f218 100644
--- a/core/src/main/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizer.java
+++ b/core/src/main/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizer.java
@@ -12,6 +12,7 @@
import java.util.List;
import java.util.stream.Collectors;
import org.opensearch.sql.planner.logical.LogicalPlan;
+import org.opensearch.sql.planner.optimizer.rule.EliminateNested;
import org.opensearch.sql.planner.optimizer.rule.MergeFilterAndFilter;
import org.opensearch.sql.planner.optimizer.rule.PushFilterUnderSort;
import org.opensearch.sql.planner.optimizer.rule.read.CreateTableScanBuilder;
@@ -58,7 +59,11 @@ public static LogicalPlanOptimizer create() {
TableScanPushDown.PUSH_DOWN_HIGHLIGHT,
TableScanPushDown.PUSH_DOWN_NESTED,
TableScanPushDown.PUSH_DOWN_PROJECT,
- new CreateTableWriteBuilder()));
+ new CreateTableWriteBuilder(),
+ /*
+ * Phase 3: Transformations for others
+ */
+ new EliminateNested()));
}
/** Optimize {@link LogicalPlan}. */
diff --git a/core/src/main/java/org/opensearch/sql/planner/optimizer/rule/EliminateNested.java b/core/src/main/java/org/opensearch/sql/planner/optimizer/rule/EliminateNested.java
new file mode 100644
index 0000000000..f4f63717f6
--- /dev/null
+++ b/core/src/main/java/org/opensearch/sql/planner/optimizer/rule/EliminateNested.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.sql.planner.optimizer.rule;
+
+import static com.facebook.presto.matching.Pattern.typeOf;
+import static org.opensearch.sql.planner.optimizer.pattern.Patterns.source;
+
+import com.facebook.presto.matching.Capture;
+import com.facebook.presto.matching.Captures;
+import com.facebook.presto.matching.Pattern;
+import lombok.Getter;
+import lombok.experimental.Accessors;
+import org.opensearch.sql.planner.logical.LogicalAggregation;
+import org.opensearch.sql.planner.logical.LogicalNested;
+import org.opensearch.sql.planner.logical.LogicalPlan;
+import org.opensearch.sql.planner.optimizer.Rule;
+
+/**
+ * Eliminate LogicalNested if its child is LogicalAggregation.
+ * LogicalNested - LogicalAggregation - Child --> LogicalAggregation - Child
+ * E.g. count(nested(foo.bar, foo))
+ */
+public class EliminateNested implements Rule {
+
+ private final Capture capture;
+
+ @Accessors(fluent = true)
+ @Getter
+ private final Pattern pattern;
+
+ public EliminateNested() {
+ this.capture = Capture.newCapture();
+ this.pattern =
+ typeOf(LogicalNested.class)
+ .with(source().matching(typeOf(LogicalAggregation.class).capturedAs(capture)));
+ }
+
+ @Override
+ public LogicalPlan apply(LogicalNested plan, Captures captures) {
+ return captures.get(capture);
+ }
+}
diff --git a/core/src/test/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizerTest.java b/core/src/test/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizerTest.java
index c25e415cfa..5a12b429ad 100644
--- a/core/src/test/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizerTest.java
+++ b/core/src/test/java/org/opensearch/sql/planner/optimizer/LogicalPlanOptimizerTest.java
@@ -5,6 +5,7 @@
package org.opensearch.sql.planner.optimizer;
+import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
@@ -122,6 +123,43 @@ void multiple_filter_should_eventually_be_merged() {
DSL.equal(DSL.ref("intV", INTEGER), DSL.literal(integerValue(1))))));
}
+ @Test
+ void eliminate_nested_in_aggregation() {
+ List