Skip to content

Commit 972c5d9

Browse files
committed
Ensure last_evaluated_key follows the same tuple-convention for primary keys.
1 parent 4f79932 commit 972c5d9

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

pydanticrud/backends/dynamodb.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,15 @@ def index_definition(index_name, keys, gsi=False):
120120
class DynamoIterableResult(IterableResult):
121121
def __init__(self, cls, result, serialized_items):
122122
super(DynamoIterableResult, self).__init__(cls, serialized_items, result.get("Count"))
123-
self.last_evaluated_key = result.get("LastEvaluatedKey")
123+
124+
self.last_evaluated_key = None
125+
lsk = result.get("LastEvaluatedKey")
126+
if lsk:
127+
_key = [lsk[cls.Config.hash_key]]
128+
if cls.Config.range_key:
129+
_key.append(lsk[cls.Config.range_key])
130+
self.last_evaluated_key = tuple(_key)
131+
124132
self.scanned_count = result["ScannedCount"]
125133

126134

@@ -288,7 +296,7 @@ def query(self,
288296
filter_expr: Optional[Rule] = None,
289297
limit: Optional[int] = None,
290298
exclusive_start_key: Optional[Dict[str, Any]] = None,
291-
order: str = 'asc'
299+
order: str = 'asc',
292300
):
293301
table = self.get_table()
294302
f_expr, _ = rule_to_boto_expression(filter_expr) if filter_expr else (None, set())
@@ -298,7 +306,7 @@ def query(self,
298306
if limit:
299307
params["Limit"] = limit
300308
if exclusive_start_key:
301-
params["ExclusiveStartKey"] = exclusive_start_key
309+
params["ExclusiveStartKey"] = self._key_param_to_dict(exclusive_start_key)
302310
if f_expr:
303311
params["FilterExpression"] = f_expr
304312

tests/test_dynamodb.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,9 @@ def simple_query_data(simple_table):
211211

212212
@pytest.fixture(scope="module")
213213
def complex_query_data(complex_table):
214-
record_count = 20
214+
record_count = 500
215215
presets = [dict()] * record_count
216-
accounts = [str(uuid4()) for i in range(record_count//4)]
216+
accounts = [str(uuid4()) for i in range(4)]
217217

218218
data = [
219219
complex_model_data_generator(account=accounts[i % 4], **p)
@@ -386,6 +386,31 @@ def test_ordered_query_with_hash_key_complex(dynamo, complex_query_data, order):
386386
assert res_data == check_data
387387

388388

389+
@pytest.mark.parametrize('order', ('asc', 'desc'))
390+
def test_pagination_query_with_hash_key_complex(dynamo, complex_query_data, order):
391+
page_size = 2
392+
middle_record = complex_query_data[(len(complex_query_data)//2)]
393+
query_rule = Rule(f"account == '{middle_record['account']}' and sort_date_key >= '{middle_record['sort_date_key']}'")
394+
res = ComplexKeyModel.query(query_rule, order=order, limit=page_size)
395+
res_data = [(m.account, m.sort_date_key) for m in res]
396+
check_data = sorted([
397+
(m["account"], m["sort_date_key"])
398+
for m in complex_query_data
399+
if m["account"] == middle_record['account'] and m["sort_date_key"] >= middle_record['sort_date_key']
400+
], reverse=order == 'desc')[:page_size]
401+
assert res_data == check_data
402+
assert res.last_evaluated_key == check_data[-1]
403+
404+
res = ComplexKeyModel.query(query_rule, order=order, limit=page_size, exclusive_start_key=res.last_evaluated_key)
405+
res_data = [(m.account, m.sort_date_key) for m in res]
406+
check_data = sorted([
407+
(m["account"], m["sort_date_key"])
408+
for m in complex_query_data
409+
if m["account"] == middle_record['account'] and m["sort_date_key"] >= middle_record['sort_date_key']
410+
], reverse=order == 'desc')[page_size:page_size*2]
411+
assert res_data == check_data
412+
413+
389414
def test_query_errors_with_nonprimary_key_complex(dynamo, complex_query_data):
390415
data_by_expires = complex_query_data[:]
391416
data_by_expires.sort(key=lambda d: d["expires"])

0 commit comments

Comments
 (0)