Skip to content

ESQL: Views REST API#137818

Merged
elasticsearchmachine merged 108 commits intoelastic:mainfrom
craigtaverner:views_rest_crud
Dec 10, 2025
Merged

ESQL: Views REST API#137818
elasticsearchmachine merged 108 commits intoelastic:mainfrom
craigtaverner:views_rest_crud

Conversation

@craigtaverner
Copy link
Copy Markdown
Contributor

@craigtaverner craigtaverner commented Nov 10, 2025

This is extracted from the Views Prototype at #134995 and is originally based on the "ESQL: View" prototype by Nik but with the following changes:

  • No ES|QL language integration, only REST API for adding/removing and managing Views in cluster state
  • ViewMetadata now inside ProjectMetadata
  • NodeFeature to ensure metadata only exists in clusters fully upgraded
  • Wide CRUD support (add, update, delete, list)
  • View name validation
  • View count, length and depth restrictions

The REST API looks like:

PUT _query/view/critical_service_errors_v {"query":"FROM ..."}
DELETE _query/view/critical_service_errors_v
GET _query/view/critical_service_errors_v
GET _query/view

Checklist:

  • Extract REST API from views prototype
  • Remove list interface and generalise GET command to cover list use cases (more in-line with other APIs)
  • Verify that snapshot-only really blocks registration of REST API
  • Consider switching to use feature flags (instead of snapshot-only)
  • Consider dropping one of NodeFeature or TransportVersion since they cover the same requirement
  • Implement batched tasks, probably using SequentialAckingBatchedTaskExecutor
  • Stop using ProjectResolver and instead use the ProjectID as is done in Enrich REST API
  • Change ViewMetadata from a Map of name to View to instead be an array of View (which should contain the name)

@craigtaverner craigtaverner added >feature :Data Management/Indices APIs DO NOT USE. Use ":Distributed/Indices APIs" or ":StorageEngine/Templates" instead. Team:Data Management (obsolete) DO NOT USE. This team no longer exists. Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) :Analytics/ES|QL AKA ESQL labels Nov 10, 2025
@elasticsearchmachine
Copy link
Copy Markdown
Collaborator

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

@elasticsearchmachine
Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-data-management (Team:Data Management)

@elasticsearchmachine
Copy link
Copy Markdown
Collaborator

Hi @craigtaverner, I've created a changelog YAML for you.

@dakrone
Copy link
Copy Markdown
Member

dakrone commented Nov 10, 2025

Since these don't yet do anything, and will be part of multiple PRs before the feature is fully-fleshed out, I think it would be helpful for these to be behind a feature flag, so that they are not inadvertently released-and-then-changed.

@craigtaverner
Copy link
Copy Markdown
Contributor Author

Since these don't yet do anything, and will be part of multiple PRs before the feature is fully-fleshed out, I think it would be helpful for these to be behind a feature flag, so that they are not inadvertently released-and-then-changed.

We've made this SNAPSHOT only, which is essentially the same as a feature flag, and is the approach normally taken in the ES|QL team for cases like this where we do not yet want the feature to be released. However, I will add more tests that explicitly assert that the feature is missing outside of SNAPSHOT builds.

@dakrone
Copy link
Copy Markdown
Member

dakrone commented Nov 10, 2025

We've made this SNAPSHOT only, which is essentially the same as a feature flag, and is the approach normally taken in the ES|QL team for cases like this where we do not yet want the feature to be released. However, I will add more tests that explicitly assert that the feature is missing outside of SNAPSHOT builds.

As I read this, that is for the implementation itself, but does not prevent the REST APIs from being registered and available, even without the node feature. Unless I am misunderstanding it?

@dakrone
Copy link
Copy Markdown
Member

dakrone commented Nov 10, 2025

(Another drive-by comment, but this is not a full review)

Why do we have both a "get" and "list" action/API for these? All of our other APIs that follow this model register a single get REST handler that takes an optional {name} parameter, returning all the <things> when the {name} is missing.

@craigtaverner craigtaverner force-pushed the views_rest_crud branch 2 times, most recently from bb1ce21 to 89f766e Compare November 10, 2025 17:43
@elasticsearchmachine
Copy link
Copy Markdown
Collaborator

Hi @craigtaverner, I've created a changelog YAML for you.

craigtaverner and others added 4 commits November 12, 2025 12:50
This is extracted from the Views Prototype at elastic#134995
and is originally based on the "ESQL: View" prototype by Nik but with the following changes:
* No ES|QL language integration, only REST API for adding/removing and managing Views in cluster state
* ViewMetadata now inside ProjectMetadata
* NodeFeature to ensure metadata only exists in clusters fully upgraded
* Wide CRUD support (add, update, delete, list)
* View name validation
* View count, length and depth restrictions

Co-authored-by: Nik Everett <nik9000@gmail.com>
Copy link
Copy Markdown
Contributor

@nielsbauman nielsbauman left a comment

Choose a reason for hiding this comment

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

Thanks for working on this, @craigtaverner! I scanned most parts of the code briefly and left a few comments, some of which are not a big deal.

* Simple implementation of {@link ClusterViewService} that keeps the views in memory.
* This is useful for testing.
*/
public class InMemoryViewService extends ViewService {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm probably missing something, but why do we need this in-memory version exactly?

And, follow-up question: wouldn't it make more sense to put that in a test package somewhere, rather than having it in production code, if it's only useful for testing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is only moderately useful here, but very useful in the actual ES|QL views were we have semi-unit tests that run the full ES|QL stack, but outside of Elasticsearch, and then this becomes the main mechanism for enabling that. I could indeed move it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In this PR it serves primarily as unit tests for the ViewService, without touching cluster state (so only tests the ViewService API internally). That still has value, so I'm happy to keep it here (although I moved to the test package, as you suggested).

submitUnbatchedTask("update-esql-view-metadata", new ClusterStateUpdateTask() {
@Override
public ClusterState execute(ClusterState currentState) {
var project = getProjectMetadata(currentState);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

getProjectMetadata uses the ProjectResolver, but the ProjectResolver is not meant to be used inside cluster state update tasks, as the thread context isn't copied over when running cluster state update tasks (which is what the project resolver relies on in multi-project mode). It's totally understandable that you would implement it like this; when the multi-project work gets picked up, we should put effort into making it clearer that the project resolver isn't supposed to work like this.

That means that you'll have to explicitly pass a ProjectId to this method. I would advise resolving the project ID in the transport actions, because:

I think having this getMetadata() method that relies on the project resolver can cause confusion and possible bugs, because it's not apparent at all that this method only works if the project ID is present in the thread context, which isn't always the case, such as here. I would advise doing something along the lines of:

protected ViewMetadata getMetadata(ProjectId projectId) {
    return getMetadata(clusterService.state().metadata().getProject(projectId));
}

protected ViewMetadata getMetadata(ProjectMetadata projectMetadata) {
    return projectMetadata.custom(ViewMetadata.TYPE, ViewMetadata.EMPTY);
}

Let me know what you think and if you have any questions/concerns.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I originally got this approach from the EnrichPolicyResolver, which uses ProjectResolver, but then also noticed that the EnrichPolicy CRUD used ProjectID instead, so it seems Enrich uses both approaches (but in different places, so probably correct). I can try switch to using ProjectID for CRUD as you suggest.

Copy link
Copy Markdown
Contributor

@nielsbauman nielsbauman left a comment

Choose a reason for hiding this comment

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

Left a few more minor comments, having reviewed everything now. Almost good to go from my side!


import static org.elasticsearch.xpack.esql.plugin.EsqlFeatures.ESQL_VIEWS_FEATURE_FLAG;

public class ViewRestTests extends AbstractViewTestCase {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are all the changes in ‎x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/view only for this test class? Or are you planning on adding more tests in the future? The amount of plumbing/infra we do for this test is excessive and unnecessary; that's why I'm asking.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

After this PR goes in work will start on further changes for it, so I will keep it at least for now. If in that work I end up not needing it more, I agree it would then be excessive and should go away.

Copy link
Copy Markdown
Contributor

@nielsbauman nielsbauman left a comment

Choose a reason for hiding this comment

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

LGTM, thanks a lot for all the iterations both @craigtaverner and @dakrone!

@dakrone dakrone added the test-release Trigger CI checks against release build label Dec 10, 2025
@dakrone dakrone added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label Dec 10, 2025
@dakrone dakrone enabled auto-merge (squash) December 10, 2025 22:54
@elasticsearchmachine elasticsearchmachine merged commit d1f2980 into elastic:main Dec 10, 2025
35 checks passed
@craigtaverner craigtaverner deleted the views_rest_crud branch December 10, 2025 23:25
dakrone added a commit to elastic/elasticsearch-specification that referenced this pull request Dec 11, 2025
This adds support for (get|put|delete)-view APIs to the specification

Relates to elastic/elasticsearch#137818
pquentin added a commit to elastic/elasticsearch-specification that referenced this pull request Dec 18, 2025
* Add specification for ESQL Views CRUD

This adds support for (get|put|delete)-view APIs to the specification

Relates to elastic/elasticsearch#137818

* Update after make contrib

* Apply suggestions from code review

* Fix lint and run make contrib

---------

Co-authored-by: Quentin Pradet <quentin.pradet@elastic.co>
@craigtaverner craigtaverner mentioned this pull request Dec 19, 2025
23 tasks
dakrone added a commit to dakrone/elasticsearch that referenced this pull request Dec 30, 2025
This commit adds the `View` class as the fourth `IndexAbstraction`. This means that it can be
"resolved" in the expression resolver, and moves us towards implementing the ES security model for
views. It also includes code that prevents conflicting views, indices, aliases, and data streams
with the same name. Additionally, we prevent indexing into a view or directly resolving it for a
search (if someone searches for `*` for instance).

Relates to elastic#137818
dakrone added a commit that referenced this pull request Jan 2, 2026
* Add ESQL View as an IndexAbstraction

This commit adds the `View` class as the fourth `IndexAbstraction`. This means that it can be
"resolved" in the expression resolver, and moves us towards implementing the ES security model for
views. It also includes code that prevents conflicting views, indices, aliases, and data streams
with the same name. Additionally, we prevent indexing into a view or directly resolving it for a
search (if someone searches for `*` for instance).

Relates to #137818

* Include views in comment

* Add earlier filtering of views in IndexNameExpressionResolver

* Use empty list of indices and null write index for View
samxbr pushed a commit to samxbr/elasticsearch that referenced this pull request Jan 5, 2026
* Add ESQL View as an IndexAbstraction

This commit adds the `View` class as the fourth `IndexAbstraction`. This means that it can be
"resolved" in the expression resolver, and moves us towards implementing the ES security model for
views. It also includes code that prevents conflicting views, indices, aliases, and data streams
with the same name. Additionally, we prevent indexing into a view or directly resolving it for a
search (if someone searches for `*` for instance).

Relates to elastic#137818

* Include views in comment

* Add earlier filtering of views in IndexNameExpressionResolver

* Use empty list of indices and null write index for View
craigtaverner added a commit that referenced this pull request Jan 29, 2026
Initial support for ES|QL Views. It is based on the underlying sub-query support added in ..., and builds on the Views REST API work done in #137818.

Checklist:

 - [x] Core prototype
   - [x] Extract key parts of Nik's original prototype
   - [x] Write testing infrastructure to test views within csv-spec tests (single and multi-node tests)
   - [x] Expand REST API to cover get, list, create/update, delete views 
   - [x] Add size and count limitations to stored view definitions
   - [x] Write testing infrastructure to test views in CsvTest (csv-spec unit testing)
   - [x] Expand scope to nested views
   - [x] Expand scope to multiple views and views+indexes
 - [x] Split into multiple PRs for easy review
   - [x] Multi-relation index resolution (gives CCS/CPS support)
     - [x] #136780
   - [x] More resilient EsqlSpecTestCase
     - [x] #136781
   - [x] View CRUD with REST
     - [x] #137818
   - [x] Rebase prototype on the above to get cleaner, simple feature PR
 - [ ] Enhancements
   - [x] Prototype CCS and CPS
   - [x] Enforce view/index name uniqueness (done in separate PR)
   - [x] Add serialization tests to test source deserialization in re-written plans
   - [x] Ensure views are gated behind SNAPSHOT and/or feature flag
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Analytics/ES|QL AKA ESQL auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) :Data Management/Indices APIs DO NOT USE. Use ":Distributed/Indices APIs" or ":StorageEngine/Templates" instead. >non-issue Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) Team:Data Management (obsolete) DO NOT USE. This team no longer exists. test-release Trigger CI checks against release build v9.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants