Les update expressions DynamoDB
Une update expression dit à UpdateItem comment muter un seul item : quels attributs
écrire, incrémenter, supprimer, ou fondre dans un ensemble. Il n'y a pas de
UPDATE … SET … WHERE — tu nommes l'item par sa clé et tu décris le changement avec
quatre mots-clés de clause.
Comment fonctionnent les update expressions DynamoDB ?
Une update expression DynamoDB dit à UpdateItem comment muter un item en utilisant quatre clauses. SET écrit ou écrase un attribut. ADD incrémente atomiquement un nombre ou fait l'union dans un ensemble. REMOVE supprime un attribut ou un seul élément de liste. DELETE retire des membres précis d'un ensemble. Un seul appel peut porter les quatre à la fois.
SETécrit ou écrase un attribut — scalaires, documents, et les idiomes de fonctionif_not_existsetlist_append.ADDfait un incrément de nombre atomique ou une union d'ensemble, en un seul aller-retour, sans lecture préalable.REMOVEsupprime un attribut purement et simplement (ou un seul élément de liste par index).DELETEretire des membres précis d'un ensemble — et seulement d'un ensemble.
Venant de SQL, le piège est de sortir SET pour tout. ADD et DELETE existent
parce que le read-modify-write sur un compteur ou un ensemble est une course que tu
perdras sous concurrence.
Choisis la clause selon ce que tu changes
Un seul appel UpdateItem peut porter les quatre clauses à la fois, dans l'ordre
SET … REMOVE … ADD … DELETE. Chaque mot-clé apparaît au plus une fois et prend une
liste d'actions séparées par des virgules.
| Clause | Agit sur | Sert à |
|---|---|---|
SET | N'importe quel attribut | Écrire/écraser une valeur ou un champ de document |
ADD | Nombre ou Ensemble uniquement | Incrémenter atomiquement, ou unir dans un ensemble |
REMOVE | N'importe quel attribut ou élément de liste | Supprimer un attribut ; retirer un index de liste |
DELETE | Ensemble uniquement | Retirer des membres précis d'un ensemble |
ADD sur une chaîne et DELETE sur un scalaire sont des erreurs de validation, pas
des no-ops — DynamoDB rejette tout l'appel. D'après la
référence des update expressions d'AWS,
ADD est restreint aux nombres et ensembles, et DELETE aux ensembles.
L'exemple travaillé : un panier d'achat
Un item par panier, clé par CartPK = "CART#c-9f21" et CartSK = "SUMMARY". Il suit
un OrderTotal courant, une liste LineItems, un string set PromoCodes et un
ItemCount.
SET — écrire les scalaires et les documents
SET écrase ce qui était là. Ajoute un article à la liste et augmente le total dans
le même appel :
SET OrderTotal = :total,
LineItems = list_append(LineItems, :newItem),
UpdatedAt = :now
list_append(LineItems, :newItem) ajoute à la queue ; inverse les arguments —
list_append(:newItem, LineItems) — pour préfixer. L'ordre des arguments est l'ordre
de concaténation, rien de plus.
Il y a une arme à fragmentation dans ce premier appel : si le panier est tout neuf,
LineItems n'existe pas encore, et list_append sur un attribut manquant échoue.
Protège-le avec if_not_exists :
SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem)
if_not_exists(LineItems, :empty) renvoie la liste courante si présente, sinon le
repli :empty (une liste vide []). Ça fait que le premier ajout et chaque ajout
ultérieur utilisent la même expression — une vraie raison d'être de ces idiomes.
ADD — incrémenter le compte, atomiquement
Pour augmenter ItemCount, ne le lis pas, n'ajoute pas un dans ton code, et ne le
SET pas en retour. C'est une course de mise à jour perdue : deux ajouts concurrents
lisent tous deux 3, écrivent tous deux 4, et tu en as perdu un. ADD fait
l'arithmétique côté serveur :
ADD ItemCount :one
Avec :one = 1, c'est un compteur atomique. Les appels concurrents se sérialisent
sur l'item, donc deux ajouts atterrissent comme +2. Passe un nombre négatif pour
décrémenter. Si ItemCount est absent, ADD le traite comme 0 d'abord — donc tu
n'as jamais besoin d'amorcer le compteur.
Tu peux construire cette expression exacte — noms, valeurs typées, et la requête
marshalled — dans le
DynamoDB expression builder sans échapper à la
main un seul placeholder #name ou :value.
REMOVE — retirer un attribut ou un article
REMOVE est ainsi que tu supprimes un attribut entièrement (il n'y a pas de « le
mettre à null » — ça écrit juste un type NULL). Efface une remise appliquée et
retire le troisième article en un seul appel :
REMOVE AppliedDiscount, LineItems[2]
LineItems[2] retire l'élément à l'index 2 et décale tout ce qui suit vers le bas
— l'index 3 devient 2, et ainsi de suite. Si tu REMOVE deux indices dans une seule
expression, les deux sont évalués contre la liste originale, donc retirer [2] et
[3] ensemble supprime le troisième et le quatrième élément comme tu t'y attendrais.
DELETE — retirer des membres d'ensemble
PromoCodes est un string set, donc un client qui retire un code utilise DELETE,
pas REMOVE. REMOVE PromoCodes annihilerait tout l'ensemble ; DELETE soustrait
les membres nommés :
DELETE PromoCodes :pulled
Avec :pulled = l'ensemble {"SAVE10"}, seul ce membre s'en va. Deux règles mordent
ici : un ensemble ne peut jamais être vide, donc supprimer le dernier membre retire
l'attribut PromoCodes purement et simplement ; et la valeur doit être un type
ensemble correspondant à l'attribut — une simple chaîne est une erreur de type.
Mets-le tout ensemble
Une mise à jour « ajouter un article, appliquer une promo, augmenter le compte » est un seul appel à travers trois clauses :
SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem),
OrderTotal = OrderTotal + :price
ADD ItemCount :one
DELETE PromoCodes :expiredCode
Note OrderTotal = OrderTotal + :price — l'arithmétique à l'intérieur de SET
travaille sur la valeur existante. Ce n'est pas atomique comme l'est ADD pour la
sécurité aux courses, mais ça lit le total courant côté serveur plutôt que de le faire
transiter par ton code.
Pièges à éviter
SET-er un compteur que tu lis d'abord. UtiliseADD— le read-modify-write perd des mises à jour sous concurrence. C'est le bug de panier/inventaire le plus courant.list_appendsur une liste manquante. Enveloppe la cible dansif_not_existsou la première écriture échoue.- Confondre
REMOVEetDELETE.REMOVEretire l'attribut ;DELETEsoustrait des membres d'un ensemble. Les mélanger supprime plus que tu ne voulais. - Oublier que
UpdateItemest un upsert. Si la clé n'existe pas, il crée l'item. Utilise uneConditionExpression(attribute_exists(CartPK)) quand tu veux dire « mettre à jour seulement ».
Pour modéliser les clés contre lesquelles ces expressions s'exécutent, vois single-table design ; pour décider comment tu reliras le panier, vois query vs scan.
Construis et copie n'importe laquelle d'entre elles dans l'expression builder, puis essaie DynoTable pour les exécuter contre tes propres tables et regarder l'item changer en direct.