From 45e17688ff93e38d7557d10587c0753d5903c1d5 Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:33:21 +0100 Subject: [PATCH 1/6] Add ESQL CPS doc --- explore-analyze/cross-project-search.md | 1 + .../cross-project-search-esql.md | 347 ++++++++++++++++++ explore-analyze/toc.yml | 1 + 3 files changed, 349 insertions(+) create mode 100644 explore-analyze/cross-project-search/cross-project-search-esql.md diff --git a/explore-analyze/cross-project-search.md b/explore-analyze/cross-project-search.md index 4ac3adef64..f078064a6f 100644 --- a/explore-analyze/cross-project-search.md +++ b/explore-analyze/cross-project-search.md @@ -21,6 +21,7 @@ For details on how search, tags, and project routing work in {{cps-init}}, refer * [Search in {{cps-init}}](/explore-analyze/cross-project-search/cross-project-search-search.md): learn how search expressions, search options, and index resolution work. * [Tags in {{cps-init}}](/explore-analyze/cross-project-search/cross-project-search-tags.md): learn about predefined and custom project tags and how to use them in queries. * [Project routing in {{cps-init}}](/explore-analyze/cross-project-search/cross-project-search-project-routing.md): learn how to route searches to specific projects based on tag values. +* [Query across serverless projects with {{esql}}](/explore-analyze/cross-project-search/cross-project-search-esql.md): {{esql}}-specific syntax for index resolution, project routing, and accessing project metadata. ## {{cps-cap}} as the default behavior for linked projects diff --git a/explore-analyze/cross-project-search/cross-project-search-esql.md b/explore-analyze/cross-project-search/cross-project-search-esql.md new file mode 100644 index 0000000000..7299c332a9 --- /dev/null +++ b/explore-analyze/cross-project-search/cross-project-search-esql.md @@ -0,0 +1,347 @@ +--- +applies_to: + stack: unavailable + serverless: preview +products: + - id: elasticsearch +description: Learn how to use the ES|QL language in Elasticsearch to query across multiple Serverless projects. Learn about index resolution, project routing, and accessing project metadata. +navigation_title: "CPS with ES|QL" +--- + +# Query across {{serverless-short}} projects with {{esql}} + +[Cross-project search](/explore-analyze/cross-project-search.md) ({{cps-init}}) enables you to run queries across multiple [linked {{serverless-short}} projects](/explore-analyze/cross-project-search.md#project-linking) from a single request. + +There are several ways to control which projects a query runs against: + +- **[Query all projects](#query-all-projects-default)**: If you just want to query across all linked projects, no special syntax is required. Queries automatically run against the origin and all linked projects by default. +- **[Use project routing](#use-project-routing)**: Use project routing for project-level filtering before query execution. Excluded projects are not queried. +- **[Use search expressions](#use-search-expressions)**: Use search expressions for fine-grained control over which projects and indices are queried, by qualifying index names with a project identifier. Search expressions can be used independently or combined with project routing. + +## Before you begin + +This page covers {{esql}}-specific CPS behavior. Before continuing, make sure you are familiar with the following: + +* [{{cps-cap}}](/explore-analyze/cross-project-search.md) +* [Linked projects](/explore-analyze/cross-project-search/cross-project-search-link-projects.md) +* [How search works in CPS](/explore-analyze/cross-project-search/cross-project-search-search.md) +* [Project routing in CPS](/explore-analyze/cross-project-search/cross-project-search-project-routing.md) +* [Tags in CPS](/explore-analyze/cross-project-search/cross-project-search-tags.md) + +## Query all projects (default) + +The default behavior is to query across the origin project and all linked projects automatically. +The following example queries the `data` index and includes the `_index` metadata field to identify which project each result came from: + +```esql +GET /_query +{ + "query": "FROM data METADATA _index" <1> +} +``` + +1. `METADATA _index` returns the fully-qualified index name for each document. Documents from linked projects include the project alias prefix, for example `linked-project-1:data`. + +The response includes: +- a `_clusters` object showing the status of each participating project +- a `values` array where each row includes the qualified index name identifying which project the document came from + +:::{dropdown} Example response +```json +{ + "took": 329, + "is_partial": false, + "columns": [ + { "name": "_index", "type": "keyword" } + ], + "values": [ + ["data"], <1> + ["linked-project-1:data"] <2> + ], + "_clusters": { + "total": 2, + "successful": 2, + "running": 0, + "skipped": 0, + "partial": 0, + "failed": 0, + "details": { + "_origin": { <3> + "status": "successful", + "indices": "data", + "took": 328, + "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 } + }, + "linked-project-1": { <4> + "status": "successful", + "indices": "data", + "took": 256, + "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 } + } + } + } +} +``` + +1. Documents from the origin project show an unqualified index name. +2. Documents from linked projects show a qualified index name: the project alias, a colon, then the index name. +3. `_origin` is the reserved identifier for the origin project. +4. Each linked project is identified by its project alias. +::: + +## Use project routing + +[Project routing](/explore-analyze/cross-project-search/cross-project-search-project-routing.md) is a project-level filter that limits which projects are queried, based on tag values. +Project routing happens before query execution, so excluded projects receive no requests. This can help reduce cost and latency. + +:::{note} +Project routing expressions use Lucene query syntax. The `:` operator matches a tag value, equivalent to `=` in other query languages. For example, `_alias:my-project` matches projects whose alias is `my-project`. +::: + +You can specify project routing in two ways: + +- [Embed project routing in the query with `SET`](#option-1-use-the-set-source-command): This approach works wherever you can write an {{esql}} query. +- [Pass project routing in the `_query` API request body](#option-2-pass-project_routing-in-the-api-request-body): You can pass a `project_routing` field to keep project routing logic separate from the query string. + +:::{important} +If both options are combined, `SET project_routing` takes precedence. +::: + +### Option 1: Use the `SET` source command + +`SET project_routing` embeds project routing directly within the {{esql}} query. You can use this approach wherever you write {{esql}}. + +```esql +SET project_routing="_alias:my-project"; <1> +FROM data +| STATS COUNT(*) +``` + +1. `SET project_routing` must appear before other {{esql}} commands. The semicolon separates it from the rest of the query. + +### Option 2: Pass `project_routing` in the API request body + +If you are constructing the full `_query` request, you can pass the `project_routing` field in the request body. This keeps project routing logic separate from the query string: + +```json +GET /_query +{ + "query": "FROM data | STATS COUNT(*)", + "project_routing": "_alias:my-project" <1> +} +``` + +1. Routes the query to projects whose alias matches `my-project`. + +### Reference a named project routing expression + +Both options support referencing a named project routing expression using the `@` prefix. +Before you can reference a named expression, you must create it using the `_project_routing` API. +For instructions, refer to [Using named project routing expressions](/explore-analyze/cross-project-search/cross-project-search-project-routing.md#named-routing-expressions). + +::::{tab-set} + +:::{tab-item} Request body +```json +GET /_query +{ + "query": "FROM logs | STATS COUNT(*)", + "project_routing": "@custom-expression" +} +``` +::: + +:::{tab-item} SET directive +```esql +SET project_routing="@custom-expression"; +FROM logs +| STATS COUNT(*) +``` +::: + +:::: + +## Use search expressions + +{{esql}} supports [unqualified and qualified search expressions](/explore-analyze/cross-project-search/cross-project-search-search.md#search-expressions), which provide fine-grained control over which projects and indices a query runs against. +Prefix an index name with a project identifier to restrict the query to a specific project and its indices. + +### Restrict to the origin project + +Use `_origin:` to target only the project from which the query is run: + +```esql +FROM _origin:data <1> +| STATS COUNT(*) +``` + +1. `_origin` always refers to the origin project, regardless of its alias. + +### Restrict to a specific linked project + +Prefix the index name with the linked project's alias: + +```esql +FROM linked-project-1:data <1> +| STATS COUNT(*) +``` + +1. Replace `linked-project-1` with the actual project alias. + +### Exclude specific projects + +Prefix a search expression with `-` to exclude it from the resolved set. +The following example uses `-_origin:*` to exclude all indices from the origin project: + +```esql +FROM data,-_origin:* <1> +| STATS COUNT(*) +``` + +1. `data` is resolved across all projects except the origin project. + +::::{note} +`*:` in {{cps-init}} does not behave like `*:` in cross-cluster search (CCS): + +- In CCS, `*:` targets all remote clusters and excludes the local cluster. +- In {{cps-init}}, `*:` resolves against all projects including the origin, the same as an unqualified expression. +:::: + +### Combine qualified and unqualified expressions + +You can mix unqualified and qualified expressions in the same query: + +```esql +FROM data, _origin:logs <1> +| LIMIT 100 +``` + +1. `data` is resolved across all projects. `_origin:logs` is resolved only in the origin project. + +::::{tip} +Error handling differs between expression types. Unqualified expressions fail only if the index exists in none of the searched projects. Qualified expressions fail if the index is missing from the targeted project, regardless of whether it exists elsewhere. +For a detailed explanation, refer to [Unqualified expression behavior](/explore-analyze/cross-project-search/cross-project-search-search.md#behavior-unqualified). +:::: + +## Include project metadata in results + +Use the `METADATA` keyword in a `FROM` command to include project-level information alongside query results. +Project metadata fields use the `_project.` prefix to distinguish them from document fields. + +You can use project metadata fields in two ways: + +* As columns in returned result rows, to identify which project each document came from. +* In downstream commands such as `WHERE`, `STATS`, and `KEEP`, to filter, aggregate, or sort results by project. + +Available fields include all predefined tags and any custom tags you have defined. +You can also use wildcard patterns such as `_project.my-prefix*` or `_project.*`. + +For a full list of predefined tags, refer to [Tags in CPS](/explore-analyze/cross-project-search/cross-project-search-tags.md). + +::::{important} +You must declare a project metadata field in the `METADATA` clause to use it anywhere in the query, including in `WHERE`, `STATS`, `KEEP`, and other downstream commands. +:::: + +### Return project alias alongside results + +Include `_project._alias` in `METADATA` to add the project alias as a column on each result row: + +```esql +FROM logs* METADATA _project._alias <1> +| KEEP @timestamp, message, _project._alias +``` + +1. Declaring `_project._alias` in `METADATA` makes it available in `KEEP` and other downstream commands. + +:::{dropdown} Example response +```json +{ + "took": 47, + "is_partial": false, + "columns": [ + { "name": "@timestamp", "type": "date" }, + { "name": "message", "type": "keyword" }, + { "name": "_project._alias", "type": "keyword" } + ], + "values": [ + ["2025-01-15T10:23:00.000Z", "connection established", "origin-project"], <1> + ["2025-01-15T10:24:00.000Z", "request timeout", "linked-project-1"], <2> + ["2025-01-15T10:25:00.000Z", "disk full", "linked-project-1"] + ] +} +``` + +1. Documents from the origin project show its project alias. +2. Documents from linked projects show the linked project's alias. +::: + +### Aggregate results by project + +Include `_project._alias` in `METADATA` to group and count results by project: + +```esql +FROM logs* METADATA _project._alias <1> +| STATS doc_count = COUNT(*) BY _project._alias +``` + +1. `_project._alias` must be in `METADATA` to use it in `STATS ... BY`. + +### Filter results by project tag + +A project tag in a `WHERE` clause filters the result set after the query runs across all projects. It does not limit which projects are queried. + +The following examples show the difference between filtering with `WHERE` and restricting the query scope with project routing. + +#### Filter with `WHERE` (post-query) + +```esql +FROM logs* METADATA _project._csp <1> +| WHERE _project._csp == "aws" <2> +``` + +1. Declare the tag in `METADATA` to use it in downstream commands. +2. All linked projects are queried. Only results from AWS projects are returned. + +::::{important} +Filtering with `WHERE` on a project tag happens after all projects are queried. To prevent unnecessary queries, use [project routing](#use-project-routing) to select projects before execution. +:::: + +#### Restrict with project routing (pre-query) + +```esql +SET project_routing="_alias:aws-project"; <1> +FROM logs* +| STATS COUNT(*) +``` + +1. Only `aws-project` is queried. No data is fetched from other projects. For supported project routing tags, refer to [Limitations](#limitations). + +### Use project routing and METADATA together + +Project routing and project metadata serve different purposes and are independent of each other. +Project routing determines which projects are queried, before execution. +METADATA makes tag values available in query results and downstream commands, at query time. + +Using a tag in `METADATA` does not route the query. Using project routing does not populate `METADATA` fields. +To both restrict queried projects and include tag values in results, specify both: + +```esql +SET project_routing="_alias:my-project"; <1> +FROM logs METADATA _project._alias <2> +| STATS COUNT(*) BY _project._alias +``` + +1. Routes the query to `my-project` only. +2. Declares `_project._alias` so it can be used in `STATS`. + +## Limitations + +### Project routing supports alias only + +Currently, project routing in {{esql}} only supports the `_alias` tag. +Other predefined tags (`_csp`, `_region`, and so on) and custom tags are not yet supported as project routing criteria. + +### LOOKUP JOIN across projects + +{{esql}} `LOOKUP JOIN` follows the same constraints as [{{esql}} cross-cluster `LOOKUP JOIN`](elasticsearch://reference/query-languages/esql/esql-lookup-join.md#cross-cluster-support). +The lookup index must exist on every project being queried, because each project uses its own local copy of the lookup index data. diff --git a/explore-analyze/toc.yml b/explore-analyze/toc.yml index cb975b6b70..06c2668777 100644 --- a/explore-analyze/toc.yml +++ b/explore-analyze/toc.yml @@ -188,6 +188,7 @@ toc: - hidden: cross-project-search/cross-project-search-search.md - hidden: cross-project-search/cross-project-search-tags.md - hidden: cross-project-search/cross-project-search-project-routing.md + - hidden: cross-project-search/cross-project-search-esql.md - file: ai-features.md children: - file: ai-features/elastic-agent-builder.md From 6709404b4e6249b7686c65bbd261af0291783515 Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:05:24 +0100 Subject: [PATCH 2/6] Address review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use {{cps-init}} instead of CPS in "Before you begin" - Use "scope" terminology instead of "filtering" for project-level selection - "project identifier" → "project alias" in search expressions intro - Mark REST API request blocks as console instead of esql/json - Add include_execution_metadata: true to default query example - Link SET command docs; clarify SET-first rule and multi-param ordering - Project routing: "excluded projects are never queried" - Add WHERE caveat with link to dedicated section in metadata bullet - WHERE tip: "To optimize a query" instead of "To prevent unnecessary queries" - Replace single-project routing+METADATA example with multi-project _alias:*linked* example - Limitations: remove "in esql" qualifier; "Initially" instead of "Currently" --- .../cross-project-search-esql.md | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/explore-analyze/cross-project-search/cross-project-search-esql.md b/explore-analyze/cross-project-search/cross-project-search-esql.md index 7299c332a9..1ca0100ff4 100644 --- a/explore-analyze/cross-project-search/cross-project-search-esql.md +++ b/explore-analyze/cross-project-search/cross-project-search-esql.md @@ -15,12 +15,12 @@ navigation_title: "CPS with ES|QL" There are several ways to control which projects a query runs against: - **[Query all projects](#query-all-projects-default)**: If you just want to query across all linked projects, no special syntax is required. Queries automatically run against the origin and all linked projects by default. -- **[Use project routing](#use-project-routing)**: Use project routing for project-level filtering before query execution. Excluded projects are not queried. -- **[Use search expressions](#use-search-expressions)**: Use search expressions for fine-grained control over which projects and indices are queried, by qualifying index names with a project identifier. Search expressions can be used independently or combined with project routing. +- **[Use project routing](#use-project-routing)**: Use project routing to limit the scope of your search to specific projects before query execution. Excluded projects are not queried. +- **[Use search expressions](#use-search-expressions)**: Use search expressions for fine-grained control over which projects and indices are queried, by qualifying index names with a project alias. Search expressions can be used independently or combined with project routing. ## Before you begin -This page covers {{esql}}-specific CPS behavior. Before continuing, make sure you are familiar with the following: +This page covers {{esql}}-specific {{cps-init}} behavior. Before continuing, make sure you are familiar with the following: * [{{cps-cap}}](/explore-analyze/cross-project-search.md) * [Linked projects](/explore-analyze/cross-project-search/cross-project-search-link-projects.md) @@ -33,14 +33,16 @@ This page covers {{esql}}-specific CPS behavior. Before continuing, make sure yo The default behavior is to query across the origin project and all linked projects automatically. The following example queries the `data` index and includes the `_index` metadata field to identify which project each result came from: -```esql +```console GET /_query { - "query": "FROM data METADATA _index" <1> + "query": "FROM data METADATA _index", <1> + "include_execution_metadata": true <2> } ``` 1. `METADATA _index` returns the fully-qualified index name for each document. Documents from linked projects include the project alias prefix, for example `linked-project-1:data`. +2. Required to include the `_clusters` object in the response. Defaults to `false`. The response includes: - a `_clusters` object showing the status of each participating project @@ -91,8 +93,8 @@ The response includes: ## Use project routing -[Project routing](/explore-analyze/cross-project-search/cross-project-search-project-routing.md) is a project-level filter that limits which projects are queried, based on tag values. -Project routing happens before query execution, so excluded projects receive no requests. This can help reduce cost and latency. +[Project routing](/explore-analyze/cross-project-search/cross-project-search-project-routing.md) limits the scope of a query to specific projects, based on tag values. +Project routing happens before query execution, so excluded projects are never queried. This can help reduce cost and latency. :::{note} Project routing expressions use Lucene query syntax. The `:` operator matches a tag value, equivalent to `=` in other query languages. For example, `_alias:my-project` matches projects whose alias is `my-project`. @@ -117,13 +119,13 @@ FROM data | STATS COUNT(*) ``` -1. `SET project_routing` must appear before other {{esql}} commands. The semicolon separates it from the rest of the query. +1. [`SET`](elasticsearch://reference/query-languages/esql/commands/set) must be the first command in the query. If multiple parameters are configured within `SET`, `project_routing` does not need to be listed first. For example, `SET unmapped_fields="LOAD"; project_routing="_alias:my-project";` is valid. The semicolon after the last parameter separates `SET` from the rest of the query. ### Option 2: Pass `project_routing` in the API request body If you are constructing the full `_query` request, you can pass the `project_routing` field in the request body. This keeps project routing logic separate from the query string: -```json +```console GET /_query { "query": "FROM data | STATS COUNT(*)", @@ -142,7 +144,7 @@ For instructions, refer to [Using named project routing expressions](/explore-an ::::{tab-set} :::{tab-item} Request body -```json +```console GET /_query { "query": "FROM logs | STATS COUNT(*)", @@ -231,7 +233,7 @@ Project metadata fields use the `_project.` prefix to distinguish them from docu You can use project metadata fields in two ways: * As columns in returned result rows, to identify which project each document came from. -* In downstream commands such as `WHERE`, `STATS`, and `KEEP`, to filter, aggregate, or sort results by project. +* In downstream commands such as `WHERE`, `STATS`, and `KEEP`, to filter, aggregate, or sort results by project. Note: `WHERE` [filters results after all projects are queried](#filter-results-by-project-tag) and does not limit query scope. Available fields include all predefined tags and any custom tags you have defined. You can also use wildcard patterns such as `_project.my-prefix*` or `_project.*`. @@ -303,7 +305,7 @@ FROM logs* METADATA _project._csp <1> 2. All linked projects are queried. Only results from AWS projects are returned. ::::{important} -Filtering with `WHERE` on a project tag happens after all projects are queried. To prevent unnecessary queries, use [project routing](#use-project-routing) to select projects before execution. +Filtering with `WHERE` on a project tag happens after all projects are queried. To optimize a query, use [project routing](#use-project-routing) to select projects before execution. :::: #### Restrict with project routing (pre-query) @@ -326,19 +328,19 @@ Using a tag in `METADATA` does not route the query. Using project routing does n To both restrict queried projects and include tag values in results, specify both: ```esql -SET project_routing="_alias:my-project"; <1> -FROM logs METADATA _project._alias <2> +SET project_routing="_alias:*linked*"; <1> +FROM logs METADATA _project._alias <2> | STATS COUNT(*) BY _project._alias ``` -1. Routes the query to `my-project` only. -2. Declares `_project._alias` so it can be used in `STATS`. +1. Routes the query to projects whose alias matches `*linked*`. Only those projects are queried. +2. Declares `_project._alias` so it can be used in `STATS`. Results show a count per matched project. ## Limitations ### Project routing supports alias only -Currently, project routing in {{esql}} only supports the `_alias` tag. +Initially, project routing only supports the `_alias` tag. Other predefined tags (`_csp`, `_region`, and so on) and custom tags are not yet supported as project routing criteria. ### LOOKUP JOIN across projects From 012775bab7a3a79d7ca5f03b087af7dd8c329196 Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:51:26 +0100 Subject: [PATCH 3/6] Move SET directive rules into prose, simplify callout --- .../cross-project-search/cross-project-search-esql.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/explore-analyze/cross-project-search/cross-project-search-esql.md b/explore-analyze/cross-project-search/cross-project-search-esql.md index 1ca0100ff4..8490ddc3d3 100644 --- a/explore-analyze/cross-project-search/cross-project-search-esql.md +++ b/explore-analyze/cross-project-search/cross-project-search-esql.md @@ -111,7 +111,7 @@ If both options are combined, `SET project_routing` takes precedence. ### Option 1: Use the `SET` source command -`SET project_routing` embeds project routing directly within the {{esql}} query. You can use this approach wherever you write {{esql}}. +`SET project_routing` embeds project routing directly within the {{esql}} query. You can use this approach wherever you write {{esql}}. [`SET`](elasticsearch://reference/query-languages/esql/commands/set.md) must appear before other {{esql}} commands. The semicolon after the last parameter separates it from the rest of the query. The order of parameters within `SET` does not matter. ```esql SET project_routing="_alias:my-project"; <1> @@ -119,7 +119,7 @@ FROM data | STATS COUNT(*) ``` -1. [`SET`](elasticsearch://reference/query-languages/esql/commands/set) must be the first command in the query. If multiple parameters are configured within `SET`, `project_routing` does not need to be listed first. For example, `SET unmapped_fields="LOAD"; project_routing="_alias:my-project";` is valid. The semicolon after the last parameter separates `SET` from the rest of the query. +1. Routes the query to projects whose alias is `my-project`. ### Option 2: Pass `project_routing` in the API request body From 8e9d245c914716e0c36a8ea3f25fb532227df77e Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:31:32 +0100 Subject: [PATCH 4/6] @naj-h's fixes --- .../cross-project-search-esql.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/explore-analyze/cross-project-search/cross-project-search-esql.md b/explore-analyze/cross-project-search/cross-project-search-esql.md index 8490ddc3d3..d6d17f8c7f 100644 --- a/explore-analyze/cross-project-search/cross-project-search-esql.md +++ b/explore-analyze/cross-project-search/cross-project-search-esql.md @@ -16,7 +16,7 @@ There are several ways to control which projects a query runs against: - **[Query all projects](#query-all-projects-default)**: If you just want to query across all linked projects, no special syntax is required. Queries automatically run against the origin and all linked projects by default. - **[Use project routing](#use-project-routing)**: Use project routing to limit the scope of your search to specific projects before query execution. Excluded projects are not queried. -- **[Use search expressions](#use-search-expressions)**: Use search expressions for fine-grained control over which projects and indices are queried, by qualifying index names with a project alias. Search expressions can be used independently or combined with project routing. +- **[Use index expressions](#use-index-expressions)**: Use index expressions for fine-grained control over which projects and indices are queried, by qualifying index names with a project alias. Search expressions can be used independently or combined with project routing. ## Before you begin @@ -139,7 +139,7 @@ GET /_query Both options support referencing a named project routing expression using the `@` prefix. Before you can reference a named expression, you must create it using the `_project_routing` API. -For instructions, refer to [Using named project routing expressions](/explore-analyze/cross-project-search/cross-project-search-project-routing.md#named-routing-expressions). +For instructions, refer to [Using named project routing expressions](/explore-analyze/cross-project-search/cross-project-search-project-routing.md#creating-and-managing-named-project-routing-expressions). ::::{tab-set} @@ -163,10 +163,12 @@ FROM logs :::: -## Use search expressions +## Use index expressions -{{esql}} supports [unqualified and qualified search expressions](/explore-analyze/cross-project-search/cross-project-search-search.md#search-expressions), which provide fine-grained control over which projects and indices a query runs against. -Prefix an index name with a project identifier to restrict the query to a specific project and its indices. +{{esql}} supports two types of [index expressions](/explore-analyze/cross-project-search/cross-project-search-search.md#search-expressions): + +- **Unqualified expressions** have no project prefix and search across all projects. Example: `logs*`. +- **Qualified expressions** include a project alias prefix to target a specific project or set of projects. Example: `project1:logs*`. ### Restrict to the origin project @@ -192,7 +194,7 @@ FROM linked-project-1:data <1> ### Exclude specific projects -Prefix a search expression with `-` to exclude it from the resolved set. +Prefix an index expression with `-` to exclude it from the resolved set. The following example uses `-_origin:*` to exclude all indices from the origin project: ```esql @@ -203,7 +205,7 @@ FROM data,-_origin:* <1> 1. `data` is resolved across all projects except the origin project. ::::{note} -`*:` in {{cps-init}} does not behave like `*:` in cross-cluster search (CCS): +`*:` in {{cps-init}} does not behave like `*:` in [cross-cluster search (CCS)](elasticsearch://reference/query-languages/esql/esql-cross-clusters.md) (which is used to query across clusters in non-serverless deployments): - In CCS, `*:` targets all remote clusters and excludes the local cluster. - In {{cps-init}}, `*:` resolves against all projects including the origin, the same as an unqualified expression. From 7baf1c61c13d3be9fc847374c6e47d6c846382b0 Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:25:20 +0100 Subject: [PATCH 5/6] fix plural --- .../cross-project-search/cross-project-search-esql.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/explore-analyze/cross-project-search/cross-project-search-esql.md b/explore-analyze/cross-project-search/cross-project-search-esql.md index d6d17f8c7f..9bb1ae05ad 100644 --- a/explore-analyze/cross-project-search/cross-project-search-esql.md +++ b/explore-analyze/cross-project-search/cross-project-search-esql.md @@ -119,7 +119,7 @@ FROM data | STATS COUNT(*) ``` -1. Routes the query to projects whose alias is `my-project`. +1. Routes the query to the project with alias `my-project`. ### Option 2: Pass `project_routing` in the API request body @@ -332,8 +332,7 @@ To both restrict queried projects and include tag values in results, specify bot ```esql SET project_routing="_alias:*linked*"; <1> FROM logs METADATA _project._alias <2> -| STATS COUNT(*) BY _project._alias -``` +| STATS COUNT(*) BY _project._alias``` 1. Routes the query to projects whose alias matches `*linked*`. Only those projects are queried. 2. Declares `_project._alias` so it can be used in `STATS`. Results show a count per matched project. From 01a70b50a5776b8ced9604f8f3eff06cf8ebf8cd Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:04:43 +0100 Subject: [PATCH 6/6] formatting --- .../cross-project-search/cross-project-search-esql.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/explore-analyze/cross-project-search/cross-project-search-esql.md b/explore-analyze/cross-project-search/cross-project-search-esql.md index 9bb1ae05ad..c7d9cc014d 100644 --- a/explore-analyze/cross-project-search/cross-project-search-esql.md +++ b/explore-analyze/cross-project-search/cross-project-search-esql.md @@ -332,7 +332,8 @@ To both restrict queried projects and include tag values in results, specify bot ```esql SET project_routing="_alias:*linked*"; <1> FROM logs METADATA _project._alias <2> -| STATS COUNT(*) BY _project._alias``` +| STATS COUNT(*) BY _project._alias +``` 1. Routes the query to projects whose alias matches `*linked*`. Only those projects are queried. 2. Declares `_project._alias` so it can be used in `STATS`. Results show a count per matched project.