This is an automated email from the ASF dual-hosted git repository.

dheerajturaga 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 51972109907 UI: Add Run ID filter to the Grid view (#70150)
51972109907 is described below

commit 51972109907ca274bbfa52aff3e2c71c7b02bcab
Author: Dheeraj Turaga <[email protected]>
AuthorDate: Mon Jul 27 13:08:22 2026 -0500

    UI: Add Run ID filter to the Grid view (#70150)
    
    The Dag Runs list page lets users narrow runs by Run ID, but the Grid
    view's filter bar offered no equivalent, forcing users to eyeball run
    columns to locate a specific run. Expose the same Run ID search on the
    Grid so both surfaces filter consistently.
    
    The pill exposes the same substring/prefix advanced-search toggle used
    elsewhere; the Grid query hooks route the value through
    ``useAdvancedSearchArg`` so the toggle chooses between the substring
    ``run_id_pattern`` and the index-friendly ``run_id_prefix_pattern``
    variant on each request.
---
 .../api_fastapi/core_api/openapi/_private_ui.yaml  | 52 ++++++++++++++++++++++
 .../airflow/api_fastapi/core_api/routes/ui/grid.py | 34 +++++++++++++-
 .../src/airflow/ui/openapi-gen/queries/common.ts   | 12 +++--
 .../ui/openapi-gen/queries/ensureQueryData.ts      | 16 +++++--
 .../src/airflow/ui/openapi-gen/queries/prefetch.ts | 16 +++++--
 .../src/airflow/ui/openapi-gen/queries/queries.ts  | 16 +++++--
 .../src/airflow/ui/openapi-gen/queries/suspense.ts | 16 +++++--
 .../ui/openapi-gen/requests/services.gen.ts        | 12 ++++-
 .../airflow/ui/openapi-gen/requests/types.gen.ts   | 16 +++++++
 .../ui/src/layouts/Details/DetailsLayout.tsx       |  5 +++
 .../airflow/ui/src/layouts/Details/Gantt/Gantt.tsx |  4 ++
 .../airflow/ui/src/layouts/Details/Grid/Grid.tsx   |  4 ++
 .../airflow/ui/src/layouts/Details/GridFilters.tsx |  1 +
 .../src/airflow/ui/src/queries/useGridRuns.ts      | 14 ++++++
 .../src/airflow/ui/src/queries/useGridStructure.ts | 14 ++++++
 .../api_fastapi/core_api/routes/ui/test_grid.py    | 33 ++++++++++++++
 16 files changed, 241 insertions(+), 24 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
index 94489a199e9..f16b2bbc9fe 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
@@ -1282,6 +1282,32 @@ paths:
           title: Triggering User Prefix
         description: Case-sensitive, index-friendly prefix match. See 
"Filtering with
           pattern parameters".
+      - name: run_id_pattern
+        in: query
+        required: false
+        schema:
+          anyOf:
+          - type: string
+          - type: 'null'
+          description: "Case-insensitive substring match (SQL `ILIKE`). Slower 
than\
+            \ `run_id_prefix_pattern` on large tables \u2014 see \"Filtering 
with\
+            \ pattern parameters\"."
+          title: Run Id Pattern
+        description: "Case-insensitive substring match (SQL `ILIKE`). Slower 
than\
+          \ `run_id_prefix_pattern` on large tables \u2014 see \"Filtering 
with pattern\
+          \ parameters\"."
+      - name: run_id_prefix_pattern
+        in: query
+        required: false
+        schema:
+          anyOf:
+          - type: string
+          - type: 'null'
+          description: Case-sensitive, index-friendly prefix match. See 
"Filtering
+            with pattern parameters".
+          title: Run Id Prefix Pattern
+        description: Case-sensitive, index-friendly prefix match. See 
"Filtering with
+          pattern parameters".
       responses:
         '200':
           description: Successful Response
@@ -1437,6 +1463,32 @@ paths:
           title: Triggering User Prefix
         description: Case-sensitive, index-friendly prefix match. See 
"Filtering with
           pattern parameters".
+      - name: run_id_pattern
+        in: query
+        required: false
+        schema:
+          anyOf:
+          - type: string
+          - type: 'null'
+          description: "Case-insensitive substring match (SQL `ILIKE`). Slower 
than\
+            \ `run_id_prefix_pattern` on large tables \u2014 see \"Filtering 
with\
+            \ pattern parameters\"."
+          title: Run Id Pattern
+        description: "Case-insensitive substring match (SQL `ILIKE`). Slower 
than\
+          \ `run_id_prefix_pattern` on large tables \u2014 see \"Filtering 
with pattern\
+          \ parameters\"."
+      - name: run_id_prefix_pattern
+        in: query
+        required: false
+        schema:
+          anyOf:
+          - type: string
+          - type: 'null'
+          description: Case-sensitive, index-friendly prefix match. See 
"Filtering
+            with pattern parameters".
+          title: Run Id Prefix Pattern
+        description: Case-sensitive, index-friendly prefix match. See 
"Filtering with
+          pattern parameters".
       responses:
         '200':
           description: Successful Response
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
index 8a0c55d315e..9cd5b6e60d9 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
@@ -42,7 +42,11 @@ from airflow.api_fastapi.common.parameters import (
     QueryOffset,
     RangeFilter,
     SortParam,
+    _PrefixSearchParam,
+    _SearchParam,
     datetime_range_filter_factory,
+    prefix_search_param_factory,
+    search_param_factory,
 )
 from airflow.api_fastapi.common.router import AirflowRouter
 from airflow.api_fastapi.core_api.datamodels.ui.common import (
@@ -144,6 +148,11 @@ def get_dag_structure(
     state: QueryDagRunStateFilter,
     triggering_user: QueryDagRunTriggeringUserSearch,
     triggering_user_prefix: QueryDagRunTriggeringUserPrefixSearch,
+    run_id_pattern: Annotated[_SearchParam, 
Depends(search_param_factory(DagRun.run_id, "run_id_pattern"))],
+    run_id_prefix_pattern: Annotated[
+        _PrefixSearchParam,
+        Depends(prefix_search_param_factory(DagRun.run_id, 
"run_id_prefix_pattern")),
+    ],
     include_upstream: QueryIncludeUpstream = False,
     include_downstream: QueryIncludeDownstream = False,
     depth: int | None = None,
@@ -177,7 +186,15 @@ def get_dag_structure(
         statement=base_query,
         order_by=order_by,
         offset=offset,
-        filters=[run_after, run_type, state, triggering_user, 
triggering_user_prefix],
+        filters=[
+            run_after,
+            run_type,
+            state,
+            triggering_user,
+            triggering_user_prefix,
+            run_id_pattern,
+            run_id_prefix_pattern,
+        ],
         limit=limit,
     )
     run_ids = list(session.scalars(dag_runs_select_filter))
@@ -282,6 +299,11 @@ def get_grid_runs(
     state: QueryDagRunStateFilter,
     triggering_user: QueryDagRunTriggeringUserSearch,
     triggering_user_prefix: QueryDagRunTriggeringUserPrefixSearch,
+    run_id_pattern: Annotated[_SearchParam, 
Depends(search_param_factory(DagRun.run_id, "run_id_pattern"))],
+    run_id_prefix_pattern: Annotated[
+        _PrefixSearchParam,
+        Depends(prefix_search_param_factory(DagRun.run_id, 
"run_id_prefix_pattern")),
+    ],
 ) -> list[GridRunsResponse]:
     """Get info about a run for the grid."""
     # Retrieve, sort the previous Dag Runs
@@ -330,7 +352,15 @@ def get_grid_runs(
         statement=base_query,
         order_by=order_by,
         offset=offset,
-        filters=[run_after, run_type, state, triggering_user, 
triggering_user_prefix],
+        filters=[
+            run_after,
+            run_type,
+            state,
+            triggering_user,
+            triggering_user_prefix,
+            run_id_pattern,
+            run_id_prefix_pattern,
+        ],
         limit=limit,
         return_total_entries=False,
     )
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 4f485202973..05a4b47ec1b 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -991,7 +991,7 @@ export const UseStructureServiceStructureDataKeyFn = ({ 
dagId, depth, externalDe
 export type GridServiceGetDagStructureDefaultResponse = 
Awaited<ReturnType<typeof GridService.getDagStructure>>;
 export type GridServiceGetDagStructureQueryResult<TData = 
GridServiceGetDagStructureDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useGridServiceGetDagStructureKey = "GridServiceGetDagStructure";
-export const UseGridServiceGetDagStructureKeyFn = ({ dagId, depth, 
includeDownstream, includeUpstream, limit, offset, orderBy, root, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, 
triggeringUserPrefix }: {
+export const UseGridServiceGetDagStructureKeyFn = ({ dagId, depth, 
includeDownstream, includeUpstream, limit, offset, orderBy, root, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, 
runType, state, triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   depth?: number;
   includeDownstream?: boolean;
@@ -1004,15 +1004,17 @@ export const UseGridServiceGetDagStructureKeyFn = ({ 
dagId, depth, includeDownst
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetDagStructureKey, 
...(queryKey ?? [{ dagId, depth, includeDownstream, includeUpstream, limit, 
offset, orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, 
runType, state, triggeringUser, triggeringUserPrefix }])];
+}, queryKey?: Array<unknown>) => [useGridServiceGetDagStructureKey, 
...(queryKey ?? [{ dagId, depth, includeDownstream, includeUpstream, limit, 
offset, orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, 
runIdPattern, runIdPrefixPattern, runType, state, triggeringUser, 
triggeringUserPrefix }])];
 export type GridServiceGetGridRunsDefaultResponse = Awaited<ReturnType<typeof 
GridService.getGridRuns>>;
 export type GridServiceGetGridRunsQueryResult<TData = 
GridServiceGetGridRunsDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useGridServiceGetGridRunsKey = "GridServiceGetGridRuns";
-export const UseGridServiceGetGridRunsKeyFn = ({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }: {
+export const UseGridServiceGetGridRunsKeyFn = ({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1021,11 +1023,13 @@ export const UseGridServiceGetGridRunsKeyFn = ({ dagId, 
limit, offset, orderBy,
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetGridRunsKey, ...(queryKey 
?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runType, state, triggeringUser, triggeringUserPrefix }])];
+}, queryKey?: Array<unknown>) => [useGridServiceGetGridRunsKey, ...(queryKey 
?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, triggeringUser, 
triggeringUserPrefix }])];
 export type GridServiceGetGridTiSummariesStreamDefaultResponse = 
Awaited<ReturnType<typeof GridService.getGridTiSummariesStream>>;
 export type GridServiceGetGridTiSummariesStreamQueryResult<TData = 
GridServiceGetGridTiSummariesStreamDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useGridServiceGetGridTiSummariesStreamKey = 
"GridServiceGetGridTiSummariesStream";
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 8f3fcea0d76..f16710ae81d 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1921,10 +1921,12 @@ export const ensureUseStructureServiceStructureDataData 
= (queryClient: QueryCli
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const ensureUseGridServiceGetDagStructureData = (queryClient: 
QueryClient, { dagId, depth, includeDownstream, includeUpstream, limit, offset, 
orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
state, triggeringUser, triggeringUserPrefix }: {
+export const ensureUseGridServiceGetDagStructureData = (queryClient: 
QueryClient, { dagId, depth, includeDownstream, includeUpstream, limit, offset, 
orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   depth?: number;
   includeDownstream?: boolean;
@@ -1937,11 +1939,13 @@ export const ensureUseGridServiceGetDagStructureData = 
(queryClient: QueryClient
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, state, triggeringUser, triggeringUserPrefix 
}), queryFn: () => GridService.getDagStructure({ dagId, depth, 
includeDownstream, includeUpstream, limit, offset, orderBy, root, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, triggeri 
[...]
+}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, 
triggeringUser, triggeringUserPrefix }), queryFn: () => 
GridService.getDagStructure({ dagId, depth, includeDownstream, includeUpstream, 
limit, offset, orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, 
runIdP [...]
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1958,10 +1962,12 @@ export const ensureUseGridServiceGetDagStructureData = 
(queryClient: QueryClient
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const ensureUseGridServiceGetGridRunsData = (queryClient: QueryClient, 
{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runType, state, triggeringUser, triggeringUserPrefix }: {
+export const ensureUseGridServiceGetGridRunsData = (queryClient: QueryClient, 
{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, triggeringUser, 
triggeringUserPrefix }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1970,11 +1976,13 @@ export const ensureUseGridServiceGetGridRunsData = 
(queryClient: QueryClient, {
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, 
triggeringUserPrefix }) });
+}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }), 
queryFn: () => GridService.getGridRuns({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }) });
 /**
 * Get Grid Ti Summaries Stream
 * Stream TI summaries for multiple Dag runs as NDJSON (one JSON line per run).
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 9af03e88a16..e0199a66938 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1921,10 +1921,12 @@ export const prefetchUseStructureServiceStructureData = 
(queryClient: QueryClien
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const prefetchUseGridServiceGetDagStructure = (queryClient: 
QueryClient, { dagId, depth, includeDownstream, includeUpstream, limit, offset, 
orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
state, triggeringUser, triggeringUserPrefix }: {
+export const prefetchUseGridServiceGetDagStructure = (queryClient: 
QueryClient, { dagId, depth, includeDownstream, includeUpstream, limit, offset, 
orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   depth?: number;
   includeDownstream?: boolean;
@@ -1937,11 +1939,13 @@ export const prefetchUseGridServiceGetDagStructure = 
(queryClient: QueryClient,
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, state, triggeringUser, triggeringUserPrefix 
}), queryFn: () => GridService.getDagStructure({ dagId, depth, 
includeDownstream, includeUpstream, limit, offset, orderBy, root, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, 
triggering [...]
+}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, 
triggeringUser, triggeringUserPrefix }), queryFn: () => 
GridService.getDagStructure({ dagId, depth, includeDownstream, includeUpstream, 
limit, offset, orderBy, root, runAfterGt, runAfterGte, runAfterLt, runAfterLte, 
runIdPat [...]
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1958,10 +1962,12 @@ export const prefetchUseGridServiceGetDagStructure = 
(queryClient: QueryClient,
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const prefetchUseGridServiceGetGridRuns = (queryClient: QueryClient, { 
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runType, state, triggeringUser, triggeringUserPrefix }: {
+export const prefetchUseGridServiceGetGridRuns = (queryClient: QueryClient, { 
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, triggeringUser, 
triggeringUserPrefix }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1970,11 +1976,13 @@ export const prefetchUseGridServiceGetGridRuns = 
(queryClient: QueryClient, { da
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, 
triggeringUserPrefix }) });
+}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }), 
queryFn: () => GridService.getGridRuns({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }) });
 /**
 * Get Grid Ti Summaries Stream
 * Stream TI summaries for multiple Dag runs as NDJSON (one JSON line per run).
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 5d746b71921..cbd1e141960 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1921,10 +1921,12 @@ export const useStructureServiceStructureData = <TData 
= Common.StructureService
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, state, triggeringUser, triggeringUserPrefix 
}: {
+export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, 
triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   depth?: number;
   includeDownstream?: boolean;
@@ -1937,11 +1939,13 @@ export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagSt
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, state, triggeringUser, triggeringUserPrefix 
}, queryKey), queryFn: () => GridService.getDagStructure({ dagId, depth, 
includeDownstream, includeUpstream, limit, offset, order [...]
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, 
triggeringUser, triggeringUserPrefix }, queryKey), queryFn: () => 
GridService.getDagStructure({ dagId, depth, includeDownstream, inc [...]
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1958,10 +1962,12 @@ export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagSt
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetGridRuns = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }: {
+export const useGridServiceGetGridRuns = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1970,11 +1976,13 @@ export const useGridServiceGetGridRuns = <TData = 
Common.GridServiceGetGridRunsD
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }, queryKey), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, 
triggeringUser [...]
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }, 
queryKey), queryFn: () => GridService.getGridRuns({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern [...]
 /**
 * Get Grid Ti Summaries Stream
 * Stream TI summaries for multiple Dag runs as NDJSON (one JSON line per run).
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 08bbc85a0d6..743209b7bac 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1921,10 +1921,12 @@ export const useStructureServiceStructureDataSuspense = 
<TData = Common.Structur
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetDagStructureSuspense = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, state, triggeringUser, triggeringUserPrefix 
}: {
+export const useGridServiceGetDagStructureSuspense = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, 
triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   depth?: number;
   includeDownstream?: boolean;
@@ -1937,11 +1939,13 @@ export const useGridServiceGetDagStructureSuspense = 
<TData = Common.GridService
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, state, triggeringUser, triggeringUserPrefix 
}, queryKey), queryFn: () => GridService.getDagStructure({ dagId, depth, 
includeDownstream, includeUpstream, limit, offse [...]
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, depth, includeDownstream, 
includeUpstream, limit, offset, orderBy, root, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runIdPattern, runIdPrefixPattern, runType, state, 
triggeringUser, triggeringUserPrefix }, queryKey), queryFn: () => 
GridService.getDagStructure({ dagId, depth, includeDownstr [...]
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1958,10 +1962,12 @@ export const useGridServiceGetDagStructureSuspense = 
<TData = Common.GridService
 * @param data.state
 * @param data.triggeringUser Case-insensitive substring match (SQL `ILIKE`). 
Slower than `triggering_user` on large tables — see "Filtering with pattern 
parameters".
 * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+* @param data.runIdPattern Case-insensitive substring match (SQL `ILIKE`). 
Slower than `run_id_prefix_pattern` on large tables — see "Filtering with 
pattern parameters".
+* @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix match. 
See "Filtering with pattern parameters".
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetGridRunsSuspense = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }: {
+export const useGridServiceGetGridRunsSuspense = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1970,11 +1976,13 @@ export const useGridServiceGetGridRunsSuspense = <TData 
= Common.GridServiceGetG
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
+  runIdPattern?: string;
+  runIdPrefixPattern?: string;
   runType?: string[];
   state?: string[];
   triggeringUser?: string;
   triggeringUserPrefix?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state, 
triggeringUser, triggeringUserPrefix }, queryKey), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte, runType, state, triggeringUser, trigge 
[...]
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, 
runIdPrefixPattern, runType, state, triggeringUser, triggeringUserPrefix }, 
queryKey), queryFn: () => GridService.getGridRuns({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runI [...]
 /**
 * Get Grid Ti Summaries Stream
 * Stream TI summaries for multiple Dag runs as NDJSON (one JSON line per run).
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 c1ed9997d45..14a7cb408ce 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
@@ -4919,6 +4919,8 @@ export class GridService {
      * @param data.state
      * @param data.triggeringUser Case-insensitive substring match (SQL 
`ILIKE`). Slower than `triggering_user` on large tables — see "Filtering with 
pattern parameters".
      * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+     * @param data.runIdPattern Case-insensitive substring match (SQL 
`ILIKE`). Slower than `run_id_prefix_pattern` on large tables — see "Filtering 
with pattern parameters".
+     * @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
      * @returns GridNodeResponse Successful Response
      * @throws ApiError
      */
@@ -4944,7 +4946,9 @@ export class GridService {
                 run_type: data.runType,
                 state: data.state,
                 triggering_user: data.triggeringUser,
-                triggering_user_prefix: data.triggeringUserPrefix
+                triggering_user_prefix: data.triggeringUserPrefix,
+                run_id_pattern: data.runIdPattern,
+                run_id_prefix_pattern: data.runIdPrefixPattern
             },
             errors: {
                 400: 'Bad Request',
@@ -4970,6 +4974,8 @@ export class GridService {
      * @param data.state
      * @param data.triggeringUser Case-insensitive substring match (SQL 
`ILIKE`). Slower than `triggering_user` on large tables — see "Filtering with 
pattern parameters".
      * @param data.triggeringUserPrefix Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
+     * @param data.runIdPattern Case-insensitive substring match (SQL 
`ILIKE`). Slower than `run_id_prefix_pattern` on large tables — see "Filtering 
with pattern parameters".
+     * @param data.runIdPrefixPattern Case-sensitive, index-friendly prefix 
match. See "Filtering with pattern parameters".
      * @returns GridRunsResponse Successful Response
      * @throws ApiError
      */
@@ -4991,7 +4997,9 @@ export class GridService {
                 run_type: data.runType,
                 state: data.state,
                 triggering_user: data.triggeringUser,
-                triggering_user_prefix: data.triggeringUserPrefix
+                triggering_user_prefix: data.triggeringUserPrefix,
+                run_id_pattern: data.runIdPattern,
+                run_id_prefix_pattern: data.runIdPrefixPattern
             },
             errors: {
                 400: 'Bad Request',
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 3af0c45ee13..7d8551c8cd7 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
@@ -4630,6 +4630,14 @@ export type GetDagStructureData = {
     runAfterGte?: string | null;
     runAfterLt?: string | null;
     runAfterLte?: string | null;
+    /**
+     * Case-insensitive substring match (SQL `ILIKE`). Slower than 
`run_id_prefix_pattern` on large tables — see "Filtering with pattern 
parameters".
+     */
+    runIdPattern?: string | null;
+    /**
+     * Case-sensitive, index-friendly prefix match. See "Filtering with 
pattern parameters".
+     */
+    runIdPrefixPattern?: string | null;
     runType?: Array<(string)>;
     state?: Array<(string)>;
     /**
@@ -4656,6 +4664,14 @@ export type GetGridRunsData = {
     runAfterGte?: string | null;
     runAfterLt?: string | null;
     runAfterLte?: string | null;
+    /**
+     * Case-insensitive substring match (SQL `ILIKE`). Slower than 
`run_id_prefix_pattern` on large tables — see "Filtering with pattern 
parameters".
+     */
+    runIdPattern?: string | null;
+    /**
+     * Case-sensitive, index-friendly prefix match. See "Filtering with 
pattern parameters".
+     */
+    runIdPrefixPattern?: string | null;
     runType?: Array<(string)>;
     state?: Array<(string)>;
     /**
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
index 95ac32ffc4d..979bfe22485 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
@@ -134,6 +134,7 @@ export const DetailsLayout = ({ children, error, isLoading, 
outletContext, tabs
   const runAfterLte = searchParams.get(SearchParamsKeys.RUN_AFTER_LTE) ?? 
undefined;
   const runTypeFilter = (searchParams.get(SearchParamsKeys.RUN_TYPE) as 
DagRunType | null) ?? undefined;
   const triggeringUserFilter = 
searchParams.get(SearchParamsKeys.TRIGGERING_USER_NAME_PATTERN) ?? undefined;
+  const runIdPatternFilter = searchParams.get(SearchParamsKeys.RUN_ID_PATTERN) 
?? undefined;
   const dagRunStateFilter = (searchParams.get(SearchParamsKeys.STATE) as 
DagRunState | null) ?? undefined;
 
   // --- Setters that write back to URL ---
@@ -164,6 +165,7 @@ export const DetailsLayout = ({ children, error, isLoading, 
outletContext, tabs
   const { data: initialGridRuns } = useGridRuns({
     dagRunState: dagRunStateFilter,
     limit,
+    runIdPattern: runIdPatternFilter,
     runType: runTypeFilter,
     triggeringUser: triggeringUserFilter,
   });
@@ -289,6 +291,7 @@ export const DetailsLayout = ({ children, error, isLoading, 
outletContext, tabs
                           onJumpToLatest={handleJumpToLatest}
                           runAfterGte={runAfterGte}
                           runAfterLte={runAfterLte}
+                          runIdPattern={runIdPatternFilter}
                           runType={runTypeFilter}
                           setOffset={setOffset}
                           sharedScrollContainerRef={sharedGridGanttScrollRef}
@@ -302,6 +305,7 @@ export const DetailsLayout = ({ children, error, isLoading, 
outletContext, tabs
                           offset={offset}
                           runAfterGte={runAfterGte}
                           runAfterLte={runAfterLte}
+                          runIdPattern={runIdPatternFilter}
                           runType={runTypeFilter}
                           sharedScrollContainerRef={sharedGridGanttScrollRef}
                           triggeringUser={triggeringUserFilter}
@@ -325,6 +329,7 @@ export const DetailsLayout = ({ children, error, isLoading, 
outletContext, tabs
                         onJumpToLatest={handleJumpToLatest}
                         runAfterGte={runAfterGte}
                         runAfterLte={runAfterLte}
+                        runIdPattern={runIdPatternFilter}
                         runType={runTypeFilter}
                         setOffset={setOffset}
                         showVersionIndicatorMode={showVersionIndicatorMode}
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
index 6383d57623a..ad7b3e68cb2 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
@@ -48,6 +48,7 @@ type Props = {
   readonly offset?: number;
   readonly runAfterGte?: string | undefined;
   readonly runAfterLte?: string | undefined;
+  readonly runIdPattern?: string | undefined;
   readonly runType?: DagRunType | undefined;
   readonly sharedScrollContainerRef?: RefObject<HTMLDivElement | null>;
   readonly triggeringUser?: string | undefined;
@@ -59,6 +60,7 @@ export const Gantt = ({
   offset,
   runAfterGte,
   runAfterLte,
+  runIdPattern,
   runType,
   sharedScrollContainerRef,
   triggeringUser,
@@ -85,6 +87,7 @@ export const Gantt = ({
     offset,
     runAfterGte,
     runAfterLte,
+    runIdPattern,
     runType,
     triggeringUser,
   });
@@ -95,6 +98,7 @@ export const Gantt = ({
     includeUpstream,
     limit,
     root: filterRoot,
+    runIdPattern,
     runType,
     triggeringUser,
   });
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
index a1cb9d36e99..ff122b47b2d 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
@@ -53,6 +53,7 @@ type Props = {
   readonly onJumpToLatest: () => void;
   readonly runAfterGte?: string;
   readonly runAfterLte?: string;
+  readonly runIdPattern?: string | undefined;
   readonly runType?: DagRunType | undefined;
   readonly setOffset: (value: number) => void;
   readonly sharedScrollContainerRef?: RefObject<HTMLDivElement | null>;
@@ -70,6 +71,7 @@ export const Grid = ({
   onJumpToLatest,
   runAfterGte,
   runAfterLte,
+  runIdPattern,
   runType,
   setOffset,
   sharedScrollContainerRef,
@@ -99,6 +101,7 @@ export const Grid = ({
     offset,
     runAfterGte,
     runAfterLte,
+    runIdPattern,
     runType,
     triggeringUser,
   });
@@ -121,6 +124,7 @@ export const Grid = ({
     includeUpstream,
     limit,
     root: filterRoot,
+    runIdPattern,
     runType,
     triggeringUser,
   });
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/GridFilters.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/GridFilters.tsx
index ee30042ef70..ab7a281dd0d 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/GridFilters.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/GridFilters.tsx
@@ -21,6 +21,7 @@ import { SearchParamsKeys } from "src/constants/searchParams";
 import { useFiltersHandler, type FilterableSearchParamsKeys } from "src/utils";
 
 const searchParamKeys: Array<FilterableSearchParamsKeys> = [
+  SearchParamsKeys.RUN_ID_PATTERN,
   SearchParamsKeys.STATE,
   SearchParamsKeys.RUN_TYPE,
   SearchParamsKeys.TRIGGERING_USER_NAME_PATTERN,
diff --git a/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts 
b/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
index f487de7a03f..fe6340f08ad 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
@@ -20,6 +20,8 @@ import { useParams } from "react-router-dom";
 
 import { useGridServiceGetGridRuns } from "openapi/queries";
 import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
+import { SearchParamsKeys } from "src/constants/searchParams";
+import { useAdvancedSearchArg } from "src/hooks/useAdvancedSearch";
 import { isStatePending, useAutoRefresh } from "src/utils";
 
 export const useGridRuns = ({
@@ -28,6 +30,7 @@ export const useGridRuns = ({
   offset,
   runAfterGte,
   runAfterLte,
+  runIdPattern,
   runType,
   triggeringUser,
 }: {
@@ -36,6 +39,7 @@ export const useGridRuns = ({
   offset?: number;
   runAfterGte?: string;
   runAfterLte?: string;
+  runIdPattern?: string | undefined;
   runType?: DagRunType | undefined;
   triggeringUser?: string | undefined;
 }) => {
@@ -43,6 +47,15 @@ export const useGridRuns = ({
 
   const refetchInterval = useAutoRefresh({ dagId });
 
+  // Advanced-search toggle picks between the substring ``runIdPattern`` and 
the
+  // index-friendly ``runIdPrefixPattern`` variants of the Run ID filter.
+  const runIdPatternArg = useAdvancedSearchArg({
+    patternApiKey: "runIdPattern",
+    prefixApiKey: "runIdPrefixPattern",
+    storageKey: SearchParamsKeys.RUN_ID_PATTERN,
+    value: runIdPattern,
+  });
+
   const { data: GridRuns, ...rest } = useGridServiceGetGridRuns(
     {
       dagId,
@@ -51,6 +64,7 @@ export const useGridRuns = ({
       orderBy: ["-run_after"],
       runAfterGte: runAfterGte ?? undefined,
       runAfterLte: runAfterLte ?? undefined,
+      ...runIdPatternArg,
       runType: runType ? [runType] : undefined,
       state: dagRunState ? [dagRunState] : undefined,
       triggeringUser: triggeringUser ?? undefined,
diff --git a/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts 
b/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
index bbae7d789da..702145b2bf5 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
@@ -20,6 +20,8 @@ import { useParams } from "react-router-dom";
 
 import { useGridServiceGetDagStructure } from "openapi/queries";
 import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
+import { SearchParamsKeys } from "src/constants/searchParams";
+import { useAdvancedSearchArg } from "src/hooks/useAdvancedSearch";
 import { useAutoRefresh } from "src/utils";
 
 export const useGridStructure = ({
@@ -30,6 +32,7 @@ export const useGridStructure = ({
   includeUpstream,
   limit,
   root,
+  runIdPattern,
   runType,
   triggeringUser,
 }: {
@@ -40,12 +43,22 @@ export const useGridStructure = ({
   includeUpstream?: boolean;
   limit?: number;
   root?: string;
+  runIdPattern?: string | undefined;
   runType?: DagRunType | undefined;
   triggeringUser?: string | undefined;
 }) => {
   const { dagId = "" } = useParams();
   const refetchInterval = useAutoRefresh({ dagId });
 
+  // Advanced-search toggle picks between the substring ``runIdPattern`` and 
the
+  // index-friendly ``runIdPrefixPattern`` variants of the Run ID filter.
+  const runIdPatternArg = useAdvancedSearchArg({
+    patternApiKey: "runIdPattern",
+    prefixApiKey: "runIdPrefixPattern",
+    storageKey: SearchParamsKeys.RUN_ID_PATTERN,
+    value: runIdPattern,
+  });
+
   // This is necessary for keepPreviousData
   const { data: dagStructure, ...rest } = useGridServiceGetDagStructure(
     {
@@ -56,6 +69,7 @@ export const useGridStructure = ({
       limit,
       orderBy: ["-run_after"],
       root,
+      ...runIdPatternArg,
       runType: runType ? [runType] : undefined,
       state: dagRunState ? [dagRunState] : undefined,
       triggeringUser: triggeringUser ?? undefined,
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
index 17b485b9ebf..ad4450903df 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
@@ -720,6 +720,39 @@ class TestGetGridDataEndpoint:
         assert response.status_code == 200
         assert _strip_dag_version_ids(response.json()) == [GRID_RUN_2]
 
+    @pytest.mark.parametrize(
+        ("endpoint", "run_id_pattern", "expected"),
+        [
+            ("runs", "run_1", [GRID_RUN_1]),
+            ("runs", "run_2", [GRID_RUN_2]),
+            ("runs", "RUN_1", [GRID_RUN_1]),  # ILIKE substring search is 
case-insensitive
+            ("runs", "nonexistent", []),
+            ("structure", "run_1", GRID_NODES),
+        ],
+    )
+    def test_filter_by_run_id_pattern(self, session, test_client, endpoint, 
run_id_pattern, expected):
+        session.commit()
+        response = 
test_client.get(f"/grid/{endpoint}/{DAG_ID}?run_id_pattern={run_id_pattern}")
+        assert response.status_code == 200
+        assert _strip_dag_version_ids(response.json()) == expected
+
+    @pytest.mark.parametrize(
+        ("endpoint", "run_id_prefix_pattern", "expected"),
+        [
+            ("runs", "run_1", [GRID_RUN_1]),
+            ("runs", "run_2", [GRID_RUN_2]),
+            ("runs", "nonexistent", []),
+            ("structure", "run_1", GRID_NODES),
+        ],
+    )
+    def test_filter_by_run_id_prefix_pattern(
+        self, session, test_client, endpoint, run_id_prefix_pattern, expected
+    ):
+        session.commit()
+        response = 
test_client.get(f"/grid/{endpoint}/{DAG_ID}?run_id_prefix_pattern={run_id_prefix_pattern}")
+        assert response.status_code == 200
+        assert _strip_dag_version_ids(response.json()) == expected
+
     @pytest.mark.parametrize(
         ("endpoint", "state", "expected"),
         [

Reply via email to