Query em um GSI do DynamoDB em Python (boto3)
Uma consulta a um GSI é um query normal mais o IndexName, e o AlbumTitle-index nos dá as músicas por álbum, um padrão de acesso que a chave de tabela Artist + SongTitle não consegue atender. O que muda no Python é o tratamento de erros: os dois erros de índice mais comuns falham em camadas diferentes do boto3, e apenas um deles pode ser capturado por classe de exceção.
Código
import boto3
client = boto3.client("dynamodb")
paginator = client.get_paginator("query")
items = []
for page in paginator.paginate(
TableName="Music",
IndexName="AlbumTitle-index",
KeyConditionExpression="#hashKey = :hashKeyValue",
ExpressionAttributeNames={"#hashKey": "AlbumTitle"},
ExpressionAttributeValues={":hashKeyValue": {"S": "Danzon"}},
):
items.extend(page["Items"])
print(f"Found {len(items)} songs on the album")except ValidationException nem compila, quanto mais captura
Adicione ConsistentRead=True à consulta acima e o boto3 lança isto, tanto na API de cliente quanto na de recurso:
botocore.exceptions.ClientError: An error occurred (ValidationException) when
calling the Query operation: Consistent reads are not supported on global
secondary indexesO handler óbvio é except client.exceptions.ValidationException. Ele não existe:
AttributeError: <botocore.errorfactory.DynamoDBExceptions object> has no
attribute ValidationException. Valid exceptions are: BackupInUseException,
... IndexNotFoundException, ... ProvisionedThroughputExceededException, ...O botocore gera as classes de exceção a partir do modelo do serviço, e o DynamoDB modela 33 delas. ValidationException é um erro de nível de protocolo e não é uma delas, então a única ramificação confiável é pelo código:
except ClientError as exc:
if exc.response["Error"]["Code"] == "ValidationException":
...A assimetria é real. Digite o nome do índice errado e você recebe IndexNotFoundException, que é modelada e capturável por classe. Use a flag de consistência de forma indevida e você recebe uma comparação de string. Ambos são erros de índice; só um tem tipo.
O cursor carrega a chave da tabela também
O paginador esconde o LastEvaluatedKey, mas vale saber o que ele guarda em um índice. Sobre 300 músicas de um mesmo álbum:
page 1: Count 271 capacity 128.5 LastEvaluatedKey ['AlbumTitle', 'Artist', 'SongTitle']
page 2: Count 29 capacity 14.0 LastEvaluatedKey []Uma chave de GSI não é única, então a chave do índice sozinha não consegue retomar a leitura; o DynamoDB devolve a chave do índice e a chave da tabela base juntas. Uma paginação feita à mão que guarde apenas a chave do índice repete ou perde itens.
Reproduzido em 2026-07-28 contra o DynamoDB Local (amazon/dynamodb-local) com boto3 1.43.58 no CPython 3.14.6. O texto do erro e as listas de chaves são a saída da própria biblioteca.
Explicação
IndexNamenão substitui oTableName. Os dois vão na mesma chamada, e aKeyConditionExpressionnomeia a chave de partição do índice (AlbumTitle), com o mesmo conjunto de operadores de uma consulta à tabela.- Você recebe a projeção e nada além dela. O índice devolve o que ele projeta (
ALL,KEYS_ONLYou a lista doINCLUDE); conforme a referência da API, "global secondary index queries cannot fetch attributes from the parent table". Um atributo faltando significa umget_itemde acompanhamento na chave base, ou uma projeção mais ampla em um novo índice. - Itens sem a chave do índice nunca aparecem — o padrão de índice esparso. Ele mantém pequeno um índice sobre
status = "OPEN", e é também por isso que uma consulta a um GSI pode devolver menos do que você espera sem lançar nada. - A API de recurso recebe o mesmo
IndexName:table.query(IndexName="AlbumTitle-index", KeyConditionExpression=Key("AlbumTitle").eq("Danzon")), com valores nativos do Python na entrada eDecimalna saída. - Uma escrita no GSI chega depois da escrita na tabela. A replicação é assíncrona, então um caminho de leitura-após-escrita contra o índice vai eventualmente não encontrar nada. Repetir isso em um loop apertado queima capacidade sem deixar a replicação mais rápida.
Faça isso visualmente
O DynamoDB Expression Builder escreve a condição de chave do índice e o mapa de valores tipados como Python pronto para o boto3, incluindo os invólucros {"S": ...} que o cliente exige e a API de recurso proíbe.
Para apontar essa mesma consulta de índice para as suas próprias tabelas a partir de um formulário e ler os resultados em uma grade paginada, baixe o DynoTable.
Exemplos relacionados
- Query em um GSI do DynamoDB em Node.js — a mesma consulta de índice com o AWS SDK v3.
- Query em um GSI do DynamoDB com a AWS CLI — a mesma consulta de índice pelo shell.
- DynamoDB Query em Python — consultando a tabela base.
- GSI vs. LSI — qual tipo de índice se encaixa no padrão de acesso.
- Por que os GSIs têm consistência eventual — o atraso de replicação explicado.
- "The table does not have the specified index" — o nome do índice não bate (nomes de GSI diferenciam maiúsculas de minúsculas).
- "Consistent reads are not supported on global secondary indexes" — por que a flag de leitura consistente falha em um GSI.