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
polydb/models.py
CHANGED
|
@@ -10,6 +10,7 @@ from enum import Enum
|
|
|
10
10
|
|
|
11
11
|
class CloudProvider(Enum):
|
|
12
12
|
"""Supported cloud providers"""
|
|
13
|
+
|
|
13
14
|
AZURE = "azure"
|
|
14
15
|
AWS = "aws"
|
|
15
16
|
GCP = "gcp"
|
|
@@ -17,11 +18,13 @@ class CloudProvider(Enum):
|
|
|
17
18
|
MONGODB = "mongodb"
|
|
18
19
|
S3_COMPATIBLE = "s3_compatible"
|
|
19
20
|
POSTGRESQL = "postgresql"
|
|
21
|
+
BLOCKCHAIN = "blockchain"
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
@dataclass
|
|
23
25
|
class PartitionConfig:
|
|
24
26
|
"""Configuration for partition and row keys"""
|
|
27
|
+
|
|
25
28
|
partition_key_template: str = "default_{id}"
|
|
26
29
|
row_key_template: Optional[str] = None
|
|
27
30
|
composite_keys: Optional[List[str]] = None
|
|
@@ -31,9 +34,68 @@ class PartitionConfig:
|
|
|
31
34
|
@dataclass
|
|
32
35
|
class QueryOptions:
|
|
33
36
|
"""LINQ-style query options"""
|
|
37
|
+
|
|
34
38
|
filter_func: Optional[Callable] = None
|
|
35
39
|
order_by: Optional[str] = None
|
|
36
40
|
skip: int = 0
|
|
37
41
|
take: Optional[int] = None
|
|
38
42
|
select_fields: Optional[List[str]] = None
|
|
39
|
-
count_only: bool = False
|
|
43
|
+
count_only: bool = False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ============================================================
|
|
47
|
+
# CONFIG CLASSES (TYPED)
|
|
48
|
+
# ============================================================
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class StorageConfig:
|
|
52
|
+
def __init__(self, provider: CloudProvider, name: str = "default"):
|
|
53
|
+
self.provider = provider
|
|
54
|
+
self.name = name
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AzureStorageConfig(StorageConfig):
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
name: str = "default",
|
|
61
|
+
connection_string: Optional[str] = None,
|
|
62
|
+
container: Optional[str] = None,
|
|
63
|
+
):
|
|
64
|
+
super().__init__(CloudProvider.AZURE, name)
|
|
65
|
+
self.connection_string = connection_string
|
|
66
|
+
self.container = container
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class S3StorageConfig(StorageConfig):
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
name: str = "default",
|
|
73
|
+
endpoint: Optional[str] = None,
|
|
74
|
+
access_key: Optional[str] = None,
|
|
75
|
+
secret_key: Optional[str] = None,
|
|
76
|
+
bucket: Optional[str] = None,
|
|
77
|
+
):
|
|
78
|
+
super().__init__(CloudProvider.S3_COMPATIBLE, name)
|
|
79
|
+
self.endpoint = endpoint
|
|
80
|
+
self.access_key = access_key
|
|
81
|
+
self.secret_key = secret_key
|
|
82
|
+
self.bucket = bucket
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class GCPStorageConfig(StorageConfig):
|
|
86
|
+
def __init__(self, name: str = "default", bucket: Optional[str] = None):
|
|
87
|
+
super().__init__(CloudProvider.GCP, name)
|
|
88
|
+
self.bucket = bucket
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class VercelStorageConfig(StorageConfig):
|
|
92
|
+
def __init__(self, name: str = "default", token: Optional[str] = None, timeout: int = 10):
|
|
93
|
+
super().__init__(CloudProvider.VERCEL, name)
|
|
94
|
+
self.token = token
|
|
95
|
+
self.timeout = timeout
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class BlockchainStorageConfig(StorageConfig):
|
|
99
|
+
def __init__(self, name: str = "default", ipfs_url: Optional[str] = None):
|
|
100
|
+
super().__init__(CloudProvider.BLOCKCHAIN, name)
|
|
101
|
+
self.ipfs_url = ipfs_url
|
polydb/query.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# src/polydb/query.py
|
|
2
|
-
from __future__ import annotations
|
|
3
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from typing import Any,
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
|
6
6
|
from enum import Enum
|
|
7
7
|
|
|
8
8
|
|
|
@@ -29,122 +29,191 @@ class QueryFilter:
|
|
|
29
29
|
|
|
30
30
|
@dataclass
|
|
31
31
|
class QueryBuilder:
|
|
32
|
-
"""LINQ-style query builder supporting
|
|
33
|
-
|
|
32
|
+
"""LINQ-style query builder supporting SQL and NoSQL"""
|
|
33
|
+
|
|
34
34
|
filters: List[QueryFilter] = field(default_factory=list)
|
|
35
|
-
order_by_fields: List[tuple[str, bool]] = field(default_factory=list)
|
|
35
|
+
order_by_fields: List[tuple[str, bool]] = field(default_factory=list)
|
|
36
|
+
|
|
36
37
|
skip_count: int = 0
|
|
37
38
|
take_count: Optional[int] = None
|
|
38
|
-
|
|
39
|
+
|
|
40
|
+
selected_fields: Optional[List[str]] = None
|
|
39
41
|
group_by_fields: Optional[List[str]] = None
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
distinct_flag: bool = False
|
|
41
44
|
count_only: bool = False
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
|
|
46
|
+
# ------------------------------------------------
|
|
47
|
+
# FILTERS
|
|
48
|
+
# ------------------------------------------------
|
|
49
|
+
|
|
50
|
+
def where(
|
|
51
|
+
self, field: str, operator: Union["Operator", str, None], value: Any
|
|
52
|
+
) -> "QueryBuilder":
|
|
53
|
+
# Allow legacy callers that pass "" meaning equality
|
|
54
|
+
if operator in ("", None):
|
|
55
|
+
op = Operator.EQ
|
|
56
|
+
elif isinstance(operator, Operator):
|
|
57
|
+
op = operator
|
|
58
|
+
else:
|
|
59
|
+
op = Operator(operator)
|
|
60
|
+
|
|
61
|
+
self.filters.append(QueryFilter(field=field, operator=op, value=value))
|
|
48
62
|
return self
|
|
49
|
-
|
|
63
|
+
|
|
64
|
+
# ------------------------------------------------
|
|
65
|
+
# ORDER
|
|
66
|
+
# ------------------------------------------------
|
|
67
|
+
|
|
50
68
|
def order_by(self, field: str, descending: bool = False) -> QueryBuilder:
|
|
51
|
-
"""Add ordering"""
|
|
52
69
|
self.order_by_fields.append((field, descending))
|
|
53
70
|
return self
|
|
54
|
-
|
|
71
|
+
|
|
72
|
+
# ------------------------------------------------
|
|
73
|
+
# PAGINATION
|
|
74
|
+
# ------------------------------------------------
|
|
75
|
+
|
|
55
76
|
def skip(self, count: int) -> QueryBuilder:
|
|
56
|
-
"""Skip records"""
|
|
57
77
|
self.skip_count = count
|
|
58
78
|
return self
|
|
59
|
-
|
|
79
|
+
|
|
60
80
|
def take(self, count: int) -> QueryBuilder:
|
|
61
|
-
"""Take records"""
|
|
62
81
|
self.take_count = count
|
|
63
82
|
return self
|
|
64
|
-
|
|
83
|
+
|
|
84
|
+
# ------------------------------------------------
|
|
85
|
+
# SELECT
|
|
86
|
+
# ------------------------------------------------
|
|
87
|
+
|
|
65
88
|
def select(self, *fields: str) -> QueryBuilder:
|
|
66
|
-
|
|
67
|
-
self
|
|
89
|
+
self.selected_fields = list(fields)
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def select_fields(self, fields: List[str]) -> QueryBuilder:
|
|
93
|
+
self.selected_fields = fields
|
|
68
94
|
return self
|
|
69
|
-
|
|
95
|
+
|
|
96
|
+
# ------------------------------------------------
|
|
97
|
+
# GROUP
|
|
98
|
+
# ------------------------------------------------
|
|
99
|
+
|
|
70
100
|
def group_by(self, *fields: str) -> QueryBuilder:
|
|
71
|
-
"""Group by fields"""
|
|
72
101
|
self.group_by_fields = list(fields)
|
|
73
102
|
return self
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
103
|
+
|
|
104
|
+
# ------------------------------------------------
|
|
105
|
+
# DISTINCT
|
|
106
|
+
# ------------------------------------------------
|
|
107
|
+
|
|
108
|
+
def distinct(self) -> QueryBuilder:
|
|
109
|
+
self.distinct_flag = True
|
|
78
110
|
return self
|
|
79
|
-
|
|
111
|
+
|
|
112
|
+
# ------------------------------------------------
|
|
113
|
+
# COUNT
|
|
114
|
+
# ------------------------------------------------
|
|
115
|
+
|
|
80
116
|
def count(self) -> QueryBuilder:
|
|
81
|
-
"""Return count only"""
|
|
82
117
|
self.count_only = True
|
|
83
118
|
return self
|
|
84
|
-
|
|
119
|
+
|
|
120
|
+
# ------------------------------------------------
|
|
121
|
+
# SQL WHERE
|
|
122
|
+
# ------------------------------------------------
|
|
123
|
+
|
|
85
124
|
def to_sql_where(self) -> tuple[str, List[Any]]:
|
|
86
|
-
|
|
125
|
+
|
|
87
126
|
if not self.filters:
|
|
88
127
|
return "", []
|
|
89
|
-
|
|
128
|
+
|
|
90
129
|
clauses = []
|
|
91
130
|
params = []
|
|
92
|
-
|
|
131
|
+
|
|
93
132
|
for f in self.filters:
|
|
133
|
+
|
|
94
134
|
if f.operator == Operator.EQ:
|
|
95
135
|
clauses.append(f"{f.field} = %s")
|
|
96
136
|
params.append(f.value)
|
|
137
|
+
|
|
97
138
|
elif f.operator == Operator.NE:
|
|
98
139
|
clauses.append(f"{f.field} != %s")
|
|
99
140
|
params.append(f.value)
|
|
141
|
+
|
|
100
142
|
elif f.operator == Operator.GT:
|
|
101
143
|
clauses.append(f"{f.field} > %s")
|
|
102
144
|
params.append(f.value)
|
|
145
|
+
|
|
103
146
|
elif f.operator == Operator.GTE:
|
|
104
147
|
clauses.append(f"{f.field} >= %s")
|
|
105
148
|
params.append(f.value)
|
|
149
|
+
|
|
106
150
|
elif f.operator == Operator.LT:
|
|
107
151
|
clauses.append(f"{f.field} < %s")
|
|
108
152
|
params.append(f.value)
|
|
153
|
+
|
|
109
154
|
elif f.operator == Operator.LTE:
|
|
110
155
|
clauses.append(f"{f.field} <= %s")
|
|
111
156
|
params.append(f.value)
|
|
157
|
+
|
|
112
158
|
elif f.operator == Operator.IN:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
|
|
160
|
+
if isinstance(f.value, (list, tuple)):
|
|
161
|
+
placeholders = ",".join(["%s"] * len(f.value))
|
|
162
|
+
clauses.append(f"{f.field} IN ({placeholders})")
|
|
163
|
+
params.extend(f.value)
|
|
164
|
+
|
|
165
|
+
else:
|
|
166
|
+
clauses.append(f"{f.field} LIKE %s")
|
|
167
|
+
params.append(f.value)
|
|
168
|
+
|
|
116
169
|
elif f.operator == Operator.NOT_IN:
|
|
117
|
-
placeholders =
|
|
170
|
+
placeholders = ",".join(["%s"] * len(f.value))
|
|
118
171
|
clauses.append(f"{f.field} NOT IN ({placeholders})")
|
|
119
172
|
params.extend(f.value)
|
|
173
|
+
|
|
120
174
|
elif f.operator == Operator.CONTAINS:
|
|
121
175
|
clauses.append(f"{f.field} LIKE %s")
|
|
122
176
|
params.append(f"%{f.value}%")
|
|
177
|
+
|
|
123
178
|
elif f.operator == Operator.STARTS_WITH:
|
|
124
179
|
clauses.append(f"{f.field} LIKE %s")
|
|
125
180
|
params.append(f"{f.value}%")
|
|
181
|
+
|
|
126
182
|
elif f.operator == Operator.ENDS_WITH:
|
|
127
183
|
clauses.append(f"{f.field} LIKE %s")
|
|
128
184
|
params.append(f"%{f.value}")
|
|
129
|
-
|
|
185
|
+
|
|
130
186
|
return " AND ".join(clauses), params
|
|
131
|
-
|
|
187
|
+
|
|
188
|
+
# ------------------------------------------------
|
|
189
|
+
# NOSQL FILTER
|
|
190
|
+
# ------------------------------------------------
|
|
191
|
+
|
|
132
192
|
def to_nosql_filter(self) -> Dict[str, Any]:
|
|
133
|
-
|
|
193
|
+
|
|
134
194
|
result = {}
|
|
195
|
+
|
|
135
196
|
for f in self.filters:
|
|
197
|
+
|
|
136
198
|
if f.operator == Operator.EQ:
|
|
137
199
|
result[f.field] = f.value
|
|
200
|
+
|
|
138
201
|
elif f.operator == Operator.IN:
|
|
139
202
|
result[f"{f.field}__in"] = f.value
|
|
203
|
+
|
|
140
204
|
elif f.operator == Operator.GT:
|
|
141
205
|
result[f"{f.field}__gt"] = f.value
|
|
206
|
+
|
|
142
207
|
elif f.operator == Operator.GTE:
|
|
143
208
|
result[f"{f.field}__gte"] = f.value
|
|
209
|
+
|
|
144
210
|
elif f.operator == Operator.LT:
|
|
145
211
|
result[f"{f.field}__lt"] = f.value
|
|
212
|
+
|
|
146
213
|
elif f.operator == Operator.LTE:
|
|
147
214
|
result[f"{f.field}__lte"] = f.value
|
|
215
|
+
|
|
148
216
|
elif f.operator == Operator.CONTAINS:
|
|
149
217
|
result[f"{f.field}__contains"] = f.value
|
|
150
|
-
|
|
218
|
+
|
|
219
|
+
return result
|