This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 0d9dd822d3a Add exact-match uri filter to GET /assets endpoint (#69489)
0d9dd822d3a is described below
commit 0d9dd822d3ac0cb44380836c455baecdf25e2eff
Author: Sean Muth <[email protected]>
AuthorDate: Thu Jul 9 09:31:20 2026 -0500
Add exact-match uri filter to GET /assets endpoint (#69489)
Resolving a single asset by its full URI currently requires uri_pattern,
which compiles to ILIKE '%...%' and cannot use a database index, so it
degrades to a full table scan on large asset tables. This restores the
fast, index-backed exact-URI lookup that the Airflow 2 REST API exposed
via GET /datasets/{uri} but that was not carried forward when the endpoint
moved to FastAPI under AIP-84.
The uri query parameter matches assets by exact URI using an equality
comparison and accepts repeated values (?uri=a&uri=b) to resolve several
assets in one call, backed by a new single-column index on asset.uri (the
existing unique index leads with name and cannot serve uri-only lookups).
Passing it as a query parameter rather than a path segment avoids the
URI-encoding problems that path parameters have with the '/' and ':'
characters in asset URIs.
---
airflow-core/docs/migrations-ref.rst | 4 +-
.../src/airflow/api_fastapi/common/parameters.py | 17 ++++++++
.../core_api/openapi/v2-rest-api-generated.yaml | 14 +++++++
.../api_fastapi/core_api/routes/public/assets.py | 12 +++++-
.../versions/0126_3_4_0_add_index_on_asset_uri.py | 47 ++++++++++++++++++++++
airflow-core/src/airflow/models/asset.py | 1 +
.../src/airflow/ui/openapi-gen/queries/common.ts | 5 ++-
.../ui/openapi-gen/queries/ensureQueryData.ts | 6 ++-
.../src/airflow/ui/openapi-gen/queries/prefetch.ts | 6 ++-
.../src/airflow/ui/openapi-gen/queries/queries.ts | 6 ++-
.../src/airflow/ui/openapi-gen/queries/suspense.ts | 6 ++-
.../ui/openapi-gen/requests/services.gen.ts | 2 +
.../airflow/ui/openapi-gen/requests/types.gen.ts | 4 ++
airflow-core/src/airflow/utils/db.py | 2 +-
.../core_api/routes/public/test_assets.py | 8 ++++
15 files changed, 127 insertions(+), 13 deletions(-)
diff --git a/airflow-core/docs/migrations-ref.rst
b/airflow-core/docs/migrations-ref.rst
index 6419525c8ab..3dd6e96a7d0 100644
--- a/airflow-core/docs/migrations-ref.rst
+++ b/airflow-core/docs/migrations-ref.rst
@@ -39,7 +39,9 @@ Here's the list of all the Database Migrations that are
executed via when you ru
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
| Revision ID | Revises ID | Airflow Version | Description
|
+=========================+==================+===================+==============================================================+
-| ``436dc127462c`` (head) | ``5a5d3253e946`` | ``3.4.0`` | Drop
span_status column. |
+| ``c4e7a1f9b2d0`` (head) | ``436dc127462c`` | ``3.4.0`` | Add index
on asset.uri. |
++-------------------------+------------------+-------------------+--------------------------------------------------------------+
+| ``436dc127462c`` | ``5a5d3253e946`` | ``3.4.0`` | Drop
span_status column. |
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
| ``5a5d3253e946`` | ``d2f4e1b3c5a7`` | ``3.4.0`` | Add GIN
index on asset_event.extra for PostgreSQL. |
+-------------------------+------------------+-------------------+--------------------------------------------------------------+
diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py
b/airflow-core/src/airflow/api_fastapi/common/parameters.py
index c3a543a2b90..060d9478a39 100644
--- a/airflow-core/src/airflow/api_fastapi/common/parameters.py
+++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py
@@ -1664,6 +1664,23 @@ QueryUriPatternSearch = Annotated[_SearchParam,
Depends(search_param_factory(Ass
QueryUriPrefixPatternSearch = Annotated[
_PrefixSearchParam, Depends(prefix_search_param_factory(AssetModel.uri,
"uri_prefix_pattern"))
]
+QueryUriExactMatch = Annotated[
+ FilterParam[list[str]],
+ Depends(
+ filter_param_factory(
+ AssetModel.uri,
+ list[str],
+ FilterOptionEnum.ANY_EQUAL,
+ filter_name="uri",
+ default_factory=list,
+ description=(
+ "Exact-match filter on the full asset URI. Compiles to an
indexed equality "
+ "comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``) to match "
+ "multiple assets."
+ ),
+ )
+ ),
+]
QueryAssetAliasNamePatternSearch = Annotated[
_SearchParam, Depends(search_param_factory(AssetAliasModel.name,
"name_pattern"))
]
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index b848b996298..8173fd5fe9c 100644
---
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -79,6 +79,20 @@ paths:
\ stays index-compatible under locale-aware collations \u2014 e.g.
`test_`\
\ effectively matches items starting with `test`, and `s3://`
matches items\
\ starting with `s3`."
+ - name: uri
+ in: query
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
+ description: Exact-match filter on the full asset URI. Compiles to
an indexed
+ equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``)
+ to match multiple assets.
+ title: Uri
+ description: Exact-match filter on the full asset URI. Compiles to an
indexed
+ equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``)
+ to match multiple assets.
- name: uri_pattern
in: query
required: false
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
index d21b1c1cbb9..d83ed8ec8d8 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
@@ -42,6 +42,7 @@ from airflow.api_fastapi.common.parameters import (
QueryAssetNamePrefixPatternSearch,
QueryLimit,
QueryOffset,
+ QueryUriExactMatch,
QueryUriPatternSearch,
QueryUriPrefixPatternSearch,
RangeFilter,
@@ -140,6 +141,7 @@ def get_assets(
offset: QueryOffset,
name_pattern: QueryAssetNamePatternSearch,
name_prefix_pattern: QueryAssetNamePrefixPatternSearch,
+ uri: QueryUriExactMatch,
uri_pattern: QueryUriPatternSearch,
uri_prefix_pattern: QueryUriPrefixPatternSearch,
dag_ids: QueryAssetDagIdPatternSearch,
@@ -185,7 +187,15 @@ def get_assets(
assets_select, total_entries = paginated_select(
statement=assets_select_statement,
- filters=[only_active, name_pattern, name_prefix_pattern, uri_pattern,
uri_prefix_pattern, dag_ids],
+ filters=[
+ only_active,
+ name_pattern,
+ name_prefix_pattern,
+ uri,
+ uri_pattern,
+ uri_prefix_pattern,
+ dag_ids,
+ ],
order_by=order_by,
offset=offset,
limit=limit,
diff --git
a/airflow-core/src/airflow/migrations/versions/0126_3_4_0_add_index_on_asset_uri.py
b/airflow-core/src/airflow/migrations/versions/0126_3_4_0_add_index_on_asset_uri.py
new file mode 100644
index 00000000000..068bfda9add
--- /dev/null
+++
b/airflow-core/src/airflow/migrations/versions/0126_3_4_0_add_index_on_asset_uri.py
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""
+Add index on asset.uri.
+
+Revision ID: c4e7a1f9b2d0
+Revises: 436dc127462c
+Create Date: 2026-07-06 00:00:00.000000
+"""
+
+from __future__ import annotations
+
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "c4e7a1f9b2d0"
+down_revision = "436dc127462c"
+branch_labels = None
+depends_on = None
+airflow_version = "3.4.0"
+
+
+def upgrade():
+ """Apply Add index on asset.uri."""
+ with op.batch_alter_table("asset", schema=None) as batch_op:
+ batch_op.create_index("idx_asset_uri", ["uri"], unique=False)
+
+
+def downgrade():
+ """Unapply Add index on asset.uri."""
+ with op.batch_alter_table("asset", schema=None) as batch_op:
+ batch_op.drop_index("idx_asset_uri")
diff --git a/airflow-core/src/airflow/models/asset.py
b/airflow-core/src/airflow/models/asset.py
index 60256f9cd88..4354034e649 100644
--- a/airflow-core/src/airflow/models/asset.py
+++ b/airflow-core/src/airflow/models/asset.py
@@ -333,6 +333,7 @@ class AssetModel(Base):
__tablename__ = "asset"
__table_args__ = (
Index("idx_asset_name_uri_unique", name, uri, unique=True),
+ Index("idx_asset_uri", uri),
{"sqlite_autoincrement": True}, # ensures PK values not reused
)
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
index 2d6dc7ffe7d..a8f08d3df78 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -6,7 +6,7 @@ import { DagRunState, DagWarningType } from
"../requests/types.gen";
export type AssetServiceGetAssetsDefaultResponse = Awaited<ReturnType<typeof
AssetService.getAssets>>;
export type AssetServiceGetAssetsQueryResult<TData =
AssetServiceGetAssetsDefaultResponse, TError = unknown> = UseQueryResult<TData,
TError>;
export const useAssetServiceGetAssetsKey = "AssetServiceGetAssets";
-export const UseAssetServiceGetAssetsKeyFn = ({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern }:
{
+export const UseAssetServiceGetAssetsKeyFn = ({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }: {
dagIds?: string[];
limit?: number;
namePattern?: string;
@@ -14,9 +14,10 @@ export const UseAssetServiceGetAssetsKeyFn = ({ dagIds,
limit, namePattern, name
offset?: number;
onlyActive?: boolean;
orderBy?: string[];
+ uri?: string[];
uriPattern?: string;
uriPrefixPattern?: string;
-} = {}, queryKey?: Array<unknown>) => [useAssetServiceGetAssetsKey,
...(queryKey ?? [{ dagIds, limit, namePattern, namePrefixPattern, offset,
onlyActive, orderBy, uriPattern, uriPrefixPattern }])];
+} = {}, queryKey?: Array<unknown>) => [useAssetServiceGetAssetsKey,
...(queryKey ?? [{ dagIds, limit, namePattern, namePrefixPattern, offset,
onlyActive, orderBy, uri, uriPattern, uriPrefixPattern }])];
export type AssetServiceGetAssetAliasesDefaultResponse =
Awaited<ReturnType<typeof AssetService.getAssetAliases>>;
export type AssetServiceGetAssetAliasesQueryResult<TData =
AssetServiceGetAssetAliasesDefaultResponse, TError = unknown> =
UseQueryResult<TData, TError>;
export const useAssetServiceGetAssetAliasesKey = "AssetServiceGetAssetAliases";
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
index 6eba7e9a9db..34eff203140 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -14,6 +14,7 @@ import * as Common from "./common";
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``name_prefix_pattern`` parameter when possible.
* @param data.namePrefixPattern Prefix match — returns items whose value
starts with the given string (case-sensitive, index-friendly). Use the pipe `|`
operator for OR logic (e.g. `dag1|dag2`). Use `~` to match all. Wildcard
characters (`%`, `_`) are treated as literal characters. Trailing
non-alphanumeric characters in the prefix are stripped before matching so the
range scan stays index-compatible under locale-aware collations — e.g. `test_`
effectively matches items starting with `te [...]
+* @param data.uri Exact-match filter on the full asset URI. Compiles to an
indexed equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``) to match multiple assets.
* @param data.uriPattern SQL LIKE expression — use `%` / `_` wildcards (e.g.
`%customer_%`). Use the pipe `|` operator for OR logic (e.g. `dag1 | dag2`).
Regular expressions are **not** supported.
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``uri_prefix_pattern`` parameter when possible.
@@ -24,7 +25,7 @@ import * as Common from "./common";
* @returns AssetCollectionResponse Successful Response
* @throws ApiError
*/
-export const ensureUseAssetServiceGetAssetsData = (queryClient: QueryClient, {
dagIds, limit, namePattern, namePrefixPattern, offset, onlyActive, orderBy,
uriPattern, uriPrefixPattern }: {
+export const ensureUseAssetServiceGetAssetsData = (queryClient: QueryClient, {
dagIds, limit, namePattern, namePrefixPattern, offset, onlyActive, orderBy,
uri, uriPattern, uriPrefixPattern }: {
dagIds?: string[];
limit?: number;
namePattern?: string;
@@ -32,9 +33,10 @@ export const ensureUseAssetServiceGetAssetsData =
(queryClient: QueryClient, { d
offset?: number;
onlyActive?: boolean;
orderBy?: string[];
+ uri?: string[];
uriPattern?: string;
uriPrefixPattern?: string;
-} = {}) => queryClient.ensureQueryData({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern
}), queryFn: () => AssetService.getAssets({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern })
});
+} = {}) => queryClient.ensureQueryData({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }), queryFn: () => AssetService.getAssets({ dagIds, limit,
namePattern, namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }) });
/**
* Get Asset Aliases
* Get asset aliases.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
index 037f6d01a39..170a17f9e58 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -14,6 +14,7 @@ import * as Common from "./common";
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``name_prefix_pattern`` parameter when possible.
* @param data.namePrefixPattern Prefix match — returns items whose value
starts with the given string (case-sensitive, index-friendly). Use the pipe `|`
operator for OR logic (e.g. `dag1|dag2`). Use `~` to match all. Wildcard
characters (`%`, `_`) are treated as literal characters. Trailing
non-alphanumeric characters in the prefix are stripped before matching so the
range scan stays index-compatible under locale-aware collations — e.g. `test_`
effectively matches items starting with `te [...]
+* @param data.uri Exact-match filter on the full asset URI. Compiles to an
indexed equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``) to match multiple assets.
* @param data.uriPattern SQL LIKE expression — use `%` / `_` wildcards (e.g.
`%customer_%`). Use the pipe `|` operator for OR logic (e.g. `dag1 | dag2`).
Regular expressions are **not** supported.
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``uri_prefix_pattern`` parameter when possible.
@@ -24,7 +25,7 @@ import * as Common from "./common";
* @returns AssetCollectionResponse Successful Response
* @throws ApiError
*/
-export const prefetchUseAssetServiceGetAssets = (queryClient: QueryClient, {
dagIds, limit, namePattern, namePrefixPattern, offset, onlyActive, orderBy,
uriPattern, uriPrefixPattern }: {
+export const prefetchUseAssetServiceGetAssets = (queryClient: QueryClient, {
dagIds, limit, namePattern, namePrefixPattern, offset, onlyActive, orderBy,
uri, uriPattern, uriPrefixPattern }: {
dagIds?: string[];
limit?: number;
namePattern?: string;
@@ -32,9 +33,10 @@ export const prefetchUseAssetServiceGetAssets =
(queryClient: QueryClient, { dag
offset?: number;
onlyActive?: boolean;
orderBy?: string[];
+ uri?: string[];
uriPattern?: string;
uriPrefixPattern?: string;
-} = {}) => queryClient.prefetchQuery({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern
}), queryFn: () => AssetService.getAssets({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern })
});
+} = {}) => queryClient.prefetchQuery({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }), queryFn: () => AssetService.getAssets({ dagIds, limit,
namePattern, namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }) });
/**
* Get Asset Aliases
* Get asset aliases.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
index 09ddcdd3837..7efadc2e4f8 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -14,6 +14,7 @@ import * as Common from "./common";
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``name_prefix_pattern`` parameter when possible.
* @param data.namePrefixPattern Prefix match — returns items whose value
starts with the given string (case-sensitive, index-friendly). Use the pipe `|`
operator for OR logic (e.g. `dag1|dag2`). Use `~` to match all. Wildcard
characters (`%`, `_`) are treated as literal characters. Trailing
non-alphanumeric characters in the prefix are stripped before matching so the
range scan stays index-compatible under locale-aware collations — e.g. `test_`
effectively matches items starting with `te [...]
+* @param data.uri Exact-match filter on the full asset URI. Compiles to an
indexed equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``) to match multiple assets.
* @param data.uriPattern SQL LIKE expression — use `%` / `_` wildcards (e.g.
`%customer_%`). Use the pipe `|` operator for OR logic (e.g. `dag1 | dag2`).
Regular expressions are **not** supported.
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``uri_prefix_pattern`` parameter when possible.
@@ -24,7 +25,7 @@ import * as Common from "./common";
* @returns AssetCollectionResponse Successful Response
* @throws ApiError
*/
-export const useAssetServiceGetAssets = <TData =
Common.AssetServiceGetAssetsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern }:
{
+export const useAssetServiceGetAssets = <TData =
Common.AssetServiceGetAssetsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }: {
dagIds?: string[];
limit?: number;
namePattern?: string;
@@ -32,9 +33,10 @@ export const useAssetServiceGetAssets = <TData =
Common.AssetServiceGetAssetsDef
offset?: number;
onlyActive?: boolean;
orderBy?: string[];
+ uri?: string[];
uriPattern?: string;
uriPrefixPattern?: string;
-} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern },
queryKey), queryFn: () => AssetService.getAssets({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern })
as TData, ...options });
+} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }, queryKey), queryFn: () => AssetService.getAssets({ dagIds,
limit, namePattern, namePrefixPattern, offset, onlyActive, orderBy, uri,
uriPattern, uriPrefixPattern }) as TData, ...options });
/**
* Get Asset Aliases
* Get asset aliases.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
index e3ec8836309..d4e694554d0 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -14,6 +14,7 @@ import * as Common from "./common";
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``name_prefix_pattern`` parameter when possible.
* @param data.namePrefixPattern Prefix match — returns items whose value
starts with the given string (case-sensitive, index-friendly). Use the pipe `|`
operator for OR logic (e.g. `dag1|dag2`). Use `~` to match all. Wildcard
characters (`%`, `_`) are treated as literal characters. Trailing
non-alphanumeric characters in the prefix are stripped before matching so the
range scan stays index-compatible under locale-aware collations — e.g. `test_`
effectively matches items starting with `te [...]
+* @param data.uri Exact-match filter on the full asset URI. Compiles to an
indexed equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``) to match multiple assets.
* @param data.uriPattern SQL LIKE expression — use `%` / `_` wildcards (e.g.
`%customer_%`). Use the pipe `|` operator for OR logic (e.g. `dag1 | dag2`).
Regular expressions are **not** supported.
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``uri_prefix_pattern`` parameter when possible.
@@ -24,7 +25,7 @@ import * as Common from "./common";
* @returns AssetCollectionResponse Successful Response
* @throws ApiError
*/
-export const useAssetServiceGetAssetsSuspense = <TData =
Common.AssetServiceGetAssetsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern }:
{
+export const useAssetServiceGetAssetsSuspense = <TData =
Common.AssetServiceGetAssetsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }: {
dagIds?: string[];
limit?: number;
namePattern?: string;
@@ -32,9 +33,10 @@ export const useAssetServiceGetAssetsSuspense = <TData =
Common.AssetServiceGetA
offset?: number;
onlyActive?: boolean;
orderBy?: string[];
+ uri?: string[];
uriPattern?: string;
uriPrefixPattern?: string;
-} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern },
queryKey), queryFn: () => AssetService.getAssets({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uriPattern, uriPrefixPattern })
as TData, ...options });
+} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseAssetServiceGetAssetsKeyFn({ dagIds, limit, namePattern,
namePrefixPattern, offset, onlyActive, orderBy, uri, uriPattern,
uriPrefixPattern }, queryKey), queryFn: () => AssetService.getAssets({ dagIds,
limit, namePattern, namePrefixPattern, offset, onlyActive, orderBy, uri,
uriPattern, uriPrefixPattern }) as TData, ...options });
/**
* Get Asset Aliases
* Get asset aliases.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
index b674a72ccbd..0356a4a3641 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -16,6 +16,7 @@ export class AssetService {
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``name_prefix_pattern`` parameter when possible.
* @param data.namePrefixPattern Prefix match — returns items whose value
starts with the given string (case-sensitive, index-friendly). Use the pipe `|`
operator for OR logic (e.g. `dag1|dag2`). Use `~` to match all. Wildcard
characters (`%`, `_`) are treated as literal characters. Trailing
non-alphanumeric characters in the prefix are stripped before matching so the
range scan stays index-compatible under locale-aware collations — e.g. `test_`
effectively matches items starting wit [...]
+ * @param data.uri Exact-match filter on the full asset URI. Compiles to
an indexed equality comparison (``uri = ...``). Repeat the parameter
(``?uri=a&uri=b``) to match multiple assets.
* @param data.uriPattern SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Use the pipe `|` operator for OR logic (e.g. `dag1 |
dag2`). Regular expressions are **not** supported.
*
* **Performance note:** this full-match pattern is evaluated as ``ILIKE
'%term%'`` and most of the time prevents the database from using B-tree
indexes, which can be very slow on large tables. Prefer the equivalent
``uri_prefix_pattern`` parameter when possible.
@@ -35,6 +36,7 @@ export class AssetService {
offset: data.offset,
name_pattern: data.namePattern,
name_prefix_pattern: data.namePrefixPattern,
+ uri: data.uri,
uri_pattern: data.uriPattern,
uri_prefix_pattern: data.uriPrefixPattern,
dag_ids: data.dagIds,
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index d1a91b68a69..acec153fd1f 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -2808,6 +2808,10 @@ export type GetAssetsData = {
* Attributes to order by, multi criteria sort is supported. Prefix with
`-` for descending order. Supported attributes: `id, name, uri, created_at,
updated_at`
*/
orderBy?: Array<(string)>;
+ /**
+ * Exact-match filter on the full asset URI. Compiles to an indexed
equality comparison (``uri = ...``). Repeat the parameter (``?uri=a&uri=b``) to
match multiple assets.
+ */
+ uri?: Array<(string)>;
/**
* SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Use
the pipe `|` operator for OR logic (e.g. `dag1 | dag2`). Regular expressions
are **not** supported.
*
diff --git a/airflow-core/src/airflow/utils/db.py
b/airflow-core/src/airflow/utils/db.py
index ee10c008f9b..ce4ef036d20 100644
--- a/airflow-core/src/airflow/utils/db.py
+++ b/airflow-core/src/airflow/utils/db.py
@@ -117,7 +117,7 @@ _REVISION_HEADS_MAP: dict[str, str] = {
"3.1.8": "509b94a1042d",
"3.2.0": "1d6611b6ab7c",
"3.3.0": "d2f4e1b3c5a7",
- "3.4.0": "436dc127462c",
+ "3.4.0": "c4e7a1f9b2d0",
}
# Prefix used to identify tables holding data moved during migration.
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
index 1b452924ffe..1ffa8552b84 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
@@ -577,6 +577,14 @@ class TestGetAssets(TestAssets):
"wasb://some_asset_bucket_/key",
},
),
+ # Exact-match ``uri`` filter: only the asset whose full URI
matches is returned.
+ ({"uri": "s3://folder/key"}, {"s3://folder/key"}),
+ ({"uri": "gcp://bucket/key"}, {"gcp://bucket/key"}),
+ # Repeated ``uri`` params match any of the given URIs.
+ ({"uri": ["s3://folder/key", "gcp://bucket/key"]},
{"s3://folder/key", "gcp://bucket/key"}),
+ # A substring of an existing URI must NOT match (unlike
uri_pattern).
+ ({"uri": "s3://folder"}, set()),
+ ({"uri": "does-not-exist://key"}, set()),
],
)
@provide_session