Intermédiaire6 min de lecture

Update Expressions DynamoDB : SET, REMOVE, ADD, DELETE (avec exemples)

Une expression de mise à jour indique à UpdateItem comment modifier un seul item : quels écrire, incrémenter, supprimer ou fusionner dans un ensemble. Il n'existe pas de UPDATE … SET … WHERE multi-lignes — tu nommes un unique item par sa clé complète et tu décris le changement avec quatre mots-clés de clause.

Comment fonctionnent les expressions de mise à jour DynamoDB ?

Une expression de mise à jour DynamoDB indique à UpdateItem comment modifier un item à l'aide de quatre clauses. SET écrit ou remplace un . ADD incrémente atomiquement un nombre ou fait une union dans un ensemble. REMOVE supprime un attribut ou un é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 remplace un attribut — scalaires, documents, et les idiomes de fonction if_not_exists et list_append.
  • ADD effectue une incrémentation atomique de nombre ou une union d'ensemble, en un seul aller-retour, sans lecture préalable.
  • REMOVE supprime un attribut pur et simple (ou un unique élément de liste par index).
  • DELETE retire des membres précis d'un ensemble — et seulement d'un ensemble.

Quand tu viens de SQL, le piège est de te tourner vers SET pour tout. ADD et DELETE existent parce que le lire-modifier-écrire sur un compteur ou un ensemble est une compétition que tu perdras en cas de concurrence.

Choisis la clause selon ce que tu changes

Un seul appel UpdateItem peut porter les quatre clauses à la fois, dans n'importe quel ordre. Chaque mot-clé apparaît au plus une fois et prend une liste d'actions séparées par des virgules.

ClauseAgit surÀ utiliser pour
SETN'importe quel attributÉcrire/remplacer une valeur ou un champ de document
ADDNombre ou ensemble uniquementIncrémenter atomiquement, ou faire une union 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 non-opérations — DynamoDB rejette tout l'appel. Selon la référence des expressions de mise à jour AWS, ADD est restreint aux nombres et aux ensembles, et DELETE aux ensembles.

L'exemple concret : un panier d'achat

Un item par panier, clé sur CartPK = "CART#c-9f21" et CartSK = "SUMMARY". Il suit un OrderTotal courant, une liste LineItems, un ensemble de chaînes PromoCodes et un ItemCount.

SET — écrire les scalaires et les documents

SET remplace 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 fin ; inverse les arguments — list_append(:newItem, LineItems) — pour ajouter au début. L'ordre des arguments est l'ordre de concaténation, rien de plus.

Il y a un piège 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 elle est présente, sinon la valeur de repli :empty (une liste vide []). Cela 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 compteur, atomiquement

Pour augmenter ItemCount, ne le lis pas, n'ajoute pas un dans ton code, et ne le réécris pas avec SET. C'est une compétition avec perte de mise à jour : 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, si bien que deux ajouts atterrissent en +2. Passe un nombre négatif pour décrémenter. Si ItemCount est absent, ADD le traite d'abord comme 0 — tu n'as donc jamais besoin d'initialiser le compteur.

En on-demand dans us-east-1, chaque ADD facture 1 WCU par Ko de l'item après l'écriture (arrondi au supérieur). Un résumé de panier de 1 Ko avec un seul ADD ItemCount :one coûte 1 WCU — la même ligne en lecture-modification-écriture avec SET coûterait le même WCU mais perdrait sous concurrence. Dimensionne l'item dans le calculateur de taille d'élément.

Tu peux construire exactement cette expression — noms, valeurs typées et requête marshalled — dans le générateur d'expressions DynamoDB sans échapper à la main un seul placeholder #name ou :value.

Modifie le préréglage ci-dessous — un SET plus un ADD atomique — et regarde l'UpdateExpression se reconstruire au fur et à mesure :

Construis ta requête
Code généré
new UpdateItemCommand({
  "TableName": "AuditLog",
  "Key": {
    "pk": {
      "S": "TENANT#acme"
    },
    "sk": {
      "S": "CONFIG"
    }
  },
  "UpdateExpression": "SET #upd0 = :updValue0 ADD #upd1 :updValue1",
  "ExpressionAttributeNames": {
    "#upd0": "plan",
    "#upd1": "seats"
  },
  "ExpressionAttributeValues": {
    ":updValue0": {
      "S": "pro"
    },
    ":updValue1": {
      "N": "5"
    }
  }
})

REMOVE — retirer un attribut ou un article

REMOVE est la façon de supprimer un attribut entièrement (il n'y a pas de « le mettre à null » — cela é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 fais un REMOVE de deux index dans une même expression, les deux sont évalués contre la liste d'origine, si bien que retirer [2] et [3] ensemble supprime le troisième et le quatrième éléments comme prévu.

DELETE — retirer des membres d'ensemble

PromoCodes est un ensemble de chaînes, donc un client qui retire un code utilise DELETE, pas REMOVE. REMOVE PromoCodes anéantirait 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, si bien que supprimer le dernier membre retire l'attribut PromoCodes pur et simple ; et la valeur doit être un type ensemble correspondant à l'attribut — une simple chaîne est une erreur de type.

Assemble le tout

Une mise à jour « ajouter un article, appliquer une promo, augmenter le compteur » est un seul appel réparti sur 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 dans SET opère sur la valeur existante. C'est tout aussi atomique et à l'abri des compétitions qu'ADD : DynamoDB évalue OrderTotal + :price côté serveur contre la valeur courante, si bien que les appels concurrents se sérialisent sur l'item au lieu de faire l'aller-retour par ton code.

Pièges à éviter

  • Faire un SET d'un compteur que tu lis d'abord. Utilise ADD — le lire-modifier-écrire perd des mises à jour en cas de concurrence. C'est le bug de panier/inventaire le plus courant.
  • list_append sur une liste manquante. Enveloppe la cible dans if_not_exists sinon 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 le voulais.
  • Oublier qu'UpdateItem est un upsert. Si la clé n'existe pas, il crée l'item. Utilise un ConditionExpression (attribute_exists(CartPK)) quand tu veux dire « mettre à jour uniquement ».

Pour modéliser les clés contre lesquelles ces expressions s'exécutent, voir conception à table unique ; pour décider comment tu reliras le panier, voir query vs scan.

Construis et copie n'importe laquelle de celles-ci dans le générateur d'expressions, puis essaie DynoTable pour les exécuter contre tes propres tables et voir l'item changer en direct.

Mis à jour