DynamoDB in examples, Example 2.3: Pagination
Let's add pagination for the page views example.
class DDBPageView(DDBTable): ... def page_views(self, page_id, last=None, limit=10): ddb_query = self._dynamodb(operation='Query') kwargs = { 'TableName': self._get_table_name(), 'IndexName': 'by_page_id', 'KeyConditions': { 'page_id': { 'AttributeValueList': [{ 'S': str(page_id), }], 'ComparisonOperator': 'EQ' }, }, 'Limit': limit } if last: kwargs['ExclusiveStartKey'] = last result = ddb_query.call(**kwargs) return ( [self.decode_item(item) for item in result.get('Items')], result.get('LastEvaluatedKey')) if __name__ == '__main__': ddb_page_view = DDBPageView() ddb_page_view.create_table() pages = [] for i in range(3): pages.append(uuid4()) users = [] for i in range(10): user_id = uuid4() users.append(user_id) for j in range(3): ddb_page_view.view(page_id=random.choice(pages), user_id=user_id) views, last = ddb_page_view.page_views(page_id=pages[0], limit=2) print(views, last) # [{'page_id': '83017f95-e4ca-4c25-a56b-d521897c0f70', 'user_id': 'a70d66d7-7fe7-41a3-bae3-1d8428918c9a', 'created': '2015-03-29 13:15:55.433674', 'page_id_user_id': '83017f95-e4ca-4c25-a56b-d521897c0f70_a70d66d7-7fe7-41a3-bae3-1d8428918c9a'}, {'page_id': '83017f95-e4ca-4c25-a56b-d521897c0f70', 'user_id': '2ea9bdf9-d0e6-4939-973c-977059f70761', 'created': '2015-03-29 13:15:55.494103', 'page_id_user_id': '83017f95-e4ca-4c25-a56b-d521897c0f70_2ea9bdf9-d0e6-4939-973c-977059f70761'}] # {'page_id': {'S': '83017f95-e4ca-4c25-a56b-d521897c0f70'}, 'created': {'S': '2015-03-29 13:15:55.494103'}, 'page_id_user_id': {'S': '83017f95-e4ca-4c25-a56b-d521897c0f70_2ea9bdf9-d0e6-4939-973c-977059f70761'}} views, last = ddb_page_view.page_views(page_id=pages[0], last=last, limit=2) print(views, last) # [{'page_id': '83017f95-e4ca-4c25-a56b-d521897c0f70', 'user_id': 'e1b98531-b366-460a-8aca-994e03171a80', 'created': '2015-03-29 13:15:55.293803', 'page_id_user_id': '83017f95-e4ca-4c25-a56b-d521897c0f70_e1b98531-b366-460a-8aca-994e03171a80'}, {'page_id': '83017f95-e4ca-4c25-a56b-d521897c0f70', 'user_id': 'c3c728d5-1db0-465e-935c-0bcf34151546', 'created': '2015-03-29 13:15:55.355471', 'page_id_user_id': '83017f95-e4ca-4c25-a56b-d521897c0f70_c3c728d5-1db0-465e-935c-0bcf34151546'}] # {'page_id': {'S': '83017f95-e4ca-4c25-a56b-d521897c0f70'}, 'created': {'S': '2015-03-29 13:15:55.355471'}, 'page_id_user_id': {'S': '83017f95-e4ca-4c25-a56b-d521897c0f70_c3c728d5-1db0-465e-935c-0bcf34151546'}}
^^ This way is preferable.
Alternative:
import datetime import random from uuid import uuid4 from ddb_table import ( DDBTable, DDBUUIDField, DDBUUID_UUIDField, DDBStrField, AmazonException) DDB_LOCAL_URL = 'http://localhost:8010' class DDBPageView(DDBTable): TABLE_NAME = 'page_view' KEY_SCHEMA = [{ 'AttributeName': 'page_id_user_id', 'KeyType': 'HASH', }] PROVISIONED_THROUGHPUT = { 'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1 } GLOBAL_SECONDARY_INDEXES = [{ 'IndexName': 'by_page_id', 'KeySchema': [{ 'AttributeName': 'page_id', 'KeyType': 'HASH' }, { 'AttributeName': 'page_id_user_id_created', 'KeyType': 'RANGE' } ], 'Projection': { 'ProjectionType': 'ALL', }, 'ProvisionedThroughput': { 'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1, } }] FIELDS = { 'page_id_user_id': DDBUUID_UUIDField, 'page_id': DDBUUIDField, 'user_id': DDBUUIDField, 'page_id_user_id_created': DDBStrField, 'created': DDBStrField, } def _get_endpoint_url(self): return DDB_LOCAL_URL def view(self, page_id, user_id): page_id_user_id = '{page_id}_{user_id}'.format(page_id=page_id, user_id=user_id) created = datetime.datetime.now() try: self._dynamodb(operation='PutItem').call( TableName=self._get_table_name(), Item=self.encode_item(data={ 'page_id_user_id': page_id_user_id, 'created': str(created), 'page_id_user_id_created': '{page_id_user_id}_{created}'.format( page_id_user_id=page_id_user_id, created=created), 'page_id': str(page_id), 'user_id': str(user_id)}), ConditionExpression='attribute_not_exists(page_id_user_id)') except AmazonException as e: if e.code == 'ConditionalCheckFailedException': return False # already exists raise e return True def page_views(self, page_id, last_page_id_user_id_created=None, limit=10): ddb_query = self._dynamodb(operation='Query') kwargs = { 'TableName': self._get_table_name(), 'IndexName': 'by_page_id', 'KeyConditions': { 'page_id': { 'AttributeValueList': [{ 'S': str(page_id), }], 'ComparisonOperator': 'EQ' }, 'page_id_user_id_created': { 'AttributeValueList': [{ 'S': str(last_page_id_user_id_created or page_id), }], 'ComparisonOperator': 'GT' }, }, 'Limit': limit, 'ScanIndexForward': True, } result = ddb_query.call(**kwargs) return [self.decode_item(item) for item in result.get('Items')] if __name__ == '__main__': ddb_page_view = DDBPageView() ddb_page_view.create_table() pages = [] for i in range(3): pages.append(uuid4()) users = [] for i in range(10): user_id = uuid4() users.append(user_id) for j in range(3): ddb_page_view.view(page_id=random.choice(pages), user_id=user_id) views = ddb_page_view.page_views(page_id=pages[0], limit=2) print(views) # [{'page_id': '02246f83-140b-4850-9893-967229a37aef', 'page_id_user_id': '02246f83-140b-4850-9893-967229a37aef_2a45c61d-29df-4ea6-9eb4-9b5e17f1a5dd', 'page_id_user_id_created': '02246f83-140b-4850-9893-967229a37aef_2a45c61d-29df-4ea6-9eb4-9b5e17f1a5dd_2015-03-29 13:36:08.069563', 'user_id': '2a45c61d-29df-4ea6-9eb4-9b5e17f1a5dd', 'created': '2015-03-29 13:36:08.069563'}, {'page_id': '02246f83-140b-4850-9893-967229a37aef', 'page_id_user_id': '02246f83-140b-4850-9893-967229a37aef_58b78382-a5e8-412e-9e32-15a35d1cd30c', 'page_id_user_id_created': '02246f83-140b-4850-9893-967229a37aef_58b78382-a5e8-412e-9e32-15a35d1cd30c_2015-03-29 13:36:07.878259', 'user_id': '58b78382-a5e8-412e-9e32-15a35d1cd30c', 'created': '2015-03-29 13:36:07.878259'}] views = ddb_page_view.page_views( page_id=pages[0], last_page_id_user_id_created=views[-1]['page_id_user_id_created'], limit=2) print(views) # [{'page_id': '02246f83-140b-4850-9893-967229a37aef', 'page_id_user_id': '02246f83-140b-4850-9893-967229a37aef_79603222-1e31-4b1e-93a9-381d90d00945', 'page_id_user_id_created': '02246f83-140b-4850-9893-967229a37aef_79603222-1e31-4b1e-93a9-381d90d00945_2015-03-29 13:36:07.714803', 'user_id': '79603222-1e31-4b1e-93a9-381d90d00945', 'created': '2015-03-29 13:36:07.714803'}, {'page_id': '02246f83-140b-4850-9893-967229a37aef', 'page_id_user_id': '02246f83-140b-4850-9893-967229a37aef_bc75c6b9-da95-4cec-b232-a0eac6a53033', 'page_id_user_id_created': '02246f83-140b-4850-9893-967229a37aef_bc75c6b9-da95-4cec-b232-a0eac6a53033_2015-03-29 13:36:07.692193', 'user_id': 'bc75c6b9-da95-4cec-b232-a0eac6a53033', 'created': '2015-03-29 13:36:07.692193'}]
Query response limited to 1MB.
If You need to iterate through all query results:
class DDBPageView(DDBTable): ... def scan_page_views(self, page_id): ddb_query = self._dynamodb(operation='Query') last = None while True: kwargs = { 'TableName': self._get_table_name(), 'IndexName': 'by_page_id', 'KeyConditions': { 'page_id': { 'AttributeValueList': [{ 'S': str(page_id), }], 'ComparisonOperator': 'EQ' }, }, 'ScanIndexForward': True, } if last: kwargs['ExclusiveStartKey'] = last result = ddb_query.call(**kwargs) for item in result.get('Items', []): data = self.decode_item(item) print(data) last = result.get('LastEvaluatedKey') if last is None: break if __name__ == '__main__': ddb_page_view = DDBPageView() ddb_page_view.create_table() pages = [] for i in range(2): pages.append(uuid4()) users = [] for i in range(100): user_id = uuid4() users.append(user_id) for j in range(3): ddb_page_view.view(page_id=random.choice(pages), user_id=user_id) ddb_page_view.scan_page_views(page_id=pages[0])
Licensed under CC BY-SA 3.0