Class ViewCompaction

java.lang.Object
org.elasticsearch.xpack.esql.rule.Rule<LogicalPlan,LogicalPlan>
org.elasticsearch.xpack.esql.view.ViewCompaction

public class ViewCompaction extends Rule<LogicalPlan,LogicalPlan>
Compacts the nested plan produced by ViewResolver into the form expected by the rest of the query pipeline. The work is split into two phases so that ViewShadowRelation siblings (CPS lenient lookups) survive long enough to be paired with their strict UnresolvedRelation sibling at field-caps time:
  1. preIndexResolution(LogicalPlan) — runs from EsqlSession before PreAnalyzer. Reshapes user-written Subquery/UnionAll structures into ViewUnionAll so the analyzer sees a uniform tree shape. Leaves shadows in place; leaves nested ViewUnionAlls nested. The UnresolvedRelation index patterns it leaves in the tree are exactly what PreAnalyzer hands to field-caps and what ResolveTable later looks up.
  2. postIndexResolution(LogicalPlan) — runs as an analyzer rule after ResolveTable. Strips any ViewShadowRelation that lenient field-caps did not fold into a sibling EsRelation (in Phase A this is all of them, since lenient field-caps is not yet wired up — see esql-planning#543), then flattens nested ViewUnionAlls and unwraps remaining NamedSubquery wrappers.

The split is what lets a colleague implement lenient field-caps purely as a Phase B analyzer rule that lives between ResolveTable and postIndexResolution(org.elasticsearch.xpack.esql.plan.logical.LogicalPlan): shadows that match remote indices get rewritten to UnresolvedRelation/EsRelation; shadows that fail to match are simply left unresolved and postIndexResolution(org.elasticsearch.xpack.esql.plan.logical.LogicalPlan) sweeps them away. Ordering details: see esql-planning#472.

Note: a small amount of compaction stays inside ViewResolver.buildPlanFromBranches(org.elasticsearch.xpack.esql.plan.logical.UnresolvedRelation, java.util.List<org.elasticsearch.xpack.esql.view.ViewResolver.ViewPlan>, int) — specifically the per-level sibling UnresolvedRelation merge — to keep the resolved tree compact at the per-level boundary, so wide branching levels of compactable views (e.g. FROM v1, v2, ... v9) collapse to a single UnresolvedRelation rather than tripping Fork.MAX_BRANCHES at post-analysis verification.