DynamoDB UpdateItem in Go (AWS SDK v2)
UpdateItem ist in Go unkompliziert; das Unangenehme ist, dass map[string]types.AttributeValue eine Map von Interfaces ist — sowohl die Werte, die du schickst, als auch die, die du zurückbekommst, sind Pointer auf eines von neun Member-Structs. Diese eine Design-Entscheidung erklärt fast die gesamte Reibung weiter unten, angefangen damit, dass das fmt.Println in diesem Snippet nicht dein Item ausgibt.
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
}Erklärung
AttributeValueMemberN.Valueist einstring, kein numerischer Typ, undattributevalue.Marshallässt das so:int64(9007199254740993)marshallt exakt zu"9007199254740993". DynamoDB-Zahlen tragen 38 Stellen Genauigkeit, mehr als jedes Go-Float — das SDK konvertiert also nie. Das Parsen machst du, am Rand, bewusst.ReturnValues: types.ReturnValueAllNew— der Wert der Konstante ist das Literal"ALL_NEW".types.ReturnValueAllNew.Values()listet alle fünf (NONE,ALL_OLD,UPDATED_OLD,ALL_NEW,UPDATED_NEW), falls du sie lieber am Typ abliest als in den Docs.UpdateExpressionist ein*string, den du selbst baust — oder an dasexpression-Paket weiter unten abgibst. So oder so ist DynamoDB der einzige Parser:ADD #upd2 :updValue2ergibt das atomare Inkrement,attribute_exists(Artist)in einerConditionExpressionmacht den Aufruf zu einem reinen Update statt zu einem Upsert, und die Klausel-Grammatik steht in Update Expressions.ValidationExceptionhat keinen Go-Typ.types/errors.godefiniert 35 Fehler-Structs, darunterConditionalCheckFailedException,TransactionCanceledException,ProvisionedThroughputExceededExceptionundTransactionConflictException. Validierungsfehler sind nicht dabei — eine fehlerhafte Expression taucht also als generischersmithy.APIErrorauf, den du nur am String erkennen kannst: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: YearBeachte das Präfix
api error, das die modellierteConditionalCheckFailedExceptionweiter unten nicht bekommt. Sein Vorhandensein ist ein brauchbares Signal dafür, dass direrrors.Asgegen einen konkreten Typ hier nicht weiterhilft.Eine fehlgeschlagene Bedingung ist im Gegensatz dazu ein echter Typ.
var ccf *types.ConditionalCheckFailedException; errors.As(err, &ccf)greift, und mitReturnValuesOnConditionCheckFailure: types.ReturnValuesOnConditionCheckFailureAllOldim Input kommtccf.Itemgefüllt an, mit dem Item so, wie es tatsächlich war.ccf.ErrorMessage()ist das nackteThe conditional request failed, ohne die Transport-Präambel, dieerr.Error()voranstellt.
Das Ergebnis zurücklesen
fmt.Println(out.Attributes) auf einer Interface-Map gibt Adressen aus:
map[Artist:0x1c8cf589e060 Awards:0x1c8cf589e078 Genre:0x1c8cf589e090 SongTitle:0x1c8cf589e0c0 Year:0x1c8cf589e0a8]Zwei Wege heraus. Unmarshall in ein Struct — das ist, was der meiste Code tun sollte:
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}Oder assertiere das eine Member, das dich interessiert — und denk daran, dass .Value ein String ist:
n, ok := out.Attributes["Awards"].(*types.AttributeValueMemberN)
if ok {
awards, _ := strconv.Atoi(n.Value) // "1" -> 1
fmt.Println(awards)
}Lass das expression-Paket sie schreiben
Go ist das einzige SDK auf dieser Site, das die UpdateExpression für dich generiert. feature/dynamodb/expression setzt die Klauseln und beide Maps zusammen:
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()Was herauskommt, ist nicht das, was du geschrieben hast:
UpdateExpression: ADD #0 :0
SET #1 = :1, #2 = :2
Names: map[#0:Awards #1:Genre #2:Year]Der Builder hat die Klauseln umsortiert, sie mit einem Zeilenumbruch getrennt und die Platzhalter selbst nummeriert — #0 ist also Awards und nicht der erste Name, den du genannt hast. Nachgelagert stört das niemanden, aber die Strings sind über Änderungen hinweg nicht stabil, was sie zu einer schlechten Grundlage für Assertions in Tests macht. Reich expr.Update(), expr.Names() und expr.Values() direkt in UpdateItemInput und sieh sie dir nie an.
Der Vorteil ist, dass es jeden Namen aliasiert — reservierte Wörter sind damit keine Fehlerklasse mehr, die du ausliefern kannst. Schreibst du die Expression stattdessen von Hand, jag die Attributnamen vorher durch den DynamoDB-Reserved-Words-Checker — die AWS-Liste hat 573 Einträge, und Year, Name und Status stehen alle darauf. Und wenn du ein Item lieber als Daten ansiehst denn als Map von Interface-Pointern, lade DynoTable herunter.
Verwandte Beispiele
- DynamoDB UpdateItem in Java — dasselbe Update mit AWS SDK für Java 2.x.
- DynamoDB PutItem in Go — stattdessen das ganze Item ersetzen.
- DynamoDB Update Expressions —
SET,ADD,REMOVE,DELETEund Idiome. - ReturnValues verstehen — was dir jede
ReturnValues-Option liefert. - „Attribute name is a reserved keyword" — warum die Alias-Map hier nicht optional ist.
- „Invalid UpdateExpression"-Syntaxfehler — die häufigen SET-/ADD-Syntaxfehler, entschlüsselt.
Referenzen
- 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
Zuletzt verifiziert am 2026-07-28 gegen die oben verlinkte offizielle AWS-Dokumentation.