From 5431badfd4ebdb56ebf12b6410a14417043125be Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 24 Mar 2026 10:58:21 +0530 Subject: [PATCH] fix: preserve custom metadata when updating memory (#4495) --- mem0/vector_stores/mongodb.py | 3 +- tests/vector_stores/test_mongodb.py | 46 ++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/mem0/vector_stores/mongodb.py b/mem0/vector_stores/mongodb.py index 7d1e82bb6..b9cf0aa6b 100644 --- a/mem0/vector_stores/mongodb.py +++ b/mem0/vector_stores/mongodb.py @@ -197,7 +197,8 @@ class MongoDB(VectorStoreBase): if vector is not None: update_fields["embedding"] = vector if payload is not None: - update_fields["payload"] = payload + for key, value in payload.items(): + update_fields[f"payload.{key}"] = value if update_fields: try: diff --git a/tests/vector_stores/test_mongodb.py b/tests/vector_stores/test_mongodb.py index d75a05846..33a8a7ad0 100644 --- a/tests/vector_stores/test_mongodb.py +++ b/tests/vector_stores/test_mongodb.py @@ -203,6 +203,10 @@ def test_delete(mongo_vector_fixture): def test_update(mongo_vector_fixture): + """ + Test that update() uses dot notation for payload fields instead of replacing + the entire payload document. + """ mongo_vector, mock_collection, _ = mongo_vector_fixture vector_id = "id1" updated_vector = [0.3] * 1536 @@ -212,11 +216,51 @@ def test_update(mongo_vector_fixture): mongo_vector.update(vector_id=vector_id, vector=updated_vector, payload=updated_payload) + # Should use dot notation (payload.name) instead of full replacement (payload) mock_collection.update_one.assert_called_once_with( - {"_id": vector_id}, {"$set": {"embedding": updated_vector, "payload": updated_payload}} + {"_id": vector_id}, {"$set": {"embedding": updated_vector, "payload.name": "updated_vector"}} ) +def test_update_payload_only_uses_dot_notation(mongo_vector_fixture): + """ + Test that updating only the payload uses dot notation for each field, + preserving existing metadata fields not included in the update. + """ + mongo_vector, mock_collection, _ = mongo_vector_fixture + + mock_collection.update_one.return_value = MagicMock(matched_count=1) + + mongo_vector.update( + vector_id="id1", + payload={"data": "updated text", "hash": "def456", "updated_at": "2025-06-01"}, + ) + + set_arg = mock_collection.update_one.call_args[0][1]["$set"] + + # Only the specified fields should be in $set, using dot notation + assert set_arg == { + "payload.data": "updated text", + "payload.hash": "def456", + "payload.updated_at": "2025-06-01", + } + # "payload" key itself should NOT appear (that would replace the whole document) + assert "payload" not in set_arg + + +def test_update_vector_only_does_not_touch_payload(mongo_vector_fixture): + """Test that updating only the vector does not touch any payload fields.""" + mongo_vector, mock_collection, _ = mongo_vector_fixture + + mock_collection.update_one.return_value = MagicMock(matched_count=1) + + mongo_vector.update(vector_id="id1", vector=[0.7, 0.8, 0.9]) + + set_arg = mock_collection.update_one.call_args[0][1]["$set"] + assert set_arg == {"embedding": [0.7, 0.8, 0.9]} + assert not any(k.startswith("payload.") for k in set_arg) + + def test_get(mongo_vector_fixture): mongo_vector, mock_collection, _ = mongo_vector_fixture vector_id = "id1"