Skip to content

fix(sqlserver): preserve MMR ranking order in results - #813

Open
DevNinja (0xDevNinja) wants to merge 3 commits into
langchain-ai:mainfrom
0xDevNinja:fix/sqlserver-mmr-order
Open

fix(sqlserver): preserve MMR ranking order in results#813
DevNinja (0xDevNinja) wants to merge 3 commits into
langchain-ai:mainfrom
0xDevNinja:fix/sqlserver-mmr-order

Conversation

@0xDevNinja

Copy link
Copy Markdown
Contributor

Problem

max_marginal_relevance_search_by_vector returns documents in the store's original fetch order rather than the order chosen by the MMR algorithm.

maximal_marginal_relevance returns a list of indices in ranked (relevance/diversity) order, but the result was built as:

return [
    value for idx, value in enumerate(results_as_docs) if idx in mmr_selects
]

Iterating results_as_docs and filtering by membership re-sorts the selected docs back into ascending fetch order, discarding the MMR ranking.

Example: query [1, 0] over docs A[0.9, 0.1], B[0.0, 1.0], C[1.0, 0.0] with k=2. MMR selects [2, 0] → should return [C, A], but the old code returned [A, C].

Fix

Index into results_as_docs by the selected indices so the ranking is preserved:

return [
    results_as_docs[idx] for idx in mmr_selects if idx < len(results_as_docs)
]

Tests

Added tests/unit_tests/test_mmr.py, which stubs _search_store and asserts the MMR ordering ([C, A]). Runs without a live database. Ruff + mypy clean; unit suite passes.

max_marginal_relevance_search_by_vector returned documents in the store's
original fetch order instead of the order chosen by the MMR algorithm. It
filtered results_as_docs by `idx in mmr_selects`, which discards the ranking
that maximal_marginal_relevance returns. Index into results_as_docs by the
selected indices so the diversity-optimized order is preserved.

Add a unit test that asserts the MMR ordering without needing a live DB.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes SQLServerVectorStore.max_marginal_relevance_search_by_vector to preserve the document ranking produced by the MMR algorithm, instead of returning results in the store fetch order.

Changes:

  • Build the MMR results by indexing results_as_docs using mmr_selects to preserve ranking.
  • Add a unit test that stubs _search_store to validate MMR ordering without a live database.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
libs/sqlserver/langchain_sqlserver/vectorstores.py Adjusts MMR result construction to return documents in MMR-selected order.
libs/sqlserver/tests/unit_tests/test_mmr.py Adds a unit test asserting the returned document order matches MMR ranking.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

# into the original fetch order.
return [
value for idx, value in enumerate(results_as_docs) if idx in mmr_selects
results_as_docs[idx] for idx in mmr_selects if idx < len(results_as_docs)
Comment on lines +42 to +44
docs = store.max_marginal_relevance_search_by_vector(
[1.0, 0.0], k=2, fetch_k=3, lambda_mult=0.5
)
Address review feedback:

- The bounds check only rejected indices past the end of the result list, so
  a negative index would have silently wrapped around and indexed from the
  end. `maximal_marginal_relevance` can return -1 when it cannot select a
  next candidate, so require `0 <= idx` as well.
- The ordering test used `lambda_mult=0.5`, where the relevance and
  redundancy terms cancel for both remaining candidates and the second pick
  depends on tie-breaking rather than the ranking. Use 0.7 so the expected
  order is determined by the MMR score itself.
@0xDevNinja

Copy link
Copy Markdown
Contributor Author

Both good catches, thanks — fixed in a5af8a9.

  1. Bounds check now requires 0 <= idx < len(results_as_docs). You're right that a -1 would have wrapped to the last element and quietly masked an upstream MMR failure.
  2. Confirmed the tie: with query [1, 0], the first pick is C, and at lambda_mult=0.5 both remaining candidates score exactly 0 (relevance and redundancy cancel — A is 0.50.994 - 0.50.994, B is 0.50 - 0.50), so the order was only holding by tie-break. Switched the test to lambda_mult=0.7, where A scores ~0.398 vs B at 0, so [C, A] follows from the ranking itself.

Also merged current main into the branch so this is verified against the use_binary_collation changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants