Skip to content

Conversation

craigtaverner
Copy link
Contributor

@craigtaverner craigtaverner commented Oct 19, 2025

This is extracted from the views prototype at #134995. It provides the underlying support for multiple FROM ... commands within the same ES|QL query, something that both non-materialized views and subqueries require.

A key feature of this work is that all UnresolvedRelation instances are treated equivalently, which means they all support CPS and CPS just as before. This implies that sub-queries using this will support CCS/CPS directly, and for views it means:

  • The underlying indexes within the view definitions will support CCS/CPS as if they were in the original FROM command
  • Views are strictly resolved in the coordinator of the local cluster, so that the indexes defined within the views can then be resolved using CCS/CPS rules as normal.

For reviewing this PR, it should be noted that most of the changes are in tests that had mistakes that went unnoticed because they only ever expected a single IndexPattern, and so could get away with mocking a single IndexResolution even if it was for a different index pattern than expressed in the test. The mismatch in index name between FROM and IndexResolution had no consequences. This is no longer possible since we need the IndexPattern to differentiate between different FROM commands. If you want to focus on the functional changes in this PR, look mostly at EsqlSession, AnalyzerContext, Analyzer and EsqlCCSUtils.

@craigtaverner craigtaverner added >non-issue Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) :Analytics/ES|QL AKA ESQL labels Oct 19, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-analytical-engine (Team:Analytics)

Copy link
Contributor

ℹ️ Important: Docs version tagging

👋 Thanks for updating the docs! Just a friendly reminder that our docs are now cumulative. This means all 9.x versions are documented on the same page and published off of the main branch, instead of creating separate pages for each minor version.

We use applies_to tags to mark version-specific features and changes.

Expand for a quick overview

When to use applies_to tags:

✅ At the page level to indicate which products/deployments the content applies to (mandatory)
✅ When features change state (e.g. preview, ga) in a specific version
✅ When availability differs across deployments and environments

What NOT to do:

❌ Don't remove or replace information that applies to an older version
❌ Don't add new information that applies to a specific version without an applies_to tag
❌ Don't forget that applies_to tags can be used at the page, section, and inline level

🤔 Need help?

This is extracted from the views prototype, which also includes necessary support for sub-queries.
Copy link
Member

@fang-xing-esql fang-xing-esql left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to Analyzer, AnalyzerContext and PreAnalyzer make sense to me. I applied the changes to the subquery PR, and subqueries can be resolved properly, thank you @craigtaverner !

The changes to EsqlSession and EsqlCCSUtils also look fine to me.

@craigtaverner craigtaverner mentioned this pull request Oct 21, 2025
20 tasks
;

country:text |language_name:keyword |MAX(language_code):integer |language_code:integer
country:keyword |language_name:keyword |MAX(language_code):integer |language_code:integer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious - why this change is happening in this pull?

}
}

static void handleFieldCapsFailures(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I get this part - why do we need two functions?

}

private LogicalPlan resolveInsist(Insist insist, List<Attribute> childrenOutput, IndexResolution indexResolution) {
private LogicalPlan resolveInsist(Insist insist, List<Attribute> childrenOutput, Collection<IndexResolution> indexResolution) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should use plural here if we're passing a collection now?


// Field is partially unmapped.
if (resolvedCol instanceof FieldAttribute fa && indexResolution.get().isPartiallyUnmappedField(fa.name())) {
// TODO: Should the check for partially unmapped fields be done specific to each sub-query in a fork?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand this part correctly - if multiple resolutions come with subqueries, aren't they scoped within the subqueries? How does it work - if I have a mapping in index in one subquery, I can't use it in another subquery, can I? What about propagating mappings up/down the subquery tree?

ignoreOrder:true
language_code:integer | country:text | language_name:keyword
2 | [Austria, Germany] | German
2 | [Germany, Austria] | German
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the order change?

public record PreAnalysis(
IndexMode indexMode,
IndexPattern indexPattern,
Map<IndexPattern, IndexMode> indexes,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Elastic code uses "indices" not "indexes"?

EsqlFunctionRegistry functionRegistry,
IndexResolution indexResolution,
Map<IndexPattern, IndexResolution> indexResolution,
Map<String, IndexResolution> lookupResolution,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here a comment why we index one by String and another by IndexPattern would help. I think I understand why (because lookup only uses single index while general case can have arbitrary expressions) but it took me some work to figure it out.

indexMode.set(p.indexMode());
indexPattern.set(p.indexPattern());
} else if (indexes.containsKey(p.indexPattern()) == false || indexes.get(p.indexPattern()) == p.indexMode()) {
indexes.put(p.indexPattern(), p.indexMode());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I get why we do the put if we already have exactly the same element already. Can't we simplify this as:

            } else {
                IndexMode previous = indexes.put(p.indexPattern(), p.indexMode());
                if(previous != null && previous != p.indexMode()) {
                    throw new IllegalStateException(
                        "index pattern '" + p.indexPattern() + "' found with with different index mode: " + previous + " != " + p.indexMode()
                    );
                }
            }

Also, when exactly can such a situation happen anyway? And why lookup mode is excluded from the check?

Holder<Boolean> supportsDenseVector = new Holder<>(false);
indexes.forEach((ip, mode) -> {
if (mode == IndexMode.TIME_SERIES) {
supportsAggregateMetricDouble.set(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I am not sure how it's supposed to work with subqueries. Should this be enabled for the whole query, or only for the subquery that this particular pattern belongs to? Maybe this is out of scope for this patch, just trying to figure out the concept.

@@ -0,0 +1,127 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit confused about how UnionAll relates to the rest of the patch. It's here but nothing else is mentioning or using it. Am I missing something in the picture?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Analytics/ES|QL AKA ESQL >non-issue Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants