altcodepro-polydb-python 2.2.2__py3-none-any.whl → 2.2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- altcodepro_polydb_python-2.2.4.dist-info/METADATA +489 -0
- altcodepro_polydb_python-2.2.4.dist-info/RECORD +57 -0
- {altcodepro_polydb_python-2.2.2.dist-info → altcodepro_polydb_python-2.2.4.dist-info}/WHEEL +1 -1
- polydb/__init__.py +2 -2
- polydb/adapters/AzureBlobStorageAdapter.py +146 -41
- polydb/adapters/AzureFileStorageAdapter.py +148 -43
- polydb/adapters/AzureQueueAdapter.py +96 -34
- polydb/adapters/AzureTableStorageAdapter.py +462 -119
- polydb/adapters/BlockchainBlobAdapter.py +111 -0
- polydb/adapters/BlockchainKVAdapter.py +152 -0
- polydb/adapters/BlockchainQueueAdapter.py +116 -0
- polydb/adapters/DynamoDBAdapter.py +463 -176
- polydb/adapters/FirestoreAdapter.py +320 -148
- polydb/adapters/GCPPubSubAdapter.py +217 -0
- polydb/adapters/GCPStorageAdapter.py +184 -39
- polydb/adapters/MongoDBAdapter.py +159 -39
- polydb/adapters/PostgreSQLAdapter.py +285 -83
- polydb/adapters/S3Adapter.py +172 -35
- polydb/adapters/S3CompatibleAdapter.py +62 -8
- polydb/adapters/SQSAdapter.py +121 -44
- polydb/adapters/VercelBlobAdapter.py +196 -0
- polydb/adapters/VercelKVAdapter.py +275 -283
- polydb/adapters/VercelQueueAdapter.py +61 -0
- polydb/audit/AuditStorage.py +1 -1
- polydb/base/NoSQLKVAdapter.py +113 -101
- polydb/base/ObjectStorageAdapter.py +42 -6
- polydb/base/QueueAdapter.py +2 -2
- polydb/base/SharedFilesAdapter.py +2 -2
- polydb/cloudDatabaseFactory.py +200 -0
- polydb/databaseFactory.py +434 -101
- polydb/models.py +63 -1
- polydb/query.py +111 -42
- altcodepro_polydb_python-2.2.2.dist-info/METADATA +0 -379
- altcodepro_polydb_python-2.2.2.dist-info/RECORD +0 -52
- polydb/adapters/PubSubAdapter.py +0 -85
- polydb/factory.py +0 -107
- {altcodepro_polydb_python-2.2.2.dist-info → altcodepro_polydb_python-2.2.4.dist-info}/licenses/LICENSE +0 -0
- {altcodepro_polydb_python-2.2.2.dist-info → altcodepro_polydb_python-2.2.4.dist-info}/top_level.txt +0 -0
|
@@ -1,83 +1,169 @@
|
|
|
1
1
|
# src/polydb/adapters/mongodb.py
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
2
5
|
import os
|
|
3
6
|
import re
|
|
4
7
|
import threading
|
|
5
8
|
from typing import Any, Dict, List, Optional
|
|
6
|
-
from polydb.base.NoSQLKVAdapter import NoSQLKVAdapter
|
|
7
9
|
|
|
8
|
-
from ..
|
|
10
|
+
from ..base.NoSQLKVAdapter import NoSQLKVAdapter
|
|
11
|
+
from ..errors import NoSQLError, ConnectionError, DatabaseError
|
|
9
12
|
from ..retry import retry
|
|
10
13
|
from ..types import JsonDict
|
|
11
14
|
from ..models import PartitionConfig
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
class MongoDBAdapter(NoSQLKVAdapter):
|
|
15
|
-
"""MongoDB with
|
|
18
|
+
"""MongoDB adapter compatible with PolyDB contract"""
|
|
16
19
|
|
|
17
|
-
def __init__(
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
partition_config: Optional[PartitionConfig] = None,
|
|
23
|
+
mongo_uri: str = "",
|
|
24
|
+
db_name: str = "",
|
|
25
|
+
):
|
|
18
26
|
super().__init__(partition_config)
|
|
19
|
-
|
|
20
|
-
self.
|
|
27
|
+
|
|
28
|
+
self.mongo_uri = mongo_uri or os.getenv("MONGODB_URI", "mongodb://localhost:27017")
|
|
29
|
+
|
|
30
|
+
self.db_name = db_name or os.getenv("MONGODB_DATABASE", "polydb")
|
|
31
|
+
|
|
21
32
|
self._client = None
|
|
22
|
-
self.
|
|
33
|
+
self._lock = threading.Lock()
|
|
34
|
+
|
|
23
35
|
self._initialize_client()
|
|
24
36
|
|
|
37
|
+
# -----------------------------------------------------
|
|
38
|
+
# Client init
|
|
39
|
+
# -----------------------------------------------------
|
|
40
|
+
|
|
25
41
|
def _initialize_client(self):
|
|
26
42
|
try:
|
|
27
43
|
from pymongo import MongoClient
|
|
28
44
|
|
|
29
|
-
with self.
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
with self._lock:
|
|
46
|
+
if self._client:
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
self._client = MongoClient(
|
|
50
|
+
self.mongo_uri,
|
|
51
|
+
maxPoolSize=int(os.getenv("MONGODB_MAX_POOL_SIZE", "10")),
|
|
52
|
+
minPoolSize=int(os.getenv("MONGODB_MIN_POOL_SIZE", "1")),
|
|
53
|
+
serverSelectionTimeoutMS=5000,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
self._client.server_info()
|
|
57
|
+
|
|
58
|
+
self.logger.info("MongoDB initialized")
|
|
59
|
+
|
|
39
60
|
except Exception as e:
|
|
40
|
-
raise ConnectionError(f"MongoDB init failed: {
|
|
61
|
+
raise ConnectionError(f"MongoDB init failed: {e}")
|
|
62
|
+
|
|
63
|
+
# -----------------------------------------------------
|
|
64
|
+
# Collection helper
|
|
65
|
+
# -----------------------------------------------------
|
|
41
66
|
|
|
42
67
|
def _get_collection(self, model: type):
|
|
43
68
|
if not self._client:
|
|
44
69
|
self._initialize_client()
|
|
45
70
|
|
|
46
|
-
meta = getattr(model, "__polydb__", {})
|
|
71
|
+
meta = getattr(model, "__polydb__", {}) or {}
|
|
72
|
+
|
|
47
73
|
collection_name = meta.get("collection") or meta.get("table") or model.__name__.lower()
|
|
74
|
+
|
|
48
75
|
return self._client[self.db_name][collection_name] # type: ignore
|
|
49
76
|
|
|
77
|
+
# -----------------------------------------------------
|
|
78
|
+
# PUT
|
|
79
|
+
# -----------------------------------------------------
|
|
50
80
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
51
81
|
def _put_raw(self, model: type, pk: str, rk: str, data: JsonDict) -> JsonDict:
|
|
52
82
|
try:
|
|
53
|
-
data_copy = dict(data)
|
|
54
|
-
data_copy["_pk"] = pk
|
|
55
|
-
data_copy["_rk"] = rk
|
|
56
|
-
|
|
57
83
|
collection = self._get_collection(model)
|
|
58
|
-
|
|
84
|
+
|
|
85
|
+
payload = dict(data or {})
|
|
86
|
+
payload["_pk"] = pk
|
|
87
|
+
payload["_rk"] = rk
|
|
88
|
+
payload["id"] = pk
|
|
89
|
+
|
|
90
|
+
collection.update_one(
|
|
59
91
|
{"_pk": pk, "_rk": rk},
|
|
60
|
-
{"$set":
|
|
92
|
+
{"$set": payload},
|
|
61
93
|
upsert=True,
|
|
62
94
|
)
|
|
63
95
|
|
|
64
|
-
return
|
|
96
|
+
# return full stored row (tests expect this)
|
|
97
|
+
result = dict(payload)
|
|
98
|
+
result.pop("_pk", None)
|
|
99
|
+
result.pop("_rk", None)
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
|
|
65
103
|
except Exception as e:
|
|
66
|
-
raise NoSQLError(f"MongoDB put failed: {
|
|
104
|
+
raise NoSQLError(f"MongoDB put failed: {e}")
|
|
105
|
+
|
|
106
|
+
# -----------------------------------------------------
|
|
107
|
+
# GET
|
|
108
|
+
# -----------------------------------------------------
|
|
67
109
|
|
|
68
110
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
69
111
|
def _get_raw(self, model: type, pk: str, rk: str) -> Optional[JsonDict]:
|
|
70
112
|
try:
|
|
71
113
|
collection = self._get_collection(model)
|
|
114
|
+
|
|
72
115
|
doc = collection.find_one({"_pk": pk, "_rk": rk})
|
|
73
116
|
|
|
74
|
-
if doc:
|
|
117
|
+
if not doc:
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
doc.pop("_id", None)
|
|
121
|
+
doc.setdefault("id", pk)
|
|
122
|
+
|
|
123
|
+
return doc
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
raise NoSQLError(f"MongoDB get failed: {e}")
|
|
127
|
+
|
|
128
|
+
# -----------------------------------------------------
|
|
129
|
+
# QUERY
|
|
130
|
+
# -----------------------------------------------------
|
|
131
|
+
def query_page(
|
|
132
|
+
self,
|
|
133
|
+
model: type,
|
|
134
|
+
query: Dict[str, Any],
|
|
135
|
+
page_size: int,
|
|
136
|
+
continuation_token: Optional[str] = None,
|
|
137
|
+
):
|
|
138
|
+
try:
|
|
139
|
+
collection = self._get_collection(model)
|
|
140
|
+
|
|
141
|
+
query = query or {}
|
|
142
|
+
|
|
143
|
+
if continuation_token:
|
|
144
|
+
query["_pk"] = {"$gt": continuation_token}
|
|
145
|
+
|
|
146
|
+
cursor = collection.find(query).sort("_pk", 1).limit(page_size)
|
|
147
|
+
|
|
148
|
+
rows = []
|
|
149
|
+
|
|
150
|
+
for doc in cursor:
|
|
75
151
|
doc.pop("_id", None)
|
|
76
|
-
|
|
152
|
+
doc.setdefault("id", doc.get("_pk"))
|
|
153
|
+
rows.append(doc)
|
|
154
|
+
|
|
155
|
+
if not rows:
|
|
156
|
+
return [], None
|
|
157
|
+
|
|
158
|
+
next_token = None
|
|
159
|
+
|
|
160
|
+
if len(rows) == page_size:
|
|
161
|
+
next_token = rows[-1]["id"]
|
|
162
|
+
|
|
163
|
+
return rows, next_token
|
|
77
164
|
|
|
78
|
-
return None
|
|
79
165
|
except Exception as e:
|
|
80
|
-
raise NoSQLError(f"MongoDB
|
|
166
|
+
raise NoSQLError(f"MongoDB query_page failed: {e}")
|
|
81
167
|
|
|
82
168
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
83
169
|
def _query_raw(
|
|
@@ -86,21 +172,36 @@ class MongoDBAdapter(NoSQLKVAdapter):
|
|
|
86
172
|
try:
|
|
87
173
|
collection = self._get_collection(model)
|
|
88
174
|
|
|
89
|
-
query = {}
|
|
90
|
-
|
|
175
|
+
query: Dict[str, Any] = {}
|
|
176
|
+
|
|
177
|
+
for k, v in (filters or {}).items():
|
|
178
|
+
|
|
179
|
+
if k == "id":
|
|
180
|
+
query["_pk"] = v
|
|
181
|
+
continue
|
|
182
|
+
|
|
91
183
|
if k.endswith("__gt"):
|
|
92
184
|
query[k[:-4]] = {"$gt": v}
|
|
185
|
+
|
|
93
186
|
elif k.endswith("__gte"):
|
|
94
187
|
query[k[:-5]] = {"$gte": v}
|
|
188
|
+
|
|
95
189
|
elif k.endswith("__lt"):
|
|
96
190
|
query[k[:-4]] = {"$lt": v}
|
|
191
|
+
|
|
97
192
|
elif k.endswith("__lte"):
|
|
98
193
|
query[k[:-5]] = {"$lte": v}
|
|
194
|
+
|
|
99
195
|
elif k.endswith("__in"):
|
|
100
196
|
query[k[:-4]] = {"$in": v}
|
|
197
|
+
|
|
101
198
|
elif k.endswith("__contains"):
|
|
102
199
|
safe_pattern = re.escape(str(v))
|
|
103
|
-
query[k[:-10]] = {
|
|
200
|
+
query[k[:-10]] = {
|
|
201
|
+
"$regex": safe_pattern,
|
|
202
|
+
"$options": "i",
|
|
203
|
+
}
|
|
204
|
+
|
|
104
205
|
else:
|
|
105
206
|
query[k] = v
|
|
106
207
|
|
|
@@ -109,28 +210,47 @@ class MongoDBAdapter(NoSQLKVAdapter):
|
|
|
109
210
|
if limit:
|
|
110
211
|
cursor = cursor.limit(limit)
|
|
111
212
|
|
|
112
|
-
results = []
|
|
213
|
+
results: List[JsonDict] = []
|
|
214
|
+
|
|
113
215
|
for doc in cursor:
|
|
114
216
|
doc.pop("_id", None)
|
|
217
|
+
doc.setdefault("id", doc.get("_pk"))
|
|
115
218
|
results.append(doc)
|
|
116
219
|
|
|
117
220
|
return results
|
|
221
|
+
|
|
118
222
|
except Exception as e:
|
|
119
|
-
raise NoSQLError(f"MongoDB query failed: {
|
|
223
|
+
raise NoSQLError(f"MongoDB query failed: {e}")
|
|
224
|
+
|
|
225
|
+
# -----------------------------------------------------
|
|
226
|
+
# DELETE
|
|
227
|
+
# -----------------------------------------------------
|
|
120
228
|
|
|
121
229
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
122
230
|
def _delete_raw(self, model: type, pk: str, rk: str, etag: Optional[str]) -> JsonDict:
|
|
123
231
|
try:
|
|
124
232
|
collection = self._get_collection(model)
|
|
233
|
+
|
|
125
234
|
result = collection.delete_one({"_pk": pk, "_rk": rk})
|
|
126
235
|
|
|
127
|
-
|
|
236
|
+
if result.deleted_count == 0:
|
|
237
|
+
raise DatabaseError(f"Item {pk}/{rk} does not exist")
|
|
238
|
+
|
|
239
|
+
return {"id": pk}
|
|
240
|
+
|
|
241
|
+
except DatabaseError:
|
|
242
|
+
raise
|
|
243
|
+
|
|
128
244
|
except Exception as e:
|
|
129
|
-
raise NoSQLError(f"MongoDB delete failed: {
|
|
245
|
+
raise NoSQLError(f"MongoDB delete failed: {e}")
|
|
246
|
+
|
|
247
|
+
# -----------------------------------------------------
|
|
248
|
+
# Close connection
|
|
249
|
+
# -----------------------------------------------------
|
|
130
250
|
|
|
131
251
|
def __del__(self):
|
|
132
252
|
if self._client:
|
|
133
253
|
try:
|
|
134
254
|
self._client.close()
|
|
135
|
-
except:
|
|
255
|
+
except Exception:
|
|
136
256
|
pass
|