Skip to content

Commit 2b3d55a

Browse files
committed
refactor(esql): Improve MvExpand push-down logic in logical optimizer
- Modify PushDownMvExpandPastProject to handle more complex projection scenarios - Update PushDownUtils to make resolveRenamesFromProject method public - Enhance test cases in LogicalPlanOptimizerTests to validate new push-down behavior - Simplify MvExpand and Project interaction by removing unnecessary Eval steps - Improve optimization logic for multi-stage MvExpand operations
1 parent 3afecaa commit 2b3d55a

File tree

3 files changed

+29
-20
lines changed

3 files changed

+29
-20
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownMvExpandPastProject.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ protected LogicalPlan rule(MvExpand mvExpand) {
4343
NamedExpression projection = projections.get(i);
4444
if (projection instanceof Alias alias) {
4545
if (alias.toAttribute().semanticEquals(mvExpand.target().toAttribute())) {
46+
// Check if the alias's original field (child) is referenced elsewhere in the projections.
47+
// If the original field is not referenced by any other projection or alias,
48+
// we don't need to inject an Eval to preserve it, and can safely resolve renames and push down.
49+
if (projections.stream()
50+
.anyMatch(
51+
ne -> ne.semanticEquals(alias.child())
52+
|| ne != alias && ne instanceof Alias as && as.child().semanticEquals(alias.child())
53+
) == false) {
54+
// The alias's original field is not referenced elsewhere, no need to preserve it,
55+
mvExpand = PushDownUtils.resolveRenamesFromProject(mvExpand, pj);
56+
break;
57+
}
58+
4659
// for query like: row a = 2 | eval b = a | keep * | mv_expand b
4760
Alias aliasAlias = new Alias(
4861
alias.source(),

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public static Project pushDownPastProject(UnaryPlan parent) {
195195
}
196196
}
197197

198-
private static <P extends LogicalPlan> P resolveRenamesFromProject(P plan, Project project) {
198+
public static <P extends LogicalPlan> P resolveRenamesFromProject(P plan, Project project) {
199199
AttributeMap.Builder<Expression> aliasBuilder = AttributeMap.builder();
200200
project.forEachExpression(Alias.class, a -> aliasBuilder.put(a.toAttribute(), a.child()));
201201
var aliases = aliasBuilder.build();

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,12 +1618,11 @@ public void testDontCombineOrderByThroughMvExpand() {
16181618
/**
16191619
* Expected
16201620
* <pre>{@code
1621-
* Project[[x{r}#20]]
1621+
* Project[[x{r}#20 AS x#5]]
16221622
* \_Limit[1000[INTEGER],true,false]
1623-
* \_MvExpand[$$first_name$x$0{r}#21,x{r}#20]
1624-
* \_Eval[[first_name{f}#10 AS $$first_name$x$0#21]]
1625-
* \_Limit[1000[INTEGER],false,false]
1626-
* \_EsRelation[test][_meta_field{f}#15, emp_no{f}#9, first_name{f}#10, g..]
1623+
* \_MvExpand[first_name{f}#10,x{r}#20]
1624+
* \_Limit[1000[INTEGER],false,false]
1625+
* \_EsRelation[test][_meta_field{f}#15, emp_no{f}#9, first_name{f}#10, g..]
16271626
* }</pre>
16281627
*/
16291628
public void testCopyDefaultLimitPastMvExpand() {
@@ -1636,8 +1635,7 @@ public void testCopyDefaultLimitPastMvExpand() {
16361635
var project = as(plan, Project.class);
16371636
var limit = asLimit(project.child(), 1000, true);
16381637
var mvExpand = as(limit.child(), MvExpand.class);
1639-
var eval = as(mvExpand.child(), Eval.class);
1640-
var limitPastMvExpand = asLimit(eval.child(), 1000, false);
1638+
var limitPastMvExpand = asLimit(mvExpand.child(), 1000, false);
16411639
as(limitPastMvExpand.child(), EsRelation.class);
16421640
}
16431641

@@ -3147,16 +3145,15 @@ public void testPushDownMvExpandPastProject() {
31473145

31483146
/**
31493147
* <pre>{@code
3150-
* Project[[a{r}#13, b{r}#14]]
3148+
* Project[[a{r}#13, b{r}#14 AS b#8]]
31513149
* \_Limit[1000[INTEGER],true,false]
3152-
* \_MvExpand[$$a$b$0$b$0{r}#16,b{r}#14]
3153-
* \_Eval[[$$a$b$0{r}#15 AS $$a$b$0$b$0#16]]
3154-
* \_Limit[1000[INTEGER],true,false]
3155-
* \_MvExpand[a{r}#6,a{r}#13]
3156-
* \_Eval[[a{r}#6 AS $$a$b$0#15]]
3157-
* \_Limit[1000[INTEGER],false,false]
3158-
* \_Aggregate[[],[COUNT(*[KEYWORD],true[BOOLEAN],PT0S[TIME_DURATION]) AS a#6]]
3159-
* \_LocalRelation[[a{r}#4],Page{blocks=[IntVectorBlock[vector=ConstantIntVector[positions=1, value=1]]]}]]
3150+
* \_MvExpand[$$a$b$0{r}#15,b{r}#14]
3151+
* \_Limit[1000[INTEGER],true,false]
3152+
* \_MvExpand[a{r}#6,a{r}#13]
3153+
* \_Eval[[a{r}#6 AS $$a$b$0#15]]
3154+
* \_Limit[1000[INTEGER],false,false]
3155+
* \_Aggregate[[],[COUNT(*[KEYWORD],true[BOOLEAN],PT0S[TIME_DURATION]) AS a#6]]
3156+
* \_LocalRelation[[a{r}#4],Page{blocks=[IntVectorBlock[vector=ConstantIntVector[positions=1, value=1]]]}]
31603157
* }</pre>
31613158
*/
31623159
public void testPushDownMvExpandPastProject2() {
@@ -3169,10 +3166,9 @@ public void testPushDownMvExpandPastProject2() {
31693166
var project = as(plan, Project.class);
31703167
var limit = asLimit(project.child(), 1000, true);
31713168
var mvExpand = as(limit.child(), MvExpand.class);
3172-
var eval = as(mvExpand.child(), Eval.class);
3173-
limit = asLimit(eval.child(), 1000, true);
3169+
limit = asLimit(mvExpand.child(), 1000, true);
31743170
mvExpand = as(limit.child(), MvExpand.class);
3175-
eval = as(mvExpand.child(), Eval.class);
3171+
var eval = as(mvExpand.child(), Eval.class);
31763172
limit = asLimit(eval.child(), 1000, false);
31773173
var agg = as(limit.child(), Aggregate.class);
31783174
as(agg.child(), LocalRelation.class);

0 commit comments

Comments
 (0)