INTPYTHON-1060 fix(deepagents-vfs): bound the fulltext branch of hybrid grep's $rankFusion (#430)
## Summary
- `SearchRouter._grep_hybrid`'s `$rankFusion` combines a vector branch
and a fulltext branch, but only the vector branch was actually bounded:
`vector_search_stage()` bakes `limit`/`numCandidates` directly into the
`$vectorSearch` stage itself, while the hand-rolled fulltext branch
(`$search` + optional `$match`) had no `$limit` of its own — only a
single `$limit` trailing the entire `$rankFusion` combination.
- A common search term against a large collection let the fulltext
branch rank an unbounded candidate set before that trailing limit ever
got a chance to trim it, and the two branches feeding the same RRF
combination were sized asymmetrically.
- Adds an explicit `{"$limit": top_k}` to the fulltext branch, matching
the vector branch's `top_k` (`grep_limit * 2`), so both branches are
bounded the same way before combination.
Kept the existing hand-rolled `$search` stage rather than switching to
the `text_search_stage()` helper already used elsewhere in this file —
that helper defaults to emitting an extra `$set: {score: ...}` stage,
which is wasted work here since `$rankFusion` combines by rank order,
not raw `$search` score, and nothing downstream reads a `score` field.
Note: this does not address the separate, larger question of whether
`path`/`glob` scoping should be pushed into the
`$search`/`$vectorSearch` stages themselves rather than applied as a
post-search `$match` — that's tracked separately (see
`jira_grep_hybrid.txt` for the fuller writeup) as a decision that needs
verifying Atlas Search's `compound.filter` support, not something to
bundle into this minimal fix.
## Test plan
- [x] Added `TestGrepHybridPipelineShape` in `test_search_router.py`:
captures the constructed `$rankFusion` pipeline via a mocked collection
and asserts the fulltext branch has its own `$limit`, matching the
vector branch's `top_k`.
- [x] `uv run pytest tests/unit_tests tests/integration_tests` — all
pass (156 tests).
- [x] `just lint` / `just typing` — clean.
Signed-off-by: Casey Clements <casey.clements@mongodb.com> C
Casey Clements committed
7e48ba2089b3365b213f3270d2bc1e8e1ef67f90
Parent: d6bd32b
Committed by GitHub <noreply@github.com>
on 8/19/2026, 10:32:10 PM