1212import org .elasticsearch .index .query .QueryBuilder ;
1313import org .elasticsearch .index .query .QueryRewriteContext ;
1414import org .elasticsearch .index .query .Rewriteable ;
15+ import org .elasticsearch .xpack .esql .core .expression .Expression ;
1516import org .elasticsearch .xpack .esql .core .util .Holder ;
1617import org .elasticsearch .xpack .esql .plan .logical .EsRelation ;
18+ import org .elasticsearch .xpack .esql .plan .logical .Fork ;
1719import org .elasticsearch .xpack .esql .plan .logical .LogicalPlan ;
1820import org .elasticsearch .xpack .esql .planner .TranslatorHandler ;
1921import org .elasticsearch .xpack .esql .plugin .TransportActionServices ;
2224import java .io .IOException ;
2325import java .util .HashSet ;
2426import java .util .Set ;
27+ import java .util .function .Function ;
28+ import java .util .stream .Collectors ;
2529
2630/**
2731 * Some {@link FullTextFunction} implementations such as {@link org.elasticsearch.xpack.esql.expression.function.fulltext.Match}
@@ -34,11 +38,7 @@ public final class QueryBuilderResolver {
3438 private QueryBuilderResolver () {}
3539
3640 public static void resolveQueryBuilders (LogicalPlan plan , TransportActionServices services , ActionListener <LogicalPlan > listener ) {
37- var hasFullTextFunctions = plan .anyMatch (p -> {
38- Holder <Boolean > hasFullTextFunction = new Holder <>(false );
39- p .forEachExpression (FullTextFunction .class , unused -> hasFullTextFunction .set (true ));
40- return hasFullTextFunction .get ();
41- });
41+ var hasFullTextFunctions = hasFullTextFunctions (plan );
4242 if (hasFullTextFunctions ) {
4343 Rewriteable .rewriteAndFetch (
4444 new FullTextFunctionsRewritable (plan ),
@@ -69,12 +69,29 @@ private static Set<String> indexNames(LogicalPlan plan) {
6969 return indexNames ;
7070 }
7171
72+ private static boolean hasFullTextFunctions (LogicalPlan plan ) {
73+ return plan .anyMatch (p -> {
74+ Holder <Boolean > hasFullTextFunction = new Holder <>(false );
75+ p .forEachExpression (FullTextFunction .class , unused -> hasFullTextFunction .set (true ));
76+
77+ if (p instanceof Fork fork ) {
78+ fork .subPlans ().forEach (subPlan -> {
79+ if (hasFullTextFunctions (subPlan )) {
80+ hasFullTextFunction .set (true );
81+ }
82+ });
83+ }
84+
85+ return hasFullTextFunction .get ();
86+ });
87+ }
88+
7289 private record FullTextFunctionsRewritable (LogicalPlan plan ) implements Rewriteable <QueryBuilderResolver .FullTextFunctionsRewritable > {
7390 @ Override
7491 public FullTextFunctionsRewritable rewrite (QueryRewriteContext ctx ) throws IOException {
7592 Holder <IOException > exceptionHolder = new Holder <>();
7693 Holder <Boolean > updated = new Holder <>(false );
77- LogicalPlan newPlan = plan . transformExpressionsDown ( FullTextFunction . class , f -> {
94+ LogicalPlan newPlan = transformPlan ( plan , f -> {
7895 QueryBuilder builder = f .queryBuilder (), initial = builder ;
7996 builder = builder == null ? f .asQuery (TranslatorHandler .TRANSLATOR_HANDLER ).toQueryBuilder () : builder ;
8097 try {
@@ -91,5 +108,15 @@ public FullTextFunctionsRewritable rewrite(QueryRewriteContext ctx) throws IOExc
91108 }
92109 return updated .get () ? new FullTextFunctionsRewritable (newPlan ) : this ;
93110 }
111+
112+ private LogicalPlan transformPlan (LogicalPlan plan , Function <FullTextFunction , ? extends Expression > rule ) {
113+ return plan .transformExpressionsDown (FullTextFunction .class , rule ).transformDown (Fork .class , fork -> {
114+ var subPlans = fork .subPlans ()
115+ .stream ()
116+ .map (subPlan -> subPlan .transformExpressionsDown (FullTextFunction .class , rule ))
117+ .collect (Collectors .toList ());
118+ return new Fork (fork .source (), fork .child (), subPlans );
119+ });
120+ }
94121 }
95122}
0 commit comments