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

dheerajturaga pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-3-test by this push:
     new be661921e37 [v3-3-test] UI: Add Run ID filter to the Grid view 
(#70150) (#70553)
be661921e37 is described below

commit be661921e3764b1262a635dd6ec5dc012fff14ed
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Jul 27 15:51:59 2026 -0500

    [v3-3-test] UI: Add Run ID filter to the Grid view (#70150) (#70553)
    
    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.
    (cherry picked from commit 51972109907ca274bbfa52aff3e2c71c7b02bcab)
    
    Co-authored-by: Dheeraj Turaga <[email protected]>
---
 .../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 ab9b221aba6..6e089005d81 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
@@ -1223,6 +1223,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
@@ -1378,6 +1404,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 ee81e2887c7..121cc79ec64 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
@@ -324,7 +346,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 d7baf71cf53..eb45a01065f 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -975,7 +975,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;
@@ -988,15 +988,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;
@@ -1005,11 +1007,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 73a9f35b95b..aa89e9e84ee 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1890,10 +1890,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;
@@ -1906,11 +1908,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.
@@ -1927,10 +1931,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;
@@ -1939,11 +1945,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 bc41b8baad5..7f3668ce6e4 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1890,10 +1890,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;
@@ -1906,11 +1908,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.
@@ -1927,10 +1931,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;
@@ -1939,11 +1945,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 6ac7a2fb5c2..0f705389911 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1890,10 +1890,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;
@@ -1906,11 +1908,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.
@@ -1927,10 +1931,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;
@@ -1939,11 +1945,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 54edc11c8b0..49eeace6880 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1890,10 +1890,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;
@@ -1906,11 +1908,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.
@@ -1927,10 +1931,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;
@@ -1939,11 +1945,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 bb1fa6a6747..78f6b2c1218 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
@@ -4878,6 +4878,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
      */
@@ -4903,7 +4905,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',
@@ -4929,6 +4933,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
      */
@@ -4950,7 +4956,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 dc81f64b4f9..80b6c188230 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
@@ -4512,6 +4512,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)>;
     /**
@@ -4538,6 +4546,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 c55f3bd0d37..d5f0431f81a 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 fe1ea80ab1a..f11b1098ac6 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 d55b2df3625..2b974148c68 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
@@ -704,6 +704,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