Repository: mlflow/mlflow

Issue:
[BUG] _expand_duplicate_retrieved_docs adds predictions' id to targets.
### Issues Policy acknowledgement

- [X] I have read and agree to submit bug reports in accordance with the [issues policy](https://www.github.com/mlflow/mlflow/blob/master/ISSUE_POLICY.md)

### Where did you encounter this bug?

Local machine

### Willingness to contribute

Yes. I can contribute a fix for this bug independently.

### MLflow version

mlflow 2.13.1

### System information

- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: Linux Ubuntu 22.04.4
- **Python version**: 3.10
- **yarn version, if running the dev UI**: NA


### Describe the problem

The function `mlflow.metrics.metric_definitions._expand_duplicate_retrieved_docs` is currently designed to rename duplicate IDs in predictions for the purpose of counting them when calculating NDCG.
However, this function adds these renamed duplicate IDs into the targets, without checking if the original IDs are present in the targets.

The expected behavior should be that these renamed duplicate IDs are added to the targets only if their corresponding original IDs are already in the targets. 
 

### Tracking information

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```shell
REPLACE_ME
```


### Code to reproduce issue

```python
from mlflow.metrics import ndcg_at_k

import pandas as pd
targets = pd.Series([["a", "b"]])
predictions = pd.Series([["c", "c", "c"]])
metrics = ndcg_at_k(k=3).eval_fn(predictions, targets)
print(metrics.scores)

# expected: 0.0 as no ids in the predictions match to targets'
# returned: 0.5307212739772434
```

### Stack trace

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```
REPLACE_ME
```


### Other info / logs

<!-- PLEASE KEEP BACKTICKS AND CHECK PREVIEW -->
```
REPLACE_ME
```


### What component(s) does this bug affect?

- [ ] `area/artifacts`: Artifact stores and artifact logging
- [ ] `area/build`: Build and test infrastructure for MLflow
- [ ] `area/deployments`: MLflow Deployments client APIs, server, and third-party Deployments integrations
- [ ] `area/docs`: MLflow documentation pages
- [ ] `area/examples`: Example code
- [ ] `area/model-registry`: Model Registry service, APIs, and the fluent client calls for Model Registry
- [ ] `area/models`: MLmodel format, model serialization/deserialization, flavors
- [ ] `area/recipes`: Recipes, Recipe APIs, Recipe configs, Recipe Templates
- [ ] `area/projects`: MLproject format, project running backends
- [ ] `area/scoring`: MLflow Model server, model deployment tools, Spark UDFs
- [ ] `area/server-infra`: MLflow Tracking server backend
- [ ] `area/tracking`: Tracking Service, tracking client APIs, autologging

### What interface(s) does this bug affect?

- [ ] `area/uiux`: Front-end, user experience, plotting, JavaScript, JavaScript dev server
- [ ] `area/docker`: Docker use across MLflow's components, such as MLflow Projects and MLflow Models
- [ ] `area/sqlalchemy`: Use of SQLAlchemy in the Tracking Service or Model Registry
- [ ] `area/windows`: Windows support

### What language(s) does this bug affect?

- [ ] `language/r`: R APIs and clients
- [ ] `language/java`: Java APIs and clients
- [ ] `language/new`: Proposals for new client languages

### What integration(s) does this bug affect?

- [ ] `integrations/azure`: Azure and Azure ML integrations
- [ ] `integrations/sagemaker`: SageMaker integrations
- [ ] `integrations/databricks`: Databricks integrations

Discussion:
github-actions[bot]: <!-- documentation preview -->


Documentation preview for 329b4f08c04fd2ada0e40771b316e8df587d1e51 will be available when [this CircleCI job](https://circleci.com/gh/mlflow/mlflow/112186)
completes successfully.

- [Top page](https://output.circle-artifacts.com/output/job/7de484df-69b9-44fd-a99d-610c39e0e33c/artifacts/0/docs/build/html/index.html)
- [Changed pages](https://output.circle-artifacts.com/output/job/7de484df-69b9-44fd-a99d-610c39e0e33c/artifacts/0/docs/build/html/diff.html)

<details>
<summary>More info</summary>

- Ignore this comment if this PR does not change the documentation.
- It takes a few minutes for the preview to be available.
- The preview is updated when a new commit is pushed to this PR.
- This comment was created by https://github.com/mlflow/mlflow/actions/runs/9610357147.

</details>


Patch:
diff --git a/mlflow/metrics/metric_definitions.py b/mlflow/metrics/metric_definitions.py
index 65fa71365..6e6f00fc4 100644
--- a/mlflow/metrics/metric_definitions.py
+++ b/mlflow/metrics/metric_definitions.py
@@ -395,7 +395,8 @@ def _expand_duplicate_retrieved_docs(predictions, targets):
                 f"{doc_id}_bc574ae_{counter[doc_id]}"  # adding a random string to avoid collisions
             )
             expanded_predictions.append(new_doc_id)
-            expanded_targets.add(new_doc_id)
+            if doc_id in expanded_targets:
+                expanded_targets.add(new_doc_id)
     return expanded_predictions, expanded_targets
 
 


Test patch:
diff --git a/tests/metrics/test_metric_definitions.py b/tests/metrics/test_metric_definitions.py
index 634afb7fd..692d98ecf 100644
--- a/tests/metrics/test_metric_definitions.py
+++ b/tests/metrics/test_metric_definitions.py
@@ -333,3 +333,8 @@ def test_ndcg_at_k():
         result = ndcg_at_k(k).eval_fn(predictions, targets)
         # row 1 and 2 have the same ndcg score
         assert pytest.approx(result.scores[0]) == pytest.approx(result.scores[1])
+
+    predictions = pd.Series([["c", "c", "c"]])
+    targets = pd.Series([["a", "b"]])
+    result = ndcg_at_k(k=3).eval_fn(predictions, targets)
+    assert result.scores[0] == 0.0