Запрос DynamoDB с ключом хеширования и ключом диапазона с помощью Boto3

У меня возникли проблемы с использованием AWS Boto3 для одновременного запроса DynamoDB с хеш-ключом и ключом диапазона с использованием рекомендованного KeyConditionExpression. Я приложил пример запроса:

import boto3
from boto3 import dynamodb
from boto3.session import Session
dynamodb_session = Session(aws_access_key_id=AWS_KEY,
              aws_secret_access_key=AWS_PASS,
              region_name=DYNAMODB_REGION)
dynamodb = dynamodb_session.resource('dynamodb')
table=dynamodb.Table(TABLE_NAME)
request = {
    'ExpressionAttributeNames': {
        '#n0': 'hash_key',
        '#n1': 'range_key'
    },
    'ExpressionAttributeValues': {
        ':v0': {'S': MY_HASH_KEY},
        ':v1': {'N': GT_RANGE_KEY}
    },
    'KeyConditionExpression': '(#n0 = :v0) AND (#n1 > :v1)',
    'TableName': TABLE_NAME
}
response = table.query(**request)

Когда я запускаю это против таблицы со следующей схемой:

Table Name: TABLE_NAME
Primary Hash Key: hash_key (String)
Primary Range Key: range_key (Number)

Я получаю следующую ошибку и не могу понять почему:

ClientError: An error occurred (ValidationException) when calling the Query operation: Invalid KeyConditionExpression: Incorrect operand type for operator or function; operator or function: >, operand type: M

Насколько я понимаю, тип M будет типом карты или словаря, и я использую тип N, который является числовым типом и соответствует моей схеме таблицы для ключа диапазона. Если бы кто-нибудь мог объяснить, почему возникает эта ошибка, или я также открыт для другого способа выполнения того же запроса, даже если вы не можете объяснить, почему существует эта ошибка.


person Sean Pannella    schedule 22.10.2015    source источник
comment
Вы выяснили, что не так с вашим кодом? У меня похожая проблема.   -  person tayfun    schedule 22.09.2016


Ответы (2)


Пакет SDK Boto 3 создает для вас выражение условия, когда вы используете функции Key и Attr, импортированные из boto3.dynamodb.conditions:

response = table.query(
    KeyConditionExpression=Key('hash_key').eq(hash_value) & Key('range_key').eq(range_key_value)

)

Ссылка: Шаг 4. Запрос и сканирование данных

Надеюсь, это поможет

person Filipe    schedule 22.10.2015
comment
Последующий вопрос по этому поводу, но знаете ли вы, есть ли способ сделать это в пакетном режиме? - person irregular; 11.03.2017
comment
У меня есть список хеш-значений. Есть ли способ присоединиться к списку с логическим |, используя понимание списка? Что-то вроде '|'.join([Key('hash_key').eq(v) for v in values]) - person falsePockets; 10.02.2018
comment
@falsePockets Только что попробовал, он выдает Произошла ошибка (ValidationException) при вызове операции запроса: KeyConditionExpressions должно содержать только одно условие для каждого ключа - person Mojimi; 03.05.2019
comment
@falsePockets точно вы не можете искать диапазон в Dynamo Key, вы должны использовать FilterExpresion `` `` python response = table.query (KeyConditionExpression = Key ('hash_key'). eq (hash_value) FilterConditionExpression = Key ('range_key') gr. (range_key_value)) `` - person Juan Sebastian Prieto Bustaman; 26.11.2020

Добавление этого решения в качестве принятого ответа не объясняет, почему используемый запрос не работает.

TL; DR: использование запроса к ресурсу Table в boto3 имеет небольшие отличия от использования client.query(...) и требует другого синтаксиса.

Синтаксис действителен для запроса на клиенте, но не для таблицы. ExpressionAttributeValues в таблице не требует указания типа данных. Также, если вы выполняете запрос к ресурсу Table, вам не нужно снова указывать TableName.

Рабочий раствор:

from boto3.session import Session

dynamodb_session = Session(aws_access_key_id=AWS_KEY,aws_secret_access_key=AWS_PASS,region_name=DYNAMODB_REGION)

dynamodb = dynamodb_session.resource('dynamodb')
table = dynamodb.Table(TABLE_NAME)

request = {
    'ExpressionAttributeNames': {
        '#n0': 'hash_key',
        '#n1': 'range_key'
    },
    'ExpressionAttributeValues': {
        ':v0': MY_HASH_KEY,
        ':v1': GT_RANGE_KEY
    },
    'KeyConditionExpression': '(#n0 = :v0) AND (#n1 > :v1)',
}
response = table.query(**request)

Я являюсь автором пакета под названием botoful, который может быть полезен, чтобы избежать этих сложностей. Код с использованием botoful будет следующим:

import boto3
from botoful import Query

client = boto3.Session(
    aws_access_key_id=AWS_KEY,
    aws_secret_access_key=AWS_PASS,
    region_name=DYNAMODB_REGION
).client('dynamodb')

results = (
    Query(TABLE_NAME)
        .key(hash_key=MY_HASH_KEY, range_key__gt=GT_RANGE_KEY)
        .execute(client)
)

print(results.items)
person Imtiaz    schedule 11.03.2021