pierrejeambrun opened a new pull request, #66008:
URL: https://github.com/apache/airflow/pull/66008

   ## Summary
   
   Closes #51820.
   
   Adds a `rendered_map_index` exact-match filter (list-of-strings, 
`ANY_EQUAL`) to the public `listMapped` and `listTaskInstances` endpoints, 
mirroring the existing `map_index` filter, and surfaces it in the **Task 
Instances** filter bar in the UI. Users can now find a dynamically mapped task 
by the named mapping that's already shown in the column — no more "search by 
what you see" gap.
   
   ## Why a hybrid expression on the model
   
   The `TaskInstance.rendered_map_index` hybrid_property's Python accessor has 
a fallback:
   
   ```python
   if self._rendered_map_index is not None:
       return self._rendered_map_index
   if self.map_index >= 0:
       return str(self.map_index)
   return None
   ```
   
   So a mapped task with no explicit `map_index_template` gets 
`"rendered_map_index": "1"` in the API response (from the column-NULL 
fallback). The hybrid had no `.expression`, which meant SQL-level resolution 
silently fell back to the bare `_rendered_map_index` column. Filtering on it 
would then miss every row whose displayed value came from the fallback. This PR 
adds the matching SQL-level expression:
   
   ```python
   @rendered_map_index.expression
   def rendered_map_index(cls):
       return case(
           (cls._rendered_map_index.isnot(None), cls._rendered_map_index),
           (cls.map_index >= 0, cast(cls.map_index, String)),
           else_=None,
       )
   ```
   
   — so SQL and Python now agree on what the value is. Side benefit: ordering 
by `rendered_map_index` is now also consistent with the displayed value.
   
   ## Indexes
   
   The `rendered_map_index` column is **not indexed by default**, and this PR 
doesn't add one. The typical UI path filters within a specific `(dag_id, 
run_id, task_id)`, where the existing `task_instance_composite_key` already 
narrows the scan to that task's mapped TIs before the CASE/COALESCE predicate 
is evaluated — so no extra index is needed. Deployments doing unscoped 
cross-DAG filtering on very large `task_instance` tables can add their own 
(`CREATE INDEX ti_rendered_map_index ON task_instance (rendered_map_index)`), 
but that index can't help the `str(map_index)` fallback branch.
   
   ## Tests
   
   - 6 parametrized cases on 
`TestGetMappedTaskInstances::test_rendered_map_index_filter`: explicit named 
values, multi-value OR'd, exact-match-only (no prefix), missing value, and 
**the original customer scenario — filtering by `["7"]` matches a TI whose 
`_rendered_map_index` is NULL but `map_index = 7`**.
   - 2 cases on `TestGetTaskInstances` (single value + multi-value OR'd) for 
the cross-DAG endpoint.
   - New `test_rendered_map_index_order` exercising both branches of the CASE 
in ORDER BY.
   - `test_expand_mapped_task_instance_with_named_index[*-unset]` expectation 
updated from `[None, None]` to `["0", "1"]` to reflect the now-consistent 
fallback (the Python-level accessor already returned the fallback; only SQL was 
inconsistent).
   
   ## Test plan
   
   - [x] Unit tests pass (`pytest test_task_instances.py -k 
rendered_map_index_filter`)
   - [x] TypeScript + ESLint clean
   - [x] Manually verified in breeze with the customer-reported curl: 
`?rendered_map_index=1` now returns the row whose `_rendered_map_index` is NULL.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes — Claude Code (Opus 4.7)
   
   Generated-by: Claude Code (Opus 4.7) following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to