3
3
import uuid
4
4
import os
5
5
import pytz
6
+ import random
7
+ import string
6
8
from datetime import datetime
7
9
from dotenv import load_dotenv
8
10
from decimal import Decimal
@@ -29,6 +31,7 @@ def __init__(
29
31
self ._secondary_index = ''
30
32
self ._attributes_to_get = ''
31
33
self ._filter_expression = ''
34
+ self ._query_filter_expression = ''
32
35
self ._filter_params = {}
33
36
self ._filter_params_name = {}
34
37
self ._last_evaluated_key = {}
@@ -88,14 +91,35 @@ def _load_dynamo(self):
88
91
def query_by (
89
92
self ,
90
93
key : str ,
91
- comparator : Literal ["=" , "<> " , "<" , "<= " , ">" , ">= " ],
94
+ comparator : Literal ["=" , "<" , "<= " , "> " , ">= " , "BETWEEN" , "begins_with " ],
92
95
key_value ,
93
- secondary_index = None
94
- ):
96
+ secondary_index = None
97
+ ):
95
98
self ._is_query_operation = True
96
99
self ._secondary_index = secondary_index
97
100
return self .find_by (key , comparator , key_value )
98
101
102
+ """
103
+ Args:
104
+ key (str): The table key to filter on.
105
+ key_value: The value to use on the filter.
106
+
107
+ Returns:
108
+ self: The DynoLayer.
109
+ or_query_by('likes', '<', 20)
110
+ """
111
+
112
+ def or_query_by (
113
+ self ,
114
+ key : str ,
115
+ comparator : Literal ["=" , "<" , "<=" , ">" , ">=" , "BETWEEN" , "begins_with" ],
116
+ key_value ,
117
+ secondary_index = None
118
+ ):
119
+ self ._is_query_operation = True
120
+ self ._secondary_index = secondary_index
121
+ return self .or_find_by (key , comparator , key_value )
122
+
99
123
"""
100
124
Args:
101
125
filter_expression (str): The filter expression string.
@@ -108,9 +132,13 @@ def query_by(
108
132
def find (
109
133
self ,
110
134
filter_expression : str = '' ,
111
- filter_params : dict = {} ,
112
- filter_params_name : dict = {}
135
+ filter_params = None ,
136
+ filter_params_name = None
113
137
):
138
+ if filter_params_name is None :
139
+ filter_params_name = {}
140
+ if filter_params is None :
141
+ filter_params = {}
114
142
self ._filter_expression = filter_expression
115
143
self ._filter_params = filter_params
116
144
self ._filter_params_name = filter_params_name
@@ -142,20 +170,141 @@ def query(
142
170
def find_by (
143
171
self ,
144
172
attribute : str ,
145
- comparator : Literal ["=" , "<>" , "<" , "<=" , ">" , ">=" ],
173
+ comparator : Literal ["=" , "<>" , "<" , "<=" , ">" , ">=" , "BETWEEN" , "begins_with" ],
146
174
attribute_value
147
- ):
148
- self ._is_find_by = True
175
+ ):
176
+ use_and_operator = ''
177
+ old_att = ''
178
+ if self ._is_find_by :
179
+ use_and_operator = ' AND '
180
+ if str (self ._filter_params .get (f':{ attribute } ' , '' )):
181
+ letters = string .ascii_lowercase
182
+ random_str = ''
183
+ for i in range (4 ):
184
+ random_str += random .choice (letters )
185
+ old_att = attribute
186
+ attribute = random_str + attribute
187
+
188
+ if not self ._is_find_by :
189
+ self ._is_find_by = True
190
+
191
+ if isinstance (attribute_value , dict ) or (isinstance (attribute_value , list ) and comparator != 'BETWEEN' ):
192
+ attribute_value = json .dumps (attribute_value )
193
+
194
+ filter_param = {
195
+ f':{ attribute } ' : attribute_value
196
+ }
197
+ if comparator == 'BETWEEN' :
198
+ self ._filter_expression += f'{ use_and_operator } (#{ attribute } { comparator } :{ attribute [0 ]} AND :{ attribute [1 ]} )'
199
+ filter_param = {
200
+ f':{ attribute [0 ]} ' : attribute_value [0 ],
201
+ f':{ attribute [1 ]} ' : attribute_value [1 ]
202
+ }
203
+ elif comparator == 'begins_with' :
204
+ self ._filter_expression += f'{ use_and_operator } ({ comparator } (#{ attribute } ,:{ attribute } ))'
205
+ else :
206
+ self ._filter_expression += f'{ use_and_operator } (#{ attribute } { comparator } :{ attribute } )'
207
+ self ._filter_params .update (filter_param )
208
+ self ._filter_params_name .update ({
209
+ f'#{ attribute } ' : old_att if old_att else attribute
210
+ })
211
+
212
+ return self
213
+
214
+ """
215
+ Args:
216
+ attribute (str): The table attribute to filter on.
217
+ attribute_value: The value to use on the filter.
218
+
219
+ Returns:
220
+ self: The DynoLayer.
221
+ """
222
+
223
+ def or_find_by (
224
+ self ,
225
+ attribute : str ,
226
+ comparator : Literal ["=" , "<" , "<=" , ">" , ">=" , "BETWEEN" , "begins_with" ],
227
+ attribute_value
228
+ ):
229
+ use_or_operator = ' OR '
230
+ old_att = ''
231
+ if self ._is_find_by :
232
+ if str (self ._filter_params .get (f':{ attribute } ' , '' )):
233
+ letters = string .ascii_lowercase
234
+ random_str = ''
235
+ for i in range (4 ):
236
+ random_str += random .choice (letters )
237
+ old_att = attribute
238
+ attribute = random_str + attribute
239
+
240
+ if not self ._is_find_by :
241
+ self ._is_find_by = True
242
+
243
+ if isinstance (attribute_value , dict ) or (isinstance (attribute_value , list ) and comparator != 'BETWEEN' ):
244
+ attribute_value = json .dumps (attribute_value )
245
+
246
+ filter_param = {
247
+ f':{ attribute } ' : attribute_value
248
+ }
249
+ if comparator == 'BETWEEN' :
250
+ self ._filter_expression += f'{ use_or_operator } (#{ attribute } { comparator } :{ attribute [0 ]} AND :{ attribute [1 ]} )'
251
+ filter_param = {
252
+ f':{ attribute [0 ]} ' : attribute_value [0 ],
253
+ f':{ attribute [1 ]} ' : attribute_value [1 ]
254
+ }
255
+ elif comparator == 'begins_with' :
256
+ self ._filter_expression += f'{ use_or_operator } ({ comparator } (#{ attribute } ,:{ attribute } ))'
257
+ else :
258
+ self ._filter_expression += f'{ use_or_operator } (#{ attribute } { comparator } :{ attribute } )'
259
+ self ._filter_params .update (filter_param )
260
+ self ._filter_params_name .update ({
261
+ f'#{ attribute } ' : old_att if old_att else attribute
262
+ })
263
+
264
+ return self
265
+
266
+ """
267
+ This function create a FilterExpression for a query operation
268
+ Args:
269
+ attributes (str): The specific attributes to return from table.
270
+
271
+ Returns:
272
+ self: The DynoLayer.
273
+ """
274
+
275
+ def filter (
276
+ self ,
277
+ attribute : str ,
278
+ comparator : Literal ["=" , "<>" , "<" , "<=" , ">" , ">=" , "BETWEEN" , "begins_with" ],
279
+ attribute_value ,
280
+ logical_operator : Literal ['AND' , 'OR' , 'NOT' ] = ''
281
+ ):
282
+ old_att = ''
283
+ if self ._is_find_by :
284
+ if str (self ._filter_params .get (f':{ attribute } ' , '' )):
285
+ letters = string .ascii_lowercase
286
+ random_str = ''
287
+ for i in range (4 ):
288
+ random_str += random .choice (letters )
289
+ old_att = attribute
290
+ attribute = random_str + attribute
149
291
150
292
if isinstance (attribute_value , dict ) or isinstance (attribute_value , list ):
151
293
attribute_value = json .dumps (attribute_value )
152
294
153
- self ._filter_expression += f'#{ attribute } { comparator } :{ attribute } '
295
+ if logical_operator :
296
+ logical_operator = f' { logical_operator } '
297
+ if comparator == 'BETWEEN' :
298
+ self ._query_filter_expression += f'{ logical_operator } (#{ attribute } { comparator } :{ attribute [0 ]} AND :{ attribute [1 ]} )'
299
+ elif comparator == 'begins_with' :
300
+ self ._query_filter_expression += f'{ logical_operator } ({ comparator } (#{ attribute } ,:{ attribute } ))'
301
+ else :
302
+ self ._query_filter_expression += f'{ logical_operator } (#{ attribute } { comparator } :{ attribute } )'
154
303
self ._filter_params .update ({
155
304
f':{ attribute } ' : attribute_value
156
305
})
157
306
self ._filter_params_name .update ({
158
- f'#{ attribute } ' : attribute
307
+ f'#{ attribute } ' : old_att if old_att else attribute
159
308
})
160
309
161
310
return self
@@ -257,6 +406,10 @@ def _fetch(self, paginate_through_results: bool = False, just_count=False, objec
257
406
if self ._filter_expression :
258
407
filter_key = 'KeyConditionExpression' if self ._is_query_operation else 'FilterExpression'
259
408
scan_params .update ({filter_key : self ._filter_expression })
409
+ if self ._query_filter_expression :
410
+ if not scan_params .get ('FilterExpression' , None ):
411
+ scan_params ['FilterExpression' ] = ''
412
+ scan_params ['FilterExpression' ] += self ._query_filter_expression
260
413
scan_params .update ({'ExpressionAttributeValues' : self ._filter_params })
261
414
scan_params .update ({'ExpressionAttributeNames' : self ._filter_params_name })
262
415
@@ -267,6 +420,9 @@ def _fetch(self, paginate_through_results: bool = False, just_count=False, objec
267
420
scan_params .update ({'IndexName' : self ._secondary_index })
268
421
269
422
self ._is_find_by = False # return to default value
423
+ self ._filter_expression = '' # return to default value
424
+ self ._filter_params = {} # return to default value
425
+ self ._filter_params_name = {} # return to default value
270
426
if self ._is_query_operation :
271
427
return self ._order_response (self ._fetch_query (scan_params , paginate_through_results ), object = object )
272
428
@@ -352,7 +508,7 @@ def save(self) -> bool:
352
508
self ._table .put_item (
353
509
Item = data
354
510
)
355
- self .id = data [self ._partition_key ]
511
+ self ._data [ 'id' ] = data [self ._partition_key ]
356
512
return True
357
513
except Exception as e :
358
514
self ._error = str (e )
@@ -494,7 +650,7 @@ def data(self):
494
650
"""
495
651
496
652
def _order_response (self , response , object = False ):
497
- if len (response ) == 0 :
653
+ if not response or len (response ) == 0 :
498
654
return response
499
655
500
656
if self ._order_by :
@@ -516,4 +672,3 @@ def _order_response(self, response, object=False):
516
672
return transformed_response
517
673
518
674
return response
519
-
0 commit comments