DynamoDB Update Expressions
Eine Update Expression sagt UpdateItem, wie ein einzelnes Item zu mutieren ist:
welche Attribute zu schreiben, zu inkrementieren, zu löschen oder in ein Set zu
falten sind. Es gibt kein UPDATE … SET … WHERE — du benennst das Item über
seinen Key und beschreibst die Änderung mit vier Klausel-Schlüsselwörtern.
Wie funktionieren DynamoDB Update Expressions?
Eine DynamoDB Update Expression sagt UpdateItem, wie ein Item mit vier Klauseln mutiert werden soll. SET schreibt oder überschreibt ein Attribut. ADD inkrementiert atomar eine Zahl oder vereinigt in ein Set. REMOVE löscht ein Attribut oder ein Listenelement. DELETE entfernt bestimmte Mitglieder aus einem Set. Ein Aufruf kann alle vier auf einmal tragen.
SETschreibt oder überschreibt ein Attribut — Skalare, Dokumente und die Funktions-Idiomeif_not_existsundlist_append.ADDmacht eine atomare Zahlen-Inkrementierung oder eine Set-Vereinigung, in einem Round Trip, ohne vorheriges Lesen.REMOVElöscht ein Attribut komplett (oder ein einzelnes Listenelement per Index).DELETEentfernt bestimmte Mitglieder aus einem Set — und nur aus einem Set.
Aus SQL kommend ist die Falle, für alles zu SET zu greifen. ADD und DELETE
existieren, weil Read-modify-write auf einem Zähler oder einem Set ein Race ist,
das du unter Nebenläufigkeit verlierst.
Wähle die Klausel nach dem, was du änderst
Ein UpdateItem-Aufruf kann alle vier Klauseln auf einmal tragen, in der
Reihenfolge SET … REMOVE … ADD … DELETE. Jedes Schlüsselwort erscheint höchstens
einmal und nimmt eine kommagetrennte Liste von Actions.
| Klausel | Wirkt auf | Verwende es, um |
|---|---|---|
SET | Jedes Attribut | Einen Wert oder ein Dokumentfeld schreiben/überschreiben |
ADD | Nur Zahl oder Set | Atomar inkrementieren oder in ein Set vereinigen |
REMOVE | Jedes Attribut oder Listenelement | Ein Attribut löschen; einen Listenindex droppen |
DELETE | Nur Set | Bestimmte Mitglieder aus einem Set entfernen |
ADD auf einem String und DELETE auf einem Skalar sind Validierungsfehler, keine
No-ops — DynamoDB lehnt den ganzen Aufruf ab. Laut der
AWS-Update-Expression-Referenz
ist ADD auf Zahlen und Sets beschränkt und DELETE auf Sets.
Das durchgespielte Beispiel: ein Warenkorb
Ein Item pro Warenkorb, gekeyt mit CartPK = "CART#c-9f21" und
CartSK = "SUMMARY". Es verfolgt eine laufende OrderTotal, eine
LineItems-Liste, ein PromoCodes-String-Set und einen ItemCount.
SET — schreib die Skalare und Dokumente
SET überschreibt, was immer da war. Füge ein Line-Item zur Liste hinzu und
erhöhe die Summe im selben Aufruf:
SET OrderTotal = :total,
LineItems = list_append(LineItems, :newItem),
UpdatedAt = :now
list_append(LineItems, :newItem) hängt ans Ende an; dreh die Argumente um —
list_append(:newItem, LineItems) — um voranzustellen. Die Argumentreihenfolge ist
die Verkettungsreihenfolge, nicht mehr.
In diesem ersten Aufruf steckt eine Falle: wenn der Warenkorb brandneu ist,
existiert LineItems noch nicht, und list_append auf einem fehlenden Attribut
scheitert. Sichere es mit if_not_exists:
SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem)
if_not_exists(LineItems, :empty) gibt die aktuelle Liste zurück, falls vorhanden,
sonst den Fallback :empty (eine leere Liste []). Das lässt das erste Hinzufügen
und jedes spätere dieselbe Expression verwenden — ein echter Grund, warum diese
Idiome existieren.
ADD — den Count atomar inkrementieren
Um ItemCount zu erhöhen, lies ihn nicht, addiere in deinem Code eins und
SET ihn zurück. Das ist ein Lost-Update-Race: zwei gleichzeitige Adds lesen
beide 3, schreiben beide 4, und du hast einen verloren. ADD macht die
Arithmetik serverseitig:
ADD ItemCount :one
Mit :one = 1 ist das ein atomarer Zähler. Gleichzeitige Aufrufe serialisieren
auf dem Item, sodass zwei Adds als +2 landen. Übergib eine negative Zahl zum
Dekrementieren. Wenn ItemCount abwesend ist, behandelt ADD es zuerst als 0 —
sodass du den Zähler nie seeden musst.
Du kannst diese exakte Expression — Namen, getypte Werte und die marshalled
Request — im
DynamoDB Expression Builder bauen, ohne einen
einzigen #name- oder :value-Platzhalter von Hand zu escapen.
REMOVE — ein Attribut oder ein Line-Item droppen
REMOVE ist, wie du ein Attribut komplett löschst (es gibt kein "setz es auf
null" — das schreibt nur einen NULL-Typ). Lösch einen angewandten Rabatt und
drop das dritte Line-Item in einem Aufruf:
REMOVE AppliedDiscount, LineItems[2]
LineItems[2] entfernt das Element an Index 2 und schiebt alles danach nach
unten — Index 3 wird 2, und so weiter. Wenn du zwei Indizes in einer Expression
REMOVEst, werden beide gegen die ursprüngliche Liste ausgewertet, sodass das
Entfernen von [2] und [3] zusammen das dritte und vierte Element droppt, wie du
es erwarten würdest.
DELETE — Set-Mitglieder entfernen
PromoCodes ist ein String-Set, also verwendet ein Kunde, der einen Code zieht,
DELETE, nicht REMOVE. REMOVE PromoCodes würde das ganze Set zerlegen;
DELETE subtrahiert die genannten Mitglieder:
DELETE PromoCodes :pulled
Mit :pulled = dem Set {"SAVE10"} geht nur dieses Mitglied. Zwei Regeln beißen
hier: ein Set kann nie leer sein, also entfernt das Löschen des letzten Mitglieds
das PromoCodes-Attribut komplett; und der Wert muss ein Set-Typ sein, der dem
Attribut entspricht — ein bloßer String ist ein Typfehler.
Setz es zusammen
Ein "Item hinzufügen, Promo anwenden, Count erhöhen"-Update ist ein Aufruf über drei Klauseln:
SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem),
OrderTotal = OrderTotal + :price
ADD ItemCount :one
DELETE PromoCodes :expiredCode
Beachte OrderTotal = OrderTotal + :price — Arithmetik innerhalb von SET wirkt
auf dem bestehenden Wert. Sie ist nicht race-sicher atomar wie ADD, aber sie
liest die aktuelle Summe serverseitig, statt sie durch deinen Code zu
round-trippen.
Zu vermeidende Fallstricke
- Einen Zähler
SETen, den du zuerst liest. VerwendeADD— Read-modify-write verliert Updates unter Nebenläufigkeit. Das ist der häufigste Warenkorb-/Inventar-Bug. list_appendauf einer fehlenden Liste. Wickle das Ziel inif_not_exists, sonst scheitert der erste Write.REMOVEundDELETEverwechseln.REMOVEdroppt das Attribut;DELETEsubtrahiert Mitglieder aus einem Set. Sie zu mischen löscht mehr, als du meintest.- Vergessen, dass
UpdateItemein Upsert ist. Wenn der Key nicht existiert, legt es das Item an. Verwende eineConditionExpression(attribute_exists(CartPK)), wenn du "nur aktualisieren" meinst.
Zum Modellieren der Keys, gegen die diese Expressions laufen, siehe Single-Table Design; um zu entscheiden, wie du den Warenkorb zurückliest, siehe Query vs. Scan.
Baue und kopiere jede davon im Expression Builder, dann probier DynoTable, um sie gegen deine eigenen Tabellen auszuführen und zuzusehen, wie sich das Item live ändert.