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.
Files changed (38) hide show
  1. altcodepro_polydb_python-2.2.4.dist-info/METADATA +489 -0
  2. altcodepro_polydb_python-2.2.4.dist-info/RECORD +57 -0
  3. {altcodepro_polydb_python-2.2.2.dist-info → altcodepro_polydb_python-2.2.4.dist-info}/WHEEL +1 -1
  4. polydb/__init__.py +2 -2
  5. polydb/adapters/AzureBlobStorageAdapter.py +146 -41
  6. polydb/adapters/AzureFileStorageAdapter.py +148 -43
  7. polydb/adapters/AzureQueueAdapter.py +96 -34
  8. polydb/adapters/AzureTableStorageAdapter.py +462 -119
  9. polydb/adapters/BlockchainBlobAdapter.py +111 -0
  10. polydb/adapters/BlockchainKVAdapter.py +152 -0
  11. polydb/adapters/BlockchainQueueAdapter.py +116 -0
  12. polydb/adapters/DynamoDBAdapter.py +463 -176
  13. polydb/adapters/FirestoreAdapter.py +320 -148
  14. polydb/adapters/GCPPubSubAdapter.py +217 -0
  15. polydb/adapters/GCPStorageAdapter.py +184 -39
  16. polydb/adapters/MongoDBAdapter.py +159 -39
  17. polydb/adapters/PostgreSQLAdapter.py +285 -83
  18. polydb/adapters/S3Adapter.py +172 -35
  19. polydb/adapters/S3CompatibleAdapter.py +62 -8
  20. polydb/adapters/SQSAdapter.py +121 -44
  21. polydb/adapters/VercelBlobAdapter.py +196 -0
  22. polydb/adapters/VercelKVAdapter.py +275 -283
  23. polydb/adapters/VercelQueueAdapter.py +61 -0
  24. polydb/audit/AuditStorage.py +1 -1
  25. polydb/base/NoSQLKVAdapter.py +113 -101
  26. polydb/base/ObjectStorageAdapter.py +42 -6
  27. polydb/base/QueueAdapter.py +2 -2
  28. polydb/base/SharedFilesAdapter.py +2 -2
  29. polydb/cloudDatabaseFactory.py +200 -0
  30. polydb/databaseFactory.py +434 -101
  31. polydb/models.py +63 -1
  32. polydb/query.py +111 -42
  33. altcodepro_polydb_python-2.2.2.dist-info/METADATA +0 -379
  34. altcodepro_polydb_python-2.2.2.dist-info/RECORD +0 -52
  35. polydb/adapters/PubSubAdapter.py +0 -85
  36. polydb/factory.py +0 -107
  37. {altcodepro_polydb_python-2.2.2.dist-info → altcodepro_polydb_python-2.2.4.dist-info}/licenses/LICENSE +0 -0
  38. {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 ..errors import NoSQLError, ConnectionError
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 overflow and LINQ"""
18
+ """MongoDB adapter compatible with PolyDB contract"""
16
19
 
17
- def __init__(self, partition_config: Optional[PartitionConfig] = None):
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
- self.mongo_uri = os.getenv("MONGODB_URI", "mongodb://localhost:27017")
20
- self.db_name = os.getenv("MONGODB_DATABASE", "default")
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._client_lock = threading.Lock()
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._client_lock:
30
- if not self._client:
31
- self._client = MongoClient(
32
- self.mongo_uri,
33
- maxPoolSize=int(os.getenv("MONGODB_MAX_POOL_SIZE", "10")),
34
- minPoolSize=int(os.getenv("MONGODB_MIN_POOL_SIZE", "1")),
35
- serverSelectionTimeoutMS=5000,
36
- )
37
- self._client.server_info()
38
- self.logger.info("MongoDB initialized")
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: {str(e)}")
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
- result = collection.update_one(
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": data_copy},
92
+ {"$set": payload},
61
93
  upsert=True,
62
94
  )
63
95
 
64
- return {"_pk": pk, "_rk": rk}
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: {str(e)}")
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
- return doc
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 get failed: {str(e)}")
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
- for k, v in filters.items():
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]] = {"$regex": safe_pattern, "$options": "i"} # case-insensitive
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: {str(e)}")
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
- return {"deleted": result.deleted_count > 0, "_pk": pk, "_rk": rk}
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: {str(e)}")
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