DynamoDB UpdateItem en Go (AWS SDK v2)
UpdateItem est simple en Go ; le point délicat, c'est que map[string]types.AttributeValue est une map d'interfaces, donc les valeurs que tu envoies comme celles que tu récupères sont des pointeurs vers l'une des neuf structs membres. Ce seul choix de conception explique l'essentiel des frictions ci-dessous, à commencer par le fait que le fmt.Println de cet extrait n'imprime pas ton élément.
Code
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
}Explication
AttributeValueMemberN.Valueest unestring, pas un type numérique, etattributevalue.Marshalla garde ainsi :int64(9007199254740993)se marshalle exactement en"9007199254740993". Les nombres DynamoDB portent 38 chiffres de précision, plus qu'aucun flottant Go, donc le SDK ne convertit jamais. C'est toi qui fais l'analyse, à la frontière, délibérément.ReturnValues: types.ReturnValueAllNew— la valeur de la constante est le littéral"ALL_NEW".types.ReturnValueAllNew.Values()liste les cinq (NONE,ALL_OLD,UPDATED_OLD,ALL_NEW,UPDATED_NEW) si tu préfères les lire sur le type plutôt que dans la documentation.UpdateExpressionest un*stringque tu construis toi-même, ou que tu confies au paquetexpressionci-dessous. Dans tous les cas, DynamoDB est le seul analyseur :ADD #upd2 :updValue2donne l'incrément atomique,attribute_exists(Artist)dans uneConditionExpressionrend l'appel purement modificateur au lieu d'un upsert, et la grammaire des clauses est dans les expressions de mise à jour.ValidationExceptionn'a pas de type Go.types/errors.godéfinit 35 structs d'erreur, dontConditionalCheckFailedException,TransactionCanceledException,ProvisionedThroughputExceededExceptionetTransactionConflictException. Les échecs de validation n'en font pas partie, donc une mauvaise expression apparaît comme unsmithy.APIErrorgénérique que tu ne peux reconnaître qu'à la chaîne :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: YearNote le préfixe
api error, que laConditionalCheckFailedExceptionmodélisée ci-dessous n'obtient pas. Sa présence est un signal correct queerrors.Ascontre un type concret ne va pas t'aider ici.Une condition échouée, en revanche, est un vrai type.
var ccf *types.ConditionalCheckFailedException; errors.As(err, &ccf)correspond, et avecReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOldsur l'entrée,ccf.Itemarrive rempli avec l'élément tel qu'il était réellement.ccf.ErrorMessage()donne le simpleThe conditional request failed, sans le préambule de transport qu'err.Error()ajoute devant.
Relire le résultat
fmt.Println(out.Attributes) sur une map d'interfaces imprime des adresses :
map[Artist:0x1c8cf589e060 Awards:0x1c8cf589e078 Genre:0x1c8cf589e090 SongTitle:0x1c8cf589e0c0 Year:0x1c8cf589e0a8]Deux sorties possibles. Démarshaller dans une struct, ce que la plupart du code devrait faire :
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 faire une assertion sur le seul membre qui t'intéresse, en te souvenant que .Value est une chaîne :
n, ok := out.Attributes["Awards"].(*types.AttributeValueMemberN)
if ok {
awards, _ := strconv.Atoi(n.Value) // "1" -> 1
fmt.Println(awards)
}Laisse le paquet expression l'écrire
Go est le seul SDK de ce site qui générera l'UpdateExpression pour toi. feature/dynamodb/expression compose les clauses et les deux maps :
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()Ce qui en sort n'est pas ce que tu as écrit :
UpdateExpression: ADD #0 :0
SET #1 = :1, #2 = :2
Names: map[#0:Awards #1:Genre #2:Year]Le builder a réordonné les clauses, les a séparées par un saut de ligne, et a numéroté lui-même les placeholders, si bien que #0 est Awards plutôt que le premier nom que tu as mentionné. Rien en aval ne s'en soucie, mais ces chaînes ne sont pas stables d'une modification à l'autre, ce qui en fait de mauvaises cibles d'assertion dans les tests. Passe expr.Update(), expr.Names() et expr.Values() directement dans UpdateItemInput et ne les regarde jamais.
L'avantage, c'est qu'il aliase chaque nom : les mots réservés cessent d'être une classe de bug que tu peux expédier. Si tu écris l'expression à la main, passe d'abord les noms d'attribut par le vérificateur de mots réservés DynamoDB — la liste AWS compte 573 entrées et Year, Name et Status y sont tous. Et si tu préfères regarder un élément comme de la donnée plutôt que comme une map de pointeurs d'interface, télécharge DynoTable.
Exemples liés
- DynamoDB UpdateItem en Java — la même mise à jour avec AWS SDK for Java 2.x.
- DynamoDB PutItem en Go — remplacer l'élément entier à la place.
- Les expressions de mise à jour DynamoDB —
SET,ADD,REMOVE,DELETE, et les idiomes. - Comprendre ReturnValues — ce que chaque option
ReturnValueste donne. - "Attribute name is a reserved keyword" — pourquoi la map d'alias ici n'est pas facultative.
- Les erreurs de syntaxe "Invalid UpdateExpression" — les fautes de syntaxe SET/ADD courantes, décodées.
Références
- 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
Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.