Skip to content

Commit 432e9e4

Browse files
refactor: remove non-native MongoDB storage option (#221)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: William Easton <strawgate@users.noreply.github.com>
1 parent d0b4bc3 commit 432e9e4

File tree

4 files changed

+28
-260
lines changed

4 files changed

+28
-260
lines changed

key-value/key-value-aio/src/key_value/aio/stores/mongodb/store.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,20 @@
3838

3939

4040
class MongoDBSerializationAdapter(SerializationAdapter):
41-
"""Adapter for MongoDB with support for native and string storage modes."""
41+
"""Adapter for MongoDB with native BSON storage."""
4242

43-
_native_storage: bool
44-
45-
def __init__(self, *, native_storage: bool = True) -> None:
43+
def __init__(self) -> None:
4644
"""Initialize the MongoDB adapter."""
4745
super().__init__()
4846

49-
self._native_storage = native_storage
5047
self._date_format = "datetime"
51-
self._value_format = "dict" if native_storage else "string"
48+
self._value_format = "dict"
5249

5350
@override
5451
def prepare_dump(self, data: dict[str, Any]) -> dict[str, Any]:
5552
value = data.pop("value")
5653

57-
data["value"] = {}
58-
59-
if self._native_storage:
60-
data["value"]["object"] = value
61-
else:
62-
data["value"]["string"] = value
54+
data["value"] = {"object": value}
6355

6456
return data
6557

@@ -69,8 +61,6 @@ def prepare_load(self, data: dict[str, Any]) -> dict[str, Any]:
6961

7062
if "object" in value:
7163
data["value"] = value["object"]
72-
elif "string" in value:
73-
data["value"] = value["string"]
7464
else:
7565
msg = "Value field not found in MongoDB document"
7666
raise DeserializationError(message=msg)
@@ -121,7 +111,6 @@ def __init__(
121111
client: AsyncMongoClient[dict[str, Any]],
122112
db_name: str | None = None,
123113
coll_name: str | None = None,
124-
native_storage: bool = True,
125114
default_collection: str | None = None,
126115
collection_sanitization_strategy: SanitizationStrategy | None = None,
127116
) -> None:
@@ -131,7 +120,6 @@ def __init__(
131120
client: The MongoDB client to use.
132121
db_name: The name of the MongoDB database.
133122
coll_name: The name of the MongoDB collection.
134-
native_storage: Whether to use native BSON storage (True, default) or JSON string storage (False).
135123
default_collection: The default collection to use if no collection is provided.
136124
collection_sanitization_strategy: The sanitization strategy to use for collections.
137125
"""
@@ -143,7 +131,6 @@ def __init__(
143131
url: str,
144132
db_name: str | None = None,
145133
coll_name: str | None = None,
146-
native_storage: bool = True,
147134
default_collection: str | None = None,
148135
collection_sanitization_strategy: SanitizationStrategy | None = None,
149136
) -> None:
@@ -153,7 +140,6 @@ def __init__(
153140
url: The url of the MongoDB cluster.
154141
db_name: The name of the MongoDB database.
155142
coll_name: The name of the MongoDB collection.
156-
native_storage: Whether to use native BSON storage (True, default) or JSON string storage (False).
157143
default_collection: The default collection to use if no collection is provided.
158144
collection_sanitization_strategy: The sanitization strategy to use for collections.
159145
"""
@@ -165,20 +151,18 @@ def __init__(
165151
url: str | None = None,
166152
db_name: str | None = None,
167153
coll_name: str | None = None,
168-
native_storage: bool = True,
169154
default_collection: str | None = None,
170155
collection_sanitization_strategy: SanitizationStrategy | None = None,
171156
) -> None:
172157
"""Initialize the MongoDB store.
173158
159+
Values are stored as native BSON dictionaries for better query support and performance.
160+
174161
Args:
175162
client: The MongoDB client to use (mutually exclusive with url).
176163
url: The url of the MongoDB cluster (mutually exclusive with client).
177164
db_name: The name of the MongoDB database.
178165
coll_name: The name of the MongoDB collection.
179-
native_storage: Whether to use native BSON storage (True, default) or JSON string storage (False).
180-
Native storage stores values as BSON dicts for better query support.
181-
Legacy mode stores values as JSON strings for backward compatibility.
182166
default_collection: The default collection to use if no collection is provided.
183167
collection_sanitization_strategy: The sanitization strategy to use for collections.
184168
"""
@@ -196,7 +180,7 @@ def __init__(
196180

197181
self._db = self._client[db_name]
198182
self._collections_by_name = {}
199-
self._adapter = MongoDBSerializationAdapter(native_storage=native_storage)
183+
self._adapter = MongoDBSerializationAdapter()
200184

201185
super().__init__(
202186
default_collection=default_collection,

key-value/key-value-aio/tests/stores/mongodb/test_mongodb.py

Lines changed: 7 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,14 @@ class MongoDBFailedToStartError(Exception):
4848
pass
4949

5050

51-
def test_managed_entry_document_conversion_native_mode():
51+
def test_managed_entry_document_conversion():
52+
"""Test that documents are stored as BSON dicts."""
5253
created_at = datetime(year=2025, month=1, day=1, hour=0, minute=0, second=0, tzinfo=timezone.utc)
5354
expires_at = created_at + timedelta(seconds=10)
5455

5556
managed_entry = ManagedEntry(value={"test": "test"}, created_at=created_at, expires_at=expires_at)
5657

57-
adapter = MongoDBSerializationAdapter(native_storage=True)
58+
adapter = MongoDBSerializationAdapter()
5859
document = adapter.dump_dict(entry=managed_entry)
5960

6061
assert document == snapshot(
@@ -74,31 +75,6 @@ def test_managed_entry_document_conversion_native_mode():
7475
assert round_trip_managed_entry.expires_at == expires_at
7576

7677

77-
def test_managed_entry_document_conversion_legacy_mode():
78-
created_at = datetime(year=2025, month=1, day=1, hour=0, minute=0, second=0, tzinfo=timezone.utc)
79-
expires_at = created_at + timedelta(seconds=10)
80-
81-
managed_entry = ManagedEntry(value={"test": "test"}, created_at=created_at, expires_at=expires_at)
82-
adapter = MongoDBSerializationAdapter(native_storage=False)
83-
document = adapter.dump_dict(entry=managed_entry)
84-
85-
assert document == snapshot(
86-
{
87-
"version": 1,
88-
"value": {"string": '{"test": "test"}'},
89-
"created_at": datetime(2025, 1, 1, 0, 0, tzinfo=timezone.utc),
90-
"expires_at": datetime(2025, 1, 1, 0, 0, 10, tzinfo=timezone.utc),
91-
}
92-
)
93-
94-
round_trip_managed_entry = adapter.load_dict(data=document)
95-
96-
assert round_trip_managed_entry.value == managed_entry.value
97-
assert round_trip_managed_entry.created_at == created_at
98-
assert round_trip_managed_entry.ttl == IsFloat(lt=0)
99-
assert round_trip_managed_entry.expires_at == expires_at
100-
101-
10278
async def clean_mongodb_database(store: MongoDBStore) -> None:
10379
with contextlib.suppress(Exception):
10480
_ = await store._client.drop_database(name_or_database=store._db.name) # pyright: ignore[reportPrivateUsage]
@@ -151,13 +127,13 @@ async def test_mongodb_collection_name_sanitization(self, sanitizing_store: Mong
151127

152128

153129
@pytest.mark.skipif(should_skip_docker_tests(), reason="Docker is not available")
154-
class TestMongoDBStoreNativeMode(BaseMongoDBStoreTests):
155-
"""Test MongoDBStore with native_storage=True (default)."""
130+
class TestMongoDBStore(BaseMongoDBStoreTests):
131+
"""Test MongoDBStore with native BSON storage."""
156132

157133
@override
158134
@pytest.fixture
159135
async def store(self, setup_mongodb: None) -> MongoDBStore:
160-
store = MongoDBStore(url=f"mongodb://{MONGODB_HOST}:{MONGODB_HOST_PORT}", db_name=f"{MONGODB_TEST_DB}-native", native_storage=True)
136+
store = MongoDBStore(url=f"mongodb://{MONGODB_HOST}:{MONGODB_HOST_PORT}", db_name=MONGODB_TEST_DB)
161137

162138
await clean_mongodb_database(store=store)
163139

@@ -167,8 +143,7 @@ async def store(self, setup_mongodb: None) -> MongoDBStore:
167143
async def sanitizing_store(self, setup_mongodb: None) -> MongoDBStore:
168144
store = MongoDBStore(
169145
url=f"mongodb://{MONGODB_HOST}:{MONGODB_HOST_PORT}",
170-
db_name=f"{MONGODB_TEST_DB}-native-sanitizing",
171-
native_storage=True,
146+
db_name=f"{MONGODB_TEST_DB}-sanitizing",
172147
collection_sanitization_strategy=MongoDBV1CollectionSanitizationStrategy(),
173148
)
174149

@@ -196,83 +171,3 @@ async def test_value_stored_as_bson_dict(self, store: MongoDBStore):
196171
"version": 1,
197172
}
198173
)
199-
200-
async def test_migration_from_legacy_mode(self, store: MongoDBStore):
201-
"""Verify native mode can read legacy JSON string data."""
202-
await store._setup_collection(collection="test") # pyright: ignore[reportPrivateUsage]
203-
sanitized_collection = store._sanitize_collection(collection="test") # pyright: ignore[reportPrivateUsage]
204-
collection = store._collections_by_name[sanitized_collection] # pyright: ignore[reportPrivateUsage]
205-
206-
await collection.insert_one(
207-
{
208-
"key": "legacy_key",
209-
"value": {"string": '{"legacy": "data"}'},
210-
}
211-
)
212-
213-
result = await store.get(collection="test", key="legacy_key")
214-
assert result == {"legacy": "data"}
215-
216-
217-
@pytest.mark.skipif(should_skip_docker_tests(), reason="Docker is not available")
218-
class TestMongoDBStoreNonNativeMode(BaseMongoDBStoreTests):
219-
"""Test MongoDBStore with native_storage=False (legacy mode) for backward compatibility."""
220-
221-
@override
222-
@pytest.fixture
223-
async def store(self, setup_mongodb: None) -> MongoDBStore:
224-
store = MongoDBStore(url=f"mongodb://{MONGODB_HOST}:{MONGODB_HOST_PORT}", db_name=MONGODB_TEST_DB, native_storage=False)
225-
226-
await clean_mongodb_database(store=store)
227-
228-
return store
229-
230-
@pytest.fixture
231-
async def sanitizing_store(self, setup_mongodb: None) -> MongoDBStore:
232-
store = MongoDBStore(
233-
url=f"mongodb://{MONGODB_HOST}:{MONGODB_HOST_PORT}",
234-
db_name=f"{MONGODB_TEST_DB}-sanitizing",
235-
native_storage=False,
236-
collection_sanitization_strategy=MongoDBV1CollectionSanitizationStrategy(),
237-
)
238-
239-
await clean_mongodb_database(store=store)
240-
241-
return store
242-
243-
async def test_value_stored_as_json(self, store: MongoDBStore):
244-
"""Verify values are stored as JSON strings."""
245-
await store.put(collection="test", key="test_key", value={"name": "Alice", "age": 30})
246-
247-
# Get the raw MongoDB document
248-
await store._setup_collection(collection="test") # pyright: ignore[reportPrivateUsage]
249-
sanitized_collection = store._sanitize_collection(collection="test") # pyright: ignore[reportPrivateUsage]
250-
collection = store._collections_by_name[sanitized_collection] # pyright: ignore[reportPrivateUsage]
251-
doc = await collection.find_one({"key": "test_key"})
252-
253-
assert doc == snapshot(
254-
{
255-
"_id": IsInstance(expected_type=ObjectId),
256-
"key": "test_key",
257-
"collection": "test",
258-
"created_at": IsDatetime(),
259-
"value": {"string": '{"age": 30, "name": "Alice"}'},
260-
"version": 1,
261-
}
262-
)
263-
264-
async def test_migration_from_native_mode(self, store: MongoDBStore):
265-
"""Verify non-native mode can read native mode data."""
266-
await store._setup_collection(collection="test") # pyright: ignore[reportPrivateUsage]
267-
sanitized_collection = store._sanitize_collection(collection="test") # pyright: ignore[reportPrivateUsage]
268-
collection = store._collections_by_name[sanitized_collection] # pyright: ignore[reportPrivateUsage]
269-
270-
await collection.insert_one(
271-
{
272-
"key": "legacy_key",
273-
"value": {"object": {"name": "Alice", "age": 30}},
274-
}
275-
)
276-
277-
result = await store.get(collection="test", key="legacy_key")
278-
assert result == {"name": "Alice", "age": 30}

key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,20 @@
4040

4141

4242
class MongoDBSerializationAdapter(SerializationAdapter):
43-
"""Adapter for MongoDB with support for native and string storage modes."""
43+
"""Adapter for MongoDB with native BSON storage."""
4444

45-
_native_storage: bool
46-
47-
def __init__(self, *, native_storage: bool = True) -> None:
45+
def __init__(self) -> None:
4846
"""Initialize the MongoDB adapter."""
4947
super().__init__()
5048

51-
self._native_storage = native_storage
5249
self._date_format = "datetime"
53-
self._value_format = "dict" if native_storage else "string"
50+
self._value_format = "dict"
5451

5552
@override
5653
def prepare_dump(self, data: dict[str, Any]) -> dict[str, Any]:
5754
value = data.pop("value")
5855

59-
data["value"] = {}
60-
61-
if self._native_storage:
62-
data["value"]["object"] = value
63-
else:
64-
data["value"]["string"] = value
56+
data["value"] = {"object": value}
6557

6658
return data
6759

@@ -71,8 +63,6 @@ def prepare_load(self, data: dict[str, Any]) -> dict[str, Any]:
7163

7264
if "object" in value:
7365
data["value"] = value["object"]
74-
elif "string" in value:
75-
data["value"] = value["string"]
7666
else:
7767
msg = "Value field not found in MongoDB document"
7868
raise DeserializationError(message=msg)
@@ -119,7 +109,6 @@ def __init__(
119109
client: MongoClient[dict[str, Any]],
120110
db_name: str | None = None,
121111
coll_name: str | None = None,
122-
native_storage: bool = True,
123112
default_collection: str | None = None,
124113
collection_sanitization_strategy: SanitizationStrategy | None = None,
125114
) -> None:
@@ -129,7 +118,6 @@ def __init__(
129118
client: The MongoDB client to use.
130119
db_name: The name of the MongoDB database.
131120
coll_name: The name of the MongoDB collection.
132-
native_storage: Whether to use native BSON storage (True, default) or JSON string storage (False).
133121
default_collection: The default collection to use if no collection is provided.
134122
collection_sanitization_strategy: The sanitization strategy to use for collections.
135123
"""
@@ -141,7 +129,6 @@ def __init__(
141129
url: str,
142130
db_name: str | None = None,
143131
coll_name: str | None = None,
144-
native_storage: bool = True,
145132
default_collection: str | None = None,
146133
collection_sanitization_strategy: SanitizationStrategy | None = None,
147134
) -> None:
@@ -151,7 +138,6 @@ def __init__(
151138
url: The url of the MongoDB cluster.
152139
db_name: The name of the MongoDB database.
153140
coll_name: The name of the MongoDB collection.
154-
native_storage: Whether to use native BSON storage (True, default) or JSON string storage (False).
155141
default_collection: The default collection to use if no collection is provided.
156142
collection_sanitization_strategy: The sanitization strategy to use for collections.
157143
"""
@@ -163,20 +149,18 @@ def __init__(
163149
url: str | None = None,
164150
db_name: str | None = None,
165151
coll_name: str | None = None,
166-
native_storage: bool = True,
167152
default_collection: str | None = None,
168153
collection_sanitization_strategy: SanitizationStrategy | None = None,
169154
) -> None:
170155
"""Initialize the MongoDB store.
171156
157+
Values are stored as native BSON dictionaries for better query support and performance.
158+
172159
Args:
173160
client: The MongoDB client to use (mutually exclusive with url).
174161
url: The url of the MongoDB cluster (mutually exclusive with client).
175162
db_name: The name of the MongoDB database.
176163
coll_name: The name of the MongoDB collection.
177-
native_storage: Whether to use native BSON storage (True, default) or JSON string storage (False).
178-
Native storage stores values as BSON dicts for better query support.
179-
Legacy mode stores values as JSON strings for backward compatibility.
180164
default_collection: The default collection to use if no collection is provided.
181165
collection_sanitization_strategy: The sanitization strategy to use for collections.
182166
"""
@@ -194,7 +178,7 @@ def __init__(
194178

195179
self._db = self._client[db_name]
196180
self._collections_by_name = {}
197-
self._adapter = MongoDBSerializationAdapter(native_storage=native_storage)
181+
self._adapter = MongoDBSerializationAdapter()
198182

199183
super().__init__(default_collection=default_collection, collection_sanitization_strategy=collection_sanitization_strategy)
200184

0 commit comments

Comments
 (0)