Query do DynamoDB em Go (AWS SDK v2)
Query lê uma partição, opcionalmente estreitada pela chave de ordenação (Query vs Scan explica quando essa é a escolha certa, e expressões de condição de chave lista todos os operadores permitidos). O que o AWS SDK for Go v2 acrescenta é o QueryPaginator, que transforma o loop de LastEvaluatedKey em um loop for e, ao fazer isso, silenciosamente tira sua chance de parar.
Código
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func main() {
ctx := context.TODO()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
if err != nil {
log.Fatalf("load config: %v", err)
}
client := dynamodb.NewFromConfig(cfg)
paginator := dynamodb.NewQueryPaginator(client, &dynamodb.QueryInput{
TableName: aws.String("Music"),
KeyConditionExpression: aws.String(
"#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)"),
ExpressionAttributeNames: map[string]string{
"#hashKey": "Artist",
"#rangeKey": "SongTitle",
},
ExpressionAttributeValues: map[string]types.AttributeValue{
":hashKeyValue": &types.AttributeValueMemberS{Value: "Arturo Sandoval"},
":rangeKeyValue": &types.AttributeValueMemberS{Value: "C"},
},
})
var items []map[string]types.AttributeValue
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
log.Fatalf("query: %v", err)
}
items = append(items, page.Items...)
}
fmt.Printf("Found %d items\n", len(items))
}Explicação
O Limit não limita o que o paginador lê. Esse é o que custa dinheiro. Definir Limit: aws.Int32(5) na entrada acima e rodar sobre uma partição de 30 itens de ~60 KB cada produziu:
page 1: Count=5 CU=37.0 page 5: Count=5 CU=37.0
page 2: Count=5 CU=37.0 page 6: Count=5 CU=37.0
page 3: Count=5 CU=37.0 page 7: Count=0 CU=0.0
page 4: Count=5 CU=37.0 total: 30 items, 222.0 unitsTodos os trinta itens voltaram. Limit é um tamanho de página, e a função do paginador é continuar pedindo até as páginas acabarem, então os dois se anulam exatamente. A AWS o define como "the maximum number of items to evaluate (not necessarily the number of matching items)" (consultado em 2026-07-28). Se você quer os cinco primeiros itens, dê break no loop você mesmo depois da primeira página.
Páginas pequenas custam um pouco mais, não menos. A mesma partição lida sem Limit pagina em duas páginas por 220,0 unidades; com Limit: 5 foram sete chamadas por 222,0. Cada página arredonda o próprio total de bytes para o próximo múltiplo de 4 KB, então mais páginas significa mais arredondamento, além de seis idas e voltas extras de latência. Baixar o Limit para "ler menos" não te dá nem uma coisa nem outra.
O loop sempre faz uma chamada a mais do que existe de dados. A página 7 acima devolveu zero itens. O DynamoDB devolve um LastEvaluatedKey sempre que o Limit foi atingido, havendo ou não algo em seguida, e o HasMorePages() acredita nele. Então um paginador com Limit termina em uma requisição desperdiçada, e qualquer efeito colateral por página (uma barra de progresso, um flush de lote, uma linha de log) dispara uma vez contra uma página vazia. Proteja-se com len(page.Items).
HasMorePages() é true antes da primeira requisição. Ele é inicializado como true para que o loop for sequer entre, o que significa que é uma condição de loop, não uma verificação de "tem dados". Chamá-lo para decidir se vale a pena consultar sempre diz sim.
Os erros aparecem por página, e uma leitura parcial é um estado real. NextPage retorna o mesmo erro embrulhado do smithy que qualquer outra chamada, então desembrulhe com errors.As contra *types.ProvisionedThroughputExceededException e afins em vez de comparar strings. O log.Fatalf do trecho joga fora as páginas já coletadas; em um serviço você geralmente quer manter items e reportar até onde conseguiu chegar.
ScanIndexForward é a única forma de ler uma partição de trás para frente. Defina ScanIndexForward: aws.Bool(false) para ordem decrescente de chave de ordenação. Não existe "sort by" para mais nada: a ordem vem da chave de ordenação, e se você precisa de outra ordem, precisa de outro índice. IndexName: aws.String("...") move a consulta inteira para aquele índice.
Dois pacotes deixam isso mais curto. github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression monta a condição de chave e os dois mapas de placeholders a partir de expression.Key("Artist").Equal(expression.Value("Arturo Sandoval")), o que elimina os pares #hashKey/:hashKeyValue escritos à mão e, junto com eles, o risco de palavra reservada. attributevalue.UnmarshalListOfMaps(page.Items, &songs) transforma uma página direto em []Song.
Faça isso visualmente
Os dois mapas de placeholders são a parte que vale gerar em vez de digitar. O DynamoDB Expression Builder gratuito monta a condição de chave com os ExpressionAttributeNames e ExpressionAttributeValues correspondentes e emite o literal Go, para que os nomes e valores não possam se descolar.
Para rodar consultas nas suas próprias tabelas — formulário de condição de chave, uma grade que pagina conforme você rola, copiar a requisição de volta como Go — baixe o DynoTable.
Exemplos relacionados
- DynamoDB Query em Java — a mesma consulta com o AWS SDK for Java 2.x.
- DynamoDB Scan em Go — quando você não consegue entrar por uma chave de partição.
- Paginação —
LastEvaluatedKey,ExclusiveStartKeye por queLimitnão é um tamanho de página. - "Query condition missed key schema element" — a condição de chave nomeia o atributo errado ou pula a chave de partição.
- "Query key condition not supported" — um operador que a condição de chave não pode usar, como contains ou uma segunda condição de chave de ordenação.
Referências
- Query — Amazon DynamoDB API Reference
- Use Query with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- dynamodb package — AWS SDK for Go v2 (pkg.go.dev)
- expression package — AWS SDK for Go v2 (pkg.go.dev)
- Querying tables — Amazon DynamoDB Developer Guide
Medido em 2026-07-28 no go1.26.5 com aws-sdk-go-v2/service/dynamodb v1.62.1, contra o DynamoDB Local (amazon/dynamodb-local) na porta 9000, sobre uma partição de 30 itens de ~60 KB cada. As contagens por página e as leituras de capacidade são saída capturada. O DynamoDB Local aplica as regras de arredondamento documentadas; trate as unidades absolutas como uma demonstração do formato e meça o serviço antes de dimensionar.