Compare cross-project search and cross-cluster search

Cross-project search (CPS) provides Elastic Cloud Serverless with cross-project search capabilities similar to cross-cluster search (CCS).

Both features let you search data across multiple deployments from a single request, but the query syntax, default scope, and configuration requirements are different. In general, CPS reduces setup overhead and simplifies queries by searching all linked projects by default.

This page highlights the key differences and shows side-by-side query examples to help you transition from CCS to CPS.

The following sections describe how CCS and CPS differ in availability, setup, query syntax, and scope behavior.

Availability
CCS is for Elasticsearch clusters on self-managed, Elastic Cloud Enterprise, Elastic Cloud on Kubernetes, and Elastic Cloud Hosted deployments. CPS is for Elastic Cloud Serverless projects. Use CCS when searching across clusters and CPS when searching across serverless projects.
Prerequisites and configuration
CCS requires remote cluster connectivity and security configuration across clusters. CPS only requires project linking in the Elastic Cloud UI, with no transport-layer setup. For details, refer to CCS prerequisites and CPS prerequisites.
Cross-environment support
CCS can connect clusters across organizations and infrastructure boundaries. CPS is limited to projects within the same Elastic Cloud organization.
Default search scope
In CCS, a query runs against the local cluster only unless you explicitly include remote clusters. In CPS, a query runs against the origin project and all linked projects by default, so you don't need to rewrite queries as you link additional projects.
Naming
CCS uses remote cluster names as prefixes. CPS uses project aliases. Project aliases are derived from the project's connection alias, while remote cluster names come from the cluster settings configuration.
Referencing the local cluster or origin project
In CCS, the local cluster appears as (local) in search responses and has no explicit prefix in query expressions. If no prefix is provided, the query runs against the local cluster only. In CPS, the origin project appears as _origin in responses and can be targeted with the _origin: prefix in query expressions.
Missing resources
In CCS, searching for an index that doesn't exist on a cluster returns an error unless ignore_unavailable is set to true. In CPS, unqualified expressions succeed as long as the target resource exists in at least one searched project. Projects that don't have the resource are silently skipped, which means queries work without error even when projects have different index sets. Qualified expressions behave like CCS: if you target a specific project and the resource is missing, the request returns an error. For details, refer to Search in CPS.
Selecting which clusters or projects to search
In CCS, you select which clusters to search by listing cluster names or using wildcards on cluster names in the index expression. CPS introduces project routing, which selects projects based on project metadata, including project aliases, cloud provider, region, and custom tags. Project routing supports boolean logic (AND, OR, NOT), grouping with parentheses, and reusable named expressions. Routing is evaluated before the query runs, so excluded projects are never searched. CPS also provides a scope selector in Kibana apps for controlling which projects are searched without modifying queries.

The following tables summarize how common search tasks translate between CCS and CPS for Query DSL and ES|QL. The CCS examples assume a local cluster with a remote cluster named cluster_one. The CPS examples assume an origin project with a linked project whose alias is linked_project.

For full examples, refer to the examples section.

Task CCS CPS
Search local/origin only GET my-index/_search GET _origin:my-index/_search
Search one remote/linked GET cluster_one:my-index/_search GET linked_project:my-index/_search
Search all GET my-index,*:my-index/_search GET my-index/_search (default)
Exclude one GET my-index,*:my-index,-cluster_one:*/_search GET my-index,-linked_project:*/_search
Route by metadata Not available project_routing
Identify origin in responses (local) in _clusters; no prefix in _index _origin in _clusters; no prefix in _index
Identify remote/linked in responses cluster_one:my-index in _index linked_project:my-index in _index
Task CCS CPS
Search local/origin only FROM my-index FROM _origin:my-index
Search one remote/linked FROM cluster_one:my-index FROM linked_project:my-index
Search all FROM my-index,cluster_one:my-index FROM my-index (default)
Exclude one FROM my-index,*:my-index,-cluster_one:* FROM my-index,-linked_project:*
Route by metadata Not available SET project_routing
Identify origin in responses No prefix in METADATA _index No prefix in METADATA _index
Identify remote/linked in responses cluster_one:my-index in METADATA _index linked_project:my-index in METADATA _index

The following examples compare equivalent tasks in CCS and CPS. The CCS examples assume a local cluster with a remote cluster named cluster_one. The CPS examples assume an origin project with a linked project whose alias is linked_project.

Restrict a query to a single cluster or project without including results from remote clusters or linked projects. Use this when you need to isolate results to one cluster or project, for example when debugging a local issue.

In CCS, a plain index name targets the local cluster only. No prefix is needed.

_search

				GET my-index/_search
		

ES|QL

FROM my-index
| LIMIT 10
		

In CPS, a plain index name searches all projects. To restrict to the origin project, use the _origin: prefix.

_search

				GET _origin:my-index/_search
		

ES|QL

FROM _origin:my-index
| LIMIT 10
		

Target a specific remote cluster or linked project. Use this when you want to check data from a single cluster or project without pulling in results from others.

Prefix the index with the remote cluster name.

_search

				GET cluster_one:my-index/_search
		

ES|QL

FROM cluster_one:my-index
| LIMIT 10
		

Prefix the index with the linked project alias.

_search

				GET linked_project:my-index/_search
		

ES|QL

FROM linked_project:my-index
| LIMIT 10
		

Search an index across every available cluster or project at once. Use this when you want to correlate data from all clusters or projects, for example during an incident investigation that spans multiple clusters or projects.

List the local index and each remote cluster explicitly, or use a wildcard for remote clusters.

_search

				GET my-index,cluster_one:my-index/_search
		

Or, using a wildcard to include all remote clusters:

				GET my-index,*:my-index/_search
		

ES|QL

List each cluster explicitly in the FROM command.

FROM my-index,cluster_one:my-index
| LIMIT 10
		

Use the index name with no prefix. All linked projects are searched by default.

_search

				GET my-index/_search
		

ES|QL

FROM my-index
| LIMIT 10
		

Search broadly but skip one or more clusters or projects. Use this when you need results from most clusters or projects but want to leave some out, for example to exclude development or staging projects. In CPS, because all projects are searched by default, you only need to specify what to skip.

Prefix the cluster name with - and use * in the index position. You can chain multiple exclusions.

_search

				GET my-index,*:my-index,-cluster_one:*,-cluster_two:*/_search
		

ES|QL

FROM my-index,*:my-index,-cluster_one:*,-cluster_two:*
| LIMIT 10
		

Use the same - prefix with the project alias. An exclusion pattern requires a preceding inclusion pattern. You can exclude multiple projects.

_search

				GET my-index,-linked_project:*,-staging_project:*/_search
		

ES|QL

FROM my-index,-linked_project:*,-staging_project:*
| LIMIT 10
		

Determine which cluster or project returned a specific document. Use this when you want to trace a result back to its source when searching across multiple clusters or projects.

In both CCS and CPS, the _index field in the response indicates where each document originated.

Documents from a remote cluster include the cluster name as a prefix: cluster_one:my-index. Documents from the local cluster have no prefix: my-index.

_search

				GET my-index,cluster_one:my-index/_search
		

Example response:

{
  "hits": {
    "hits": [
      { "_index": "my-index", "_id": "1", "_source": { "message": "local doc" } },
      { "_index": "cluster_one:my-index", "_id": "2", "_source": { "message": "remote doc" } }
    ]
  },
  ...
}
		

ES|QL

Use METADATA _index to include the field:

FROM my-index,cluster_one:my-index METADATA _index
| KEEP _index, message
| LIMIT 10
		

Example results:

{
  "columns": [
    { "name": "_index", "type": "keyword" },
    { "name": "message", "type": "keyword" }
  ],
  "values": [
    [ "my-index", "local doc" ],
    [ "cluster_one:my-index", "remote doc" ]
  ]
}
		

Documents from a linked project include the project alias as a prefix: linked_project:my-index. Documents from the origin project have no prefix: my-index.

_search

				GET my-index/_search
		

Example response:

{
  "hits": {
    "hits": [
      { "_index": "my-index", "_id": "1", "_source": { "message": "origin doc" } },
      { "_index": "linked_project:my-index", "_id": "2", "_source": { "message": "linked doc" } }
    ]
  },
  ...
}
		

ES|QL

Use METADATA _index to include the field:

FROM my-index METADATA _index
| KEEP _index, message
| LIMIT 10
		

Example results:

{
  "columns": [
    { "name": "_index", "type": "keyword" },
    { "name": "message", "type": "keyword" }
  ],
  "values": [
    [ "my-index", "origin doc" ],
    [ "linked_project:my-index", "linked doc" ]
  ]
}
		

You can also use project tags like _project._alias in METADATA (ES|QL) or fields (_search) to identify the source project directly, without parsing the _index prefix.

Route a query to a subset of projects based on project metadata like cloud provider, region, or custom tags, rather than using the index expression alone. This capability is new in CPS and has no CCS equivalent.

In CCS, you select clusters by naming them in the index expression. You can't route queries based on cluster metadata.

_search

				GET cluster_one:logs-*,cluster_two:logs-*/_search
		

ES|QL

FROM cluster_one:logs-*,cluster_two:logs-*
| STATS COUNT(*) BY service.name
		

In CPS, use project_routing to select projects dynamically. Project routing supports boolean logic (AND, OR, NOT) and wildcards.

_search

				GET logs-*/_search
					{
  "project_routing": "_csp:aws AND _region:us*"
}
		

ES|QL

SET project_routing="_csp:aws AND _region:us*";
FROM logs-*
| STATS COUNT(*) BY service.name
		

You can also define reusable named expressions and reference them with the @ prefix:

_search

				GET logs-*/_search
					{
  "project_routing": "@us-aws"
}
		

ES|QL

SET project_routing="@us-aws";
FROM logs-*
| STATS COUNT(*) BY service.name
		

If you're evaluating Elastic Cloud Serverless, refer to the following resources: