Intermedio5 min di lettura

Update expression in DynamoDB

Una update expression dice a UpdateItem come mutare un singolo item: quali attributi scrivere, incrementare, eliminare o fondere in un set. Non c'è UPDATE … SET … WHERE — nomini l'item tramite la sua chiave e descrivi il cambiamento con quattro parole chiave di clausola.

Come funzionano le update expression di DynamoDB?

Una update expression di DynamoDB dice a UpdateItem come mutare un item usando quattro clausole. SET scrive o sovrascrive un . ADD incrementa atomicamente un numero o unisce in un set. REMOVE elimina un attributo o un elemento di lista. DELETE rimuove membri specifici da un set. Una sola chiamata può portare tutte e quattro insieme.

  • SET scrive o sovrascrive un attributo — scalari, documenti e gli idiomi funzione if_not_exists e list_append.
  • ADD fa un incremento numerico atomico o un'unione di set, in un solo round trip, senza leggere prima.
  • REMOVE elimina del tutto un attributo (o un singolo elemento di lista per indice).
  • DELETE rimuove membri specifici da un set — e solo da un set.

Venendo da SQL, la trappola è ricorrere a SET per tutto. ADD e DELETE esistono perché il read-modify-write su un contatore o un set è una gara che perderai sotto concorrenza.

Scegli la clausola in base a cosa stai cambiando

Una sola chiamata UpdateItem può portare tutte e quattro le clausole insieme, nell'ordine SET … REMOVE … ADD … DELETE. Ogni parola chiave compare al massimo una volta e prende un elenco di azioni separate da virgola.

ClausolaFunziona suUsala per
SETQualsiasi attributoScrivere/sovrascrivere un valore o un campo documento
ADDSolo Number o SetIncrementare atomicamente, o unire in un set
REMOVEQualsiasi attributo o elemento di listaEliminare un attributo; togliere un indice di lista
DELETESolo SetRimuovere membri specifici da un set

ADD su una stringa e DELETE su uno scalare sono errori di validazione, non no-op — DynamoDB rifiuta l'intera chiamata. Secondo il riferimento update-expression di AWS, ADD è ristretto a numeri e set, e DELETE ai set.

L'esempio pratico: un carrello della spesa

Un item per carrello, con chiave CartPK = "CART#c-9f21" e CartSK = "SUMMARY". Traccia un OrderTotal corrente, una lista LineItems, un set di stringhe PromoCodes e un ItemCount.

SET — scrivi gli scalari e i documenti

SET sovrascrive qualunque cosa ci fosse. Aggiungi un line item alla lista e incrementa il totale nella stessa chiamata:

SET OrderTotal = :total,
LineItems = list_append(LineItems, :newItem),
UpdatedAt = :now

list_append(LineItems, :newItem) aggiunge in coda; inverti gli argomenti — list_append(:newItem, LineItems) — per anteporre. L'ordine degli argomenti è l'ordine di concatenazione, nient'altro.

C'è un footgun in quella prima chiamata: se il carrello è nuovissimo, LineItems non esiste ancora, e list_append su un attributo mancante fallisce. Proteggilo con if_not_exists:

SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem)

if_not_exists(LineItems, :empty) restituisce la lista corrente se presente, altrimenti il fallback :empty (una lista vuota []). Ciò fa sì che la prima aggiunta e ogni aggiunta successiva usino la stessa espressione — una vera ragione per cui questi idiomi esistono.

ADD — incrementa il conteggio, atomicamente

Per incrementare ItemCount, non leggerlo, aggiungere uno nel tuo codice e fare SET per riscriverlo. È una gara di update persi: due aggiunte concorrenti leggono entrambe 3, scrivono entrambe 4, e ne hai persa una. ADD fa l'aritmetica lato server:

ADD ItemCount :one

Con :one = 1, questo è un contatore atomico. Le chiamate concorrenti si serializzano sull'item, così due aggiunte atterrano come +2. Passa un numero negativo per decrementare. Se ItemCount è assente, ADD lo tratta come 0 prima — così non devi mai inizializzare il contatore.

Puoi costruire questa espressione esatta — nomi, valori tipizzati e la richiesta marshalled — nel DynamoDB expression builder senza fare l'escape a mano di un singolo placeholder #name o :value.

REMOVE — elimina un attributo o un line item

REMOVE è il modo in cui elimini del tutto un attributo (non c'è un "impostalo a null" — quello scrive solo un tipo NULL). Azzera uno sconto applicato e togli il terzo line item in una sola chiamata:

REMOVE AppliedDiscount, LineItems[2]

LineItems[2] rimuove l'elemento all'indice 2 e fa scorrere tutto ciò che lo segue verso il basso — l'indice 3 diventa 2, e così via. Se fai REMOVE di due indici in un'espressione, entrambi sono valutati contro la lista originale, quindi rimuovere [2] e [3] insieme toglie il terzo e il quarto elemento come ti aspetteresti.

DELETE — rimuovi membri da un set

PromoCodes è un set di stringhe, quindi un cliente che toglie un codice usa DELETE, non REMOVE. REMOVE PromoCodes annienterebbe l'intero set; DELETE sottrae i membri nominati:

DELETE PromoCodes :pulled

Con :pulled = il set {"SAVE10"}, va via solo quel membro. Due regole mordono qui: un set non può mai essere vuoto, quindi eliminare l'ultimo membro rimuove del tutto l'attributo PromoCodes; e il valore deve essere di tipo set corrispondente all'attributo — una stringa nuda è un errore di tipo.

Mettila insieme

Un update "aggiungi item, applica un promo, incrementa il conteggio" è una sola chiamata attraverso tre clausole:

SET LineItems = list_append(if_not_exists(LineItems, :empty), :newItem),
OrderTotal = OrderTotal + :price
ADD ItemCount :one
DELETE PromoCodes :expiredCode

Nota OrderTotal = OrderTotal + :price — l'aritmetica dentro SET opera sul valore esistente. Non è atomica come lo è ADD per la sicurezza dalle gare, ma legge il totale corrente lato server invece di farlo passare per il tuo codice.

Trappole da evitare

  • Fare SET su un contatore che leggi prima. Usa ADD — il read-modify-write perde update sotto concorrenza. È il bug più comune di carrello/inventario.
  • list_append su una lista mancante. Avvolgi il target in if_not_exists o la prima scrittura fallisce.
  • Confondere REMOVE e DELETE. REMOVE toglie l'attributo; DELETE sottrae membri da un set. Mescolarli elimina più di quanto intendevi.
  • Dimenticare che UpdateItem è un upsert. Se la chiave non esiste, crea l'item. Usa una ConditionExpression (attribute_exists(CartPK)) quando intendi "aggiorna solo".

Per modellare le chiavi contro cui girano queste espressioni, vedi single-table design; per decidere come leggerai indietro il carrello, vedi query vs scan.

Costruisci e copia una qualsiasi di queste nell'expression builder, poi prova DynoTable per eseguirle contro le tue tabelle e guardare l'item cambiare dal vivo.

Aggiornato