web-dev-qa-db-fra.com

Analyse complète de dynamoDb avec boto3

Ma table est d'environ 220Mo avec 250K enregistrements. J'essaie d'extraire toutes ces données en python. Je me rends compte qu'il doit s'agir d'un traitement par lots fragmenté et bouclé, mais je ne suis pas sûr de savoir comment définir les lots de manière à ce que les précédents soient laissés de côté.

Est-il possible de filtrer mon scan? D'après ce que j'ai lu, le filtrage a lieu après le chargement et le chargement s'arrête à 1 Mo, donc je ne serais pas en mesure d'analyser de nouveaux objets.

Toute aide serait appréciée.

import boto3
dynamodb = boto3.resource('dynamodb',
    aws_session_token = aws_session_token,
    aws_access_key_id = aws_access_key_id,
    aws_secret_access_key = aws_secret_access_key,
    region_name = region
    )

table = dynamodb.Table('widgetsTableName')

data = table.scan()
38
CJ_Spaz

Je pense que le documentation Amazon DynamoDB en ce qui concerne l'analyse de table répond à votre question.

En bref, vous devrez vérifier LastEvaluatedKey dans la réponse. Voici un exemple utilisant votre code:

import boto3
dynamodb = boto3.resource('dynamodb',
                          aws_session_token=aws_session_token,
                          aws_access_key_id=aws_access_key_id,
                          aws_secret_access_key=aws_secret_access_key,
                          region_name=region
)

table = dynamodb.Table('widgetsTableName')

response = table.scan()
data = response['Items']

while 'LastEvaluatedKey' in response:
    response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'])
    data.extend(response['Items'])
46
Tay B

boto3 propose des paginateurs qui gèrent tous les détails de la pagination pour vous. Here est la page de documentation du paginateur de numérisation. En gros, vous l'utiliseriez comme ceci:

import boto3

client = boto3.client('dynamodb')
paginator = client.get_paginator('scan')

for page in paginator.paginate():
    # do something
22
Jordon Phillips

Décrivant la réponse de Jordon Phillips, voici comment passer un FilterExpression avec la pagination:

import boto3

client = boto3.client('dynamodb')
paginator = client.get_paginator('scan')
operation_parameters = {
  'TableName': 'foo',
  'FilterExpression': 'bar > :x AND bar < :y',
  'ExpressionAttributeValues': {
    ':x': {'S': '2017-01-31T01:35'},
    ':y': {'S': '2017-01-31T02:08'},
  }
}

page_iterator = paginator.paginate(**operation_parameters)
for page in page_iterator:
    # do something
11
Abe Voelker

Code pour supprimer le type de format dynamodb comme indiqué par @kungphu.

import boto3

from boto3.dynamodb.types import TypeDeserializer
from boto3.dynamodb.transform import TransformationInjector

client = boto3.client('dynamodb')
paginator = client.get_paginator('query')
service_model = client._service_model.operation_model('Query')
trans = TransformationInjector(deserializer = TypeDeserializer())
for page in paginator.paginate():
    trans.inject_attribute_value_output(page, service_model)
8
Vincent

Il s'avère que Boto3 capture la "LastEvaluatedKey" dans la réponse renvoyée. Ceci peut être utilisé comme point de départ pour une analyse:

data= table.scan(
   ExclusiveStartKey=data['LastEvaluatedKey']
)

Je prévois de créer une boucle autour de cela jusqu'à ce que les données renvoyées ne soient plus que ExclusiveStartKey

3
CJ_Spaz

La réponse de Vincent concernant la transformation appliquée à LastEvaluatedKey et la pagination me posait problème. Résolu comme suit:

import boto3

from boto3.dynamodb.types import TypeDeserializer
from boto3.dynamodb.transform import TransformationInjector

client = boto3.client('dynamodb')
paginator = client.get_paginator('scan')
operation_model = client._service_model.operation_model('Scan')
trans = TransformationInjector(deserializer = TypeDeserializer())
operation_parameters = {
  'TableName': 'tablename',  
}
items = []

for page in paginator.paginate(**operation_parameters):
    has_last_key = 'LastEvaluatedKey' in page
    if has_last_key:
        last_key = page['LastEvaluatedKey'].copy()
    trans.inject_attribute_value_output(page, operation_model)
    if has_last_key:
        page['LastEvaluatedKey'] = last_key
    items.extend(page['Items'])
1
Dan Hook

DynamoDB limite la méthode scan à 1 Mo de données par analyse.

Documentation: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html# DynamoDB.Client.scan

Voici un exemple de boucle permettant d'obtenir toutes les données d'une table DynamoDB à l'aide de LastEvaluatedKey:

import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('your_table_name')

has_items = True
last_key = False
while has_items:

    if last_key:
        data = table.scan(ExclusiveStartKey=last_key)
    else:
        data = table.scan()

    if 'LastEvaluatedKey' in data:
        has_items = True
        last_key = data['LastEvaluatedKey']
    else:
        has_items = False
        last_key = False

    # TODO do something with data['Items'] here.
0
Richard

Les deux approches suggérées ci-dessus ont toutes deux des problèmes: écrire du code long et répétitif qui gère la pagination de manière explicite dans une boucle, ou utiliser des paginateurs Boto avec des sessions de bas niveau et renoncer aux avantages des objets Boto de niveau supérieur.

Une solution utilisant Python) pour fournir une abstraction de haut niveau permet d'utiliser des méthodes Boto de niveau supérieur, tout en masquant la complexité de la pagination AWS:

import itertools
import typing

def iterate_result_pages(function_returning_response: typing.Callable, *args, **kwargs) -> typing.Generator:
    """A wrapper for functions using AWS paging, that returns a generator which yields a sequence of items for
    every response

    Args:
        function_returning_response: A function (or callable), that returns an AWS response with 'Items' and optionally 'LastEvaluatedKey'
        This could be a bound method of an object.

    Returns:
        A generator which yields the 'Items' field of the result for every response
    """
    response = function_returning_response(*args, **kwargs)
    yield response["Items"]
    while "LastEvaluatedKey" in response:
        kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
        response = function_returning_response(*args, **kwargs)
        yield response["Items"]

    return

def iterate_paged_results(function_returning_response: typing.Callable, *args, **kwargs) -> typing.Iterator:
    """A wrapper for functions using AWS paging, that returns an iterator of all the items in the responses.
    Items are yielded to the caller as soon as they are received.

    Args:
        function_returning_response: A function (or callable), that returns an AWS response with 'Items' and optionally 'LastEvaluatedKey'
        This could be a bound method of an object.

    Returns:
        An iterator which yields one response item at a time
    """
    return itertools.chain.from_iterable(iterate_result_pages(function_returning_response, *args, **kwargs))

# Example, assuming 'table' is a Boto DynamoDB table object:
all_items = list(iterate_paged_results(ProjectionExpression = 'my_field'))
0
YitzikC