Exporting Merge Requests by CSV ignores some filters
## Summary
CSV export of merge requests returns significantly more results than displayed in the UI when using filters on the "merged" tab (specifically `merged_by`, `target_branch`, and `deployed_after`). Seemingly some filters are ignored when exporting the CSV and it results in a mismatch between the amount being filtered and the amount actually in the CSV.
## Steps to reproduce
1. Navigate to a project's merge requests page
2. Apply a mix of the following filters on the "merged" tab:
- Sort: `created_asc`
- State: `merged`
- Merged by: specific user (e.g., `cleveland`)
- Target branch: `main`
- Deployed after: `2025-01-01`
3. Note the count displayed in the UI (e.g., 933 entries)
4. Click the three-vertical-dot menu and select "Export as CSV"
5. Check the received CSV file
**Example URL:**
```
https://gitlab.example.com/project/-/merge_requests/?sort=created_asc&state=merged&merge_user=cleveland&target_branches%5B%5D=main&deployed_after=2025-01-01&first_page_size=20
```
## Expected behavior
The CSV export should contain the same number of merge requests as displayed in the UI.
## Actual behavior
The CSV export contains significantly more entries than shown in the UI (2000+ entries in the example, more than double the expected count).
## Relevant logs and/or screenshots
Note in my example, I have two of my own users to test with. In the UI, I am filtering by one user and should have 3 MRs, but the CSV contains 4, one of which is my other user which was not filtered out.

{width=900 height=463}
## Environment
- **GitLab version:** Reported in GitLab SM 18.2.1 (replicated on GitLab.com)
## Root Cause Analysis (Duo Assisted)
The issue stems from three filters in `MergeRequestsFinder` that don't properly respect the `state` parameter constraint when joining related tables:
### 1. `by_merge_user` filter (MergeUserFilter)
- Joins to `merge_request_metrics` table without re-applying state constraint
- Returns MRs with matching metrics regardless of actual merge state
### 2. `by_merged_at` filter (MergedAtFilter)
- Similar issue with `merge_request_metrics` join
- Doesn't verify state after applying date filters
### 3. `by_deployments` filter
- Joins to `deployment_merge_requests` without state verification
- Can return non-merged MRs if deployment records exist
**Code references:**
- https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/finders/concerns/merge_user_filter.rb
- https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/finders/concerns/merged_at_filter.rb
- https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/finders/merge_requests_finder.rb#L265-L283
- https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/merge_requests/export_csv_service.rb
## Possible fixes
The filters should re-apply the state constraint after joining to related tables, or the join scopes should include state verification in their WHERE clauses.
## Workaround
Use GraphQL API to query merge requests with proper filtering:
```graphql
query {
project(fullPath: "group/project") {
mergeRequests(
state: merged
mergedBy: "username"
deployedAfter: "2025-01-01T00:00:00Z"
first: 100
) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
iid
title
state
targetBranch
sourceBranch
mergeUser {
username
}
createdAt
mergedAt
}
}
}
}
}
```
issue