Intermédiaire5 min de lecture

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 fonction if_not_exists et list_append.
  • ADD fait un incrément de nombre atomique ou une union d'ensemble, en un seul aller-retour, sans lecture préalable.
  • REMOVE supprime un attribut purement et simplement (ou un seul élément de liste par index).
  • DELETE retire 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.

ClauseAgit surSert à
SETN'importe quel attributÉcrire/écraser une valeur ou un champ de document
ADDNombre ou Ensemble uniquementIncrémenter atomiquement, ou unir dans un ensemble
REMOVEN'importe quel attribut ou élément de listeSupprimer un attribut ; retirer un index de liste
DELETEEnsemble uniquementRetirer 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. Utilise ADD — le read-modify-write perd des mises à jour sous concurrence. C'est le bug de panier/inventaire le plus courant.
  • list_append sur une liste manquante. Enveloppe la cible dans if_not_exists ou la première écriture échoue.
  • Confondre REMOVE et DELETE. REMOVE retire l'attribut ; DELETE soustrait des membres d'un ensemble. Les mélanger supprime plus que tu ne voulais.
  • Oublier que UpdateItem est un upsert. Si la clé n'existe pas, il crée l'item. Utilise une ConditionExpression (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.

Mis à jour