UpdateItem do DynamoDB em Go (AWS SDK v2)
O UpdateItem é direto em Go; a parte incômoda é que map[string]types.AttributeValue é um mapa de interfaces, então tanto os valores que você envia quanto os que recebe de volta são ponteiros para uma de nove structs membro. Essa única decisão de design explica a maior parte do atrito abaixo, a começar pelo fato de que o fmt.Println deste trecho não imprime o seu item.
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)
out, err := client.UpdateItem(ctx, &dynamodb.UpdateItemInput{
TableName: aws.String("Music"),
Key: map[string]types.AttributeValue{
"Artist": &types.AttributeValueMemberS{Value: "Arturo Sandoval"},
"SongTitle": &types.AttributeValueMemberS{Value: "Cubano Chant"},
},
UpdateExpression: aws.String("SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2"),
ExpressionAttributeNames: map[string]string{
"#upd0": "Genre",
"#upd1": "Year",
"#upd2": "Awards",
},
ExpressionAttributeValues: map[string]types.AttributeValue{
":updValue0": &types.AttributeValueMemberS{Value: "Latin Jazz"},
":updValue1": &types.AttributeValueMemberN{Value: "1994"},
":updValue2": &types.AttributeValueMemberN{Value: "1"},
},
ReturnValues: types.ReturnValueAllNew,
})
if err != nil {
log.Fatalf("update item: %v", err)
}
fmt.Println(out.Attributes) // the item after the update
}Explicação
AttributeValueMemberN.Valueé umastring, não um tipo numérico, e oattributevalue.Marshala mantém assim:int64(9007199254740993)é marshalado para"9007199254740993"exatamente. Os números do DynamoDB carregam 38 dígitos de precisão, mais do que qualquer float do Go, então o SDK nunca converte. Você faz o parsing, na borda, deliberadamente.ReturnValues: types.ReturnValueAllNew— o valor da constante é o literal"ALL_NEW".types.ReturnValueAllNew.Values()lista as cinco (NONE,ALL_OLD,UPDATED_OLD,ALL_NEW,UPDATED_NEW) se você preferir lê-las do tipo em vez da documentação.UpdateExpressioné um*stringque você mesmo monta, ou entrega ao pacoteexpressionabaixo. De qualquer forma, o DynamoDB é o único parser:ADD #upd2 :updValue2dá o incremento atômico,attribute_exists(Artist)em umaConditionExpressionfaz a chamada ser só-atualização em vez de upsert, e a gramática das cláusulas está em expressões de atualização.ValidationExceptionnão tem tipo em Go. Otypes/errors.godefine 35 structs de erro, incluindoConditionalCheckFailedException,TransactionCanceledException,ProvisionedThroughputExceededExceptioneTransactionConflictException. Falhas de validação não estão entre elas, então uma expressão ruim aparece como umsmithy.APIErrorgenérico que você só consegue reconhecer por string:operation error DynamoDB: UpdateItem, https response error StatusCode: 400, RequestID: 702d67f1-4f15-43dc-b3e9-cea691878801, api error ValidationException: Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: YearRepare no prefixo
api error, que oConditionalCheckFailedExceptionmodelado abaixo não recebe. A presença dele é um bom sinal de queerrors.Ascontra um tipo concreto não vai te ajudar aqui.Uma condição que falha, em contraste, é um tipo de verdade.
var ccf *types.ConditionalCheckFailedException; errors.As(err, &ccf)casa, e comReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOldna entrada, occf.Itemchega preenchido com o item como ele de fato estava. Occf.ErrorMessage()é oThe conditional request failedcru, sem o preâmbulo de transporte que oerr.Error()acrescenta.
Lendo o resultado de volta
fmt.Println(out.Attributes) sobre um mapa de interfaces imprime endereços:
map[Artist:0x1c8cf589e060 Awards:0x1c8cf589e078 Genre:0x1c8cf589e090 SongTitle:0x1c8cf589e0c0 Year:0x1c8cf589e0a8]Duas saídas. Faça unmarshal para uma struct, que é o que a maioria dos códigos deveria fazer:
var song struct {
Artist string
Genre string
Year int
Awards int
}
err = attributevalue.UnmarshalMap(out.Attributes, &song)
// {Artist:Arturo Sandoval Genre:Latin Jazz Year:1994 Awards:1}Ou faça a asserção do único membro que te interessa, lembrando que .Value é uma string:
n, ok := out.Attributes["Awards"].(*types.AttributeValueMemberN)
if ok {
awards, _ := strconv.Atoi(n.Value) // "1" -> 1
fmt.Println(awards)
}Deixe o pacote expression escrever isso
O Go é o único SDK deste site que gera a UpdateExpression para você. O feature/dynamodb/expression compõe as cláusulas e os dois mapas:
upd := expression.Set(expression.Name("Genre"), expression.Value("Latin Jazz")).
Set(expression.Name("Year"), expression.Value(1994)).
Add(expression.Name("Awards"), expression.Value(1))
expr, _ := expression.NewBuilder().WithUpdate(upd).Build()O que sai não é o que você escreveu:
UpdateExpression: ADD #0 :0
SET #1 = :1, #2 = :2
Names: map[#0:Awards #1:Genre #2:Year]O builder reordenou as cláusulas, separou-as com uma quebra de linha e numerou os placeholders por conta própria, então #0 é Awards e não o primeiro nome que você mencionou. Nada mais adiante se importa, mas as strings não são estáveis entre edições, o que as torna algo ruim para asserção em testes. Passe expr.Update(), expr.Names() e expr.Values() direto para o UpdateItemInput e nunca olhe para elas.
A vantagem é que ele coloca alias em todo nome, então palavras reservadas deixam de ser uma classe de bug que você pode publicar. Se em vez disso você escrever a expressão à mão, passe os nomes de atributo pelo verificador de palavras reservadas do DynamoDB primeiro — a lista da AWS tem 573 entradas e Year, Name e Status estão todas nela. E se você preferir olhar um item como dados em vez de um mapa de ponteiros de interface, baixe o DynoTable.
Exemplos relacionados
- UpdateItem do DynamoDB em Java — a mesma atualização com o AWS SDK for Java 2.x.
- PutItem do DynamoDB em Go — substitua o item inteiro em vez disso.
- Expressões de atualização do DynamoDB —
SET,ADD,REMOVE,DELETEe idiomatismos. - Entendendo o ReturnValues — o que cada opção de
ReturnValueste dá. - "Attribute name is a reserved keyword" — por que o mapa de aliases aqui não é opcional.
- Erros de sintaxe "Invalid UpdateExpression" — os erros comuns de sintaxe de SET/ADD, decodificados.
Referências
- UpdateItem — Amazon DynamoDB API Reference
- Use UpdateItem with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- dynamodb package — AWS SDK for Go v2 (pkg.go.dev)
- dynamodb/types package — AWS SDK for Go v2 (pkg.go.dev)
- expression package — AWS SDK for Go v2 (pkg.go.dev)
- Update expressions — Amazon DynamoDB Developer Guide
Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.