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:
co-authored by
utkarsh240799
Claude Opus 4.6
parent
a2ffca3266
commit
3ac4e047de
+3
-3
@@ -977,11 +977,11 @@ class Memory(MemoryBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if operator in operator_map:
|
if operator in operator_map:
|
||||||
result[key] = {operator_map[operator]: value}
|
result.setdefault(key, {})[operator_map[operator]] = value
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported metadata filter operator: {operator}")
|
raise ValueError(f"Unsupported metadata filter operator: {operator}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
for key, value in metadata_filters.items():
|
for key, value in metadata_filters.items():
|
||||||
if key == "AND":
|
if key == "AND":
|
||||||
# Logical AND: combine multiple conditions
|
# Logical AND: combine multiple conditions
|
||||||
@@ -2099,7 +2099,7 @@ class AsyncMemory(MemoryBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if operator in operator_map:
|
if operator in operator_map:
|
||||||
result[key] = {operator_map[operator]: value}
|
result.setdefault(key, {})[operator_map[operator]] = value
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported metadata filter operator: {operator}")
|
raise ValueError(f"Unsupported metadata filter operator: {operator}")
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -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["created_at"] == "2024-01-01T00:00:00+00:00"
|
||||||
assert call_kwargs["updated_at"] is not None
|
assert call_kwargs["updated_at"] is not None
|
||||||
datetime.fromisoformat(call_kwargs["updated_at"]) # verify valid ISO timestamp
|
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},
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user