From 703e8a035d0293b96fac37ba0600281753e4f96b Mon Sep 17 00:00:00 2001 From: Yash Raj Pandey <55940078+devYRPauli@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:04:27 -0400 Subject: [PATCH] fix: FAISS filtered search drops over-fetched candidates before filtering (#5453) Co-authored-by: kartik-mem0 --- mem0/vector_stores/faiss.py | 2 +- tests/vector_stores/test_faiss.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mem0/vector_stores/faiss.py b/mem0/vector_stores/faiss.py index 0c4573fbd..103b99739 100644 --- a/mem0/vector_stores/faiss.py +++ b/mem0/vector_stores/faiss.py @@ -390,7 +390,7 @@ class FAISS(VectorStoreBase): fetch_k = top_k * 2 if filters else top_k scores, indices = self.index.search(query_vectors, fetch_k) - results = self._parse_output(scores[0], indices[0], top_k) + results = self._parse_output(scores[0], indices[0], fetch_k) if filters: filtered_results = [] diff --git a/tests/vector_stores/test_faiss.py b/tests/vector_stores/test_faiss.py index 667ab3aeb..6333b119a 100644 --- a/tests/vector_stores/test_faiss.py +++ b/tests/vector_stores/test_faiss.py @@ -173,6 +173,32 @@ def test_search_with_filters(faiss_instance, mock_faiss_index): assert results[0].payload == {"name": "vector1", "category": "A"} +def test_search_with_filters_overfetch_not_truncated(faiss_instance, mock_faiss_index): + query_vector = [0.1, 0.2, 0.3] + + # Four stored vectors: the two nearest fail the filter, the next two pass it. + faiss_instance.docstore = { + "id1": {"name": "v1", "category": "B"}, + "id2": {"name": "v2", "category": "B"}, + "id3": {"name": "v3", "category": "A"}, + "id4": {"name": "v4", "category": "A"}, + } + faiss_instance.index_to_id = {0: "id1", 1: "id2", 2: "id3", 3: "id4"} + + # top_k=2 with filters -> fetch_k = 4; the index returns all four candidates. + search_scores = np.array([[0.9, 0.8, 0.7, 0.6]]) + search_indices = np.array([[0, 1, 2, 3]]) + mock_faiss_index.search.return_value = (search_scores, search_indices) + + results = faiss_instance.search( + query="test query", vectors=query_vector, top_k=2, filters={"category": "A"} + ) + + # Two matching vectors exist among the over-fetched set, so we must get top_k of them. + assert len(results) == 2 + assert [r.id for r in results] == ["id3", "id4"] + + def test_delete(faiss_instance, mock_faiss_index): # Setup the docstore and index_to_id mapping faiss_instance.docstore = {"id1": {"name": "vector1"}, "id2": {"name": "vector2"}}