DynamoDB Update Expressions: SET, REMOVE, ADD, DELETE (mit Beispielen)
Eine Update Expression sagt UpdateItem, wie ein einzelnes Item verändert werden soll: welche
geschrieben, erhöht, gelöscht oder in ein Set gefaltet werden. Es gibt kein
UPDATE … SET … WHERE über mehrere Zeilen — du benennst ein einzelnes Item über seinen vollständigen Key und beschreibst die
Änderung mit vier Klausel-Keywords.
Wie funktionieren DynamoDB Update Expressions?
Eine DynamoDB Update Expression sagt UpdateItem über vier Klauseln, wie ein Item verändert werden soll. SET schreibt oder überschreibt ein . ADD erhöht atomar eine Zahl oder vereinigt in ein Set. REMOVE löscht ein Attribut oder ein einzelnes Listenelement. DELETE entfernt bestimmte Mitglieder aus einem Set. Ein Aufruf kann alle vier gleichzeitig tragen.
SETschreibt oder überschreibt ein Attribut — Skalare, Dokumente und die Funktions-Idiomeif_not_existsundlist_append.ADDerhöht eine Zahl atomar oder vereinigt in ein Set, in einem Round Trip, ohne vorheriges Lesen.REMOVElöscht ein Attribut komplett (oder ein einzelnes Listenelement über seinen Index).DELETEentfernt bestimmte Mitglieder aus einem Set — und nur aus einem Set.
Aus der SQL-Welt 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 danach, was du änderst
Ein UpdateItem-Aufruf kann alle vier Klauseln gleichzeitig tragen, in beliebiger Reihenfolge. Jedes
Keyword taucht höchstens einmal auf und nimmt eine kommagetrennte Liste von Aktionen entgegen.
| Klausel | Wirkt auf | Nimm sie, um |
|---|---|---|
SET | Jedes Attribut | einen Wert oder ein Dokumentfeld zu schreiben/überschreiben |
ADD | Nur Zahl oder Set | atomar zu erhöhen oder in ein Set zu vereinigen |
REMOVE | Jedes Attribut oder Listenelement | ein Attribut zu löschen; einen Listenindex zu entfernen |
DELETE | Nur Set | bestimmte Mitglieder aus einem Set zu entfernen |
ADD auf einem String und DELETE auf einem Skalar sind Validierungsfehler, keine No-Ops —
DynamoDB lehnt den ganzen Aufruf ab. Laut der
AWS-Referenz zu Update Expressions
ist ADD auf Zahlen und Sets beschränkt und DELETE auf Sets.
Das durchgerechnete Beispiel: ein Warenkorb
Ein Item pro Warenkorb, gekeyt über CartPK = "CART#c-9f21" und CartSK = "SUMMARY".
Es führt eine laufende OrderTotal, eine Liste LineItems, ein String-Set PromoCodes
und ein ItemCount.
SET — die Skalare und Dokumente schreiben
SET überschreibt, was auch immer da war. Füge im selben Aufruf eine Position zur Liste hinzu und erhöhe die
Summe:
SET OrderTotal = :total,
LineItems = list_append(LineItems, :newItem),
UpdatedAt = :now
list_append(LineItems, :newItem) hängt hinten an; dreh die Argumente um —
list_append(:newItem, LineItems) —, um vorn anzuhängen. Die Reihenfolge der Argumente ist die
Reihenfolge der Verkettung, mehr nicht.
In diesem ersten Aufruf steckt ein Footgun: Ist der Warenkorb brandneu, existiert LineItems
noch nicht, und list_append auf einem fehlenden Attribut schlägt fehl. Sichere das mit
if_not_exists ab:
SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem)
if_not_exists(LineItems, :empty) gibt die aktuelle Liste zurück, wenn sie existiert, sonst den
Rückfallwert :empty (eine leere Liste []). Damit nutzen der erste und jeder weitere
Hinzufügen-Vorgang denselben Ausdruck — ein echter Grund, warum es diese Idiome gibt.
ADD — die Anzahl atomar erhöhen
Um ItemCount zu erhöhen, lies ihn nicht aus, addier eins in deinem Code und schreib ihn per SET zurück.
Das ist ein Lost-Update-Race: Zwei nebenläufige Additionen lesen beide 3, schreiben beide 4, und
du hast eine verloren. ADD rechnet serverseitig:
ADD ItemCount :one
Mit :one = 1 ist das ein atomarer Zähler. Nebenläufige Aufrufe serialisieren auf dem
Item, zwei Additionen landen also als +2. Übergib eine negative Zahl, um zu verringern. Fehlt
ItemCount, behandelt ADD ihn zuerst als 0 — du musst den Zähler also nie vorbelegen.
Bei On-Demand in us-east-1 berechnet jedes ADD 1 WCU pro KB des Items nach
dem Schreibvorgang (aufgerundet). Eine 1 KB große Warenkorb-Zusammenfassung mit einem ADD ItemCount :one kostet
1 WCU — dieselbe Zeile mit einem Read-Modify-Write per SET würde dieselben WCU kosten,
aber unter Nebenläufigkeit verlieren. Dimensioniere das Item im
Item-Size-Rechner.
Genau diesen Ausdruck — Namen, typisierte Werte und den gemarshallten
Request — kannst du im
DynamoDB Expression Builder bauen, ohne einen einzigen
#name- oder :value-Platzhalter von Hand zu escapen.
Bearbeite das Preset unten — ein SET plus ein atomares ADD — und sieh zu, wie sich die
UpdateExpression dabei neu aufbaut:
REMOVE — ein Attribut oder eine Position entfernen
REMOVE ist der Weg, ein Attribut vollständig zu löschen (ein „setz es auf null“ gibt es nicht —
das schreibt nur einen Typ NULL). Entferne einen angewendeten Rabatt und die dritte
Position in einem Aufruf:
REMOVE AppliedDiscount, LineItems[2]
LineItems[2] entfernt das Element an Index 2 und schiebt alles danach
nach unten — Index 3 wird zu 2 und so weiter. Entfernst du zwei Indizes in einem
Ausdruck, werden beide gegen die ursprüngliche Liste ausgewertet: [2]
und [3] gemeinsam zu entfernen streicht also, wie erwartet, das dritte und vierte Element.
DELETE — Set-Mitglieder entfernen
PromoCodes ist ein String-Set, ein Kunde, der einen Code zurückzieht, nutzt also DELETE, nicht
REMOVE. REMOVE PromoCodes würde das ganze Set plattmachen; DELETE zieht die
genannten Mitglieder ab:
DELETE PromoCodes :pulled
Mit :pulled = dem Set {"SAVE10"} verschwindet nur dieses Mitglied. Zwei Regeln beißen
hier: Ein Set darf nie leer sein, das Löschen des letzten Mitglieds entfernt also das
Attribut PromoCodes ganz; und der Wert muss ein Set-Typ sein, der zum Attribut passt —
ein nackter String ist ein Typfehler.
Alles zusammensetzen
Ein Update im Sinne von „Position hinzufügen, Promo anwenden, Anzahl erhöhen“ 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 arbeitet auf dem
bestehenden Wert. Sie ist genauso atomar und race-sicher wie ADD: DynamoDB wertet
OrderTotal + :price serverseitig gegen den aktuellen Wert aus, nebenläufige Aufrufe
serialisieren also auf dem Item, statt einen Umweg durch deinen Code zu nehmen.
Fallstricke, die du vermeiden solltest
- Einen Zähler per
SETschreiben, den du vorher gelesen hast. NimmADD— Read-Modify-Write verliert Updates unter Nebenläufigkeit. Das ist der häufigste Warenkorb-/Bestandsfehler. list_appendauf einer fehlenden Liste. Wickle das Ziel inif_not_exists, sonst schlägt der erste Schreibvorgang fehl.REMOVEundDELETEverwechseln.REMOVEentfernt das Attribut;DELETEzieht Mitglieder aus einem Set ab. Sie zu vermischen löscht mehr, als du wolltest.- Vergessen, dass
UpdateItemein Upsert ist. Existiert der Key nicht, legt es das Item an. Nimm eineConditionExpression(attribute_exists(CartPK)), wenn du „nur aktualisieren“ meinst.
Zum Modellieren der Keys, gegen die diese Ausdrücke laufen, siehe Single-Table-Design; um zu entscheiden, wie du den Warenkorb zurücklesen willst, siehe Query vs. Scan.
Baue und kopiere alle diese Ausdrücke im Expression Builder und probier dann DynoTable aus, um sie gegen deine eigenen Tabellen laufen zu lassen und dem Item live beim Ändern zuzusehen.