fix: merge multiple filter operators for same key (#4559)

Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Utkarsh
2026-03-26 20:28:37 +05:30
committed by GitHub
parent a2ffca3266
commit 3ac4e047de
2 changed files with 47 additions and 3 deletions
+3 -3
View File
@@ -977,11 +977,11 @@ class Memory(MemoryBase):
}
if operator in operator_map:
result[key] = {operator_map[operator]: value}
result.setdefault(key, {})[operator_map[operator]] = value
else:
raise ValueError(f"Unsupported metadata filter operator: {operator}")
return result
for key, value in metadata_filters.items():
if key == "AND":
# Logical AND: combine multiple conditions
@@ -2099,7 +2099,7 @@ class AsyncMemory(MemoryBase):
}
if operator in operator_map:
result[key] = {operator_map[operator]: value}
result.setdefault(key, {})[operator_map[operator]] = value
else:
raise ValueError(f"Unsupported metadata filter operator: {operator}")
return result
+44
View File
@@ -713,3 +713,47 @@ async def test_async_delete_memory_history_has_timestamps(mock_sqlite, mock_llm_
assert call_kwargs["created_at"] == "2024-01-01T00:00:00+00:00"
assert call_kwargs["updated_at"] is not None
datetime.fromisoformat(call_kwargs["updated_at"]) # verify valid ISO timestamp
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
class TestProcessMetadataFiltersMerge:
"""Regression tests for issue #3952: multiple operators on the same key must be merged."""
def _make_memory(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
mock_embedder_factory.return_value = MagicMock()
mock_vector_factory.return_value = MagicMock()
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
from mem0.memory.main import Memory as MemoryClass
return MemoryClass(MemoryConfig())
def test_multiple_operators_same_key_merged(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""Filters like created_at: {gte: X, lte: Y} must preserve both operators."""
memory = self._make_memory(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory)
result = memory._process_metadata_filters({
"created_at": {"gte": 1000, "lte": 2000}
})
assert result == {"created_at": {"gte": 1000, "lte": 2000}}
def test_single_operator_still_works(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""Single operator filters must continue to work."""
memory = self._make_memory(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory)
result = memory._process_metadata_filters({
"created_at": {"gte": 1000}
})
assert result == {"created_at": {"gte": 1000}}
def test_multiple_keys_with_multiple_operators(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""Multiple keys each with multiple operators."""
memory = self._make_memory(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory)
result = memory._process_metadata_filters({
"created_at": {"gte": 1000, "lte": 2000},
"score": {"gt": 0.5, "lt": 0.9},
})
assert result == {
"created_at": {"gte": 1000, "lte": 2000},
"score": {"gt": 0.5, "lt": 0.9},
}