Skip to content

Commit 31e44f4

Browse files
authored
Split item and page iteration and track total scanned count. (pynamodb#395)
1 parent 5bf95bd commit 31e44f4

File tree

3 files changed

+130
-70
lines changed

3 files changed

+130
-70
lines changed

pynamodb/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
UTC = 'UTC'
6363
KEY = 'Key'
6464

65+
# Response Parameters
66+
SCANNED_COUNT = 'ScannedCount'
67+
6568
# Expression Parameters
6669
CONDITION_EXPRESSION = 'ConditionExpression'
6770
EXPRESSION_ATTRIBUTE_NAMES = 'ExpressionAttributeNames'

pynamodb/pagination.py

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,75 @@
1-
from pynamodb.constants import CAMEL_COUNT, ITEMS, LAST_EVALUATED_KEY
1+
from pynamodb.constants import CAMEL_COUNT, ITEMS, LAST_EVALUATED_KEY, SCANNED_COUNT
22

33

4-
class ResultIterator(object):
4+
class PageIterator(object):
55
"""
6-
ResultIterator handles Query and Scan result pagination.
6+
PageIterator handles Query and Scan result pagination.
77
88
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.Pagination
99
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.Pagination
1010
"""
11-
def __init__(self, operation, args, kwargs, map_fn=None, limit=None):
11+
def __init__(self, operation, args, kwargs):
1212
self._operation = operation
1313
self._args = args
1414
self._kwargs = kwargs
15+
self._first_iteration = True
16+
self._last_evaluated_key = None
17+
self._total_scanned_count = 0
18+
19+
def __iter__(self):
20+
return self
21+
22+
def __next__(self):
23+
if self._last_evaluated_key is None and not self._first_iteration:
24+
raise StopIteration()
25+
26+
self._first_iteration = False
27+
28+
self._kwargs['exclusive_start_key'] = self._last_evaluated_key
29+
page = self._operation(*self._args, **self._kwargs)
30+
self._last_evaluated_key = page.get(LAST_EVALUATED_KEY)
31+
self._total_scanned_count += page[SCANNED_COUNT]
32+
33+
return page
34+
35+
def next(self):
36+
return self.__next__()
37+
38+
@property
39+
def page_size(self):
40+
return self._kwargs.get('limit')
41+
42+
@page_size.setter
43+
def page_size(self, page_size):
44+
self._kwargs['limit'] = page_size
45+
46+
@property
47+
def last_evaluated_key(self):
48+
return self._last_evaluated_key
49+
50+
@property
51+
def total_scanned_count(self):
52+
return self._total_scanned_count
53+
54+
55+
class ResultIterator(object):
56+
"""
57+
ResultIterator handles Query and Scan item pagination.
58+
59+
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.Pagination
60+
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.Pagination
61+
"""
62+
def __init__(self, operation, args, kwargs, map_fn=None, limit=None):
63+
self.page_iter = PageIterator(operation, args, kwargs)
64+
self._first_iteration = True
1565
self._map_fn = map_fn
1666
self._limit = limit
17-
self._needs_execute = True
1867
self._total_count = 0
1968

20-
def _execute(self):
21-
data = self._operation(*self._args, **self._kwargs)
22-
self._count = data[CAMEL_COUNT]
23-
self._items = data.get(ITEMS) # not returned if 'Select' is set to 'COUNT'
24-
self._last_evaluated_key = data.get(LAST_EVALUATED_KEY)
69+
def _get_next_page(self):
70+
page = next(self.page_iter)
71+
self._count = page[CAMEL_COUNT]
72+
self._items = page.get(ITEMS) # not returned if 'Select' is set to 'COUNT'
2573
self._index = 0 if self._items else self._count
2674
self._total_count += self._count
2775

@@ -32,16 +80,12 @@ def __next__(self):
3280
if self._limit == 0:
3381
raise StopIteration
3482

35-
if self._needs_execute:
36-
self._needs_execute = False
37-
self._execute()
38-
39-
while self._index == self._count and self._last_evaluated_key:
40-
self._kwargs['exclusive_start_key'] = self._last_evaluated_key
41-
self._execute()
83+
if self._first_iteration:
84+
self._first_iteration = False
85+
self._get_next_page()
4286

43-
if self._index == self._count:
44-
raise StopIteration
87+
while self._index == self._count:
88+
self._get_next_page()
4589

4690
item = self._items[self._index]
4791
self._index += 1
@@ -56,7 +100,7 @@ def next(self):
56100

57101
@property
58102
def last_evaluated_key(self):
59-
return self._last_evaluated_key
103+
return self.page_iter.last_evaluated_key
60104

61105
@property
62106
def total_count(self):

0 commit comments

Comments
 (0)