Intermedio5 min di lettura

Expression attribute name e value in DynamoDB

Le espressioni DynamoDB sono template: scrivi placeholder, poi fornisci i nomi di attributo e i valori reali in due mappe a parte. #name è un placeholder di nome; :value è un placeholder di valore. Confondi i due e DynamoDB rifiuta l'intera chiamata.

Qual è la differenza tra #name e :value in DynamoDB?

#name è un placeholder per un nome di attributo, fornito tramite ExpressionAttributeNames; :value è un placeholder per un valore di attributo, fornito tramite ExpressionAttributeValues. Usa #name per schivare parole riservate, punti o spazi, e :value per ogni literal — DynamoDB non incorpora mai i valori. Non sono intercambiabili; scambiarli scatena una ValidationException.

  • #name sostituisce un nome di attributo tramite ExpressionAttributeNames — usalo ogni volta che un attributo si scontra con una parola riservata o contiene un punto/spazio.
  • :value sostituisce un valore tramite ExpressionAttributeValues — DynamoDB non incorpora mai i literal nel testo dell'espressione, quindi ogni valore è un placeholder.
  • Non sono intercambiabili. Un # dove dovrebbe esserci un : è una ValidationException, non un no-op silenzioso.

Venendo da SQL, incorpori entrambi — WHERE status = 'published'. DynamoDB non incorpora nessuno dei due. Quella separazione è ciò che fa inciampare ogni neofita.

Perché esistono le due mappe

In SQL la stringa della query porta tutto: nomi di colonne, literal, operatori. DynamoDB separa deliberatamente la forma dell'espressione dai suoi dati.

I valori vanno nella loro mappa così che DynamoDB possa tipizzare ciascuno (S, N, BOOL, …) e così che il parser non debba mai indovinare dove finisce una stringa — non c'è quoting o escape da sbagliare. Vedi i tipi di dati in DynamoDB per l'elenco completo dei type-tag.

I nomi ricevono lo stesso trattamento per una ragione diversa: DynamoDB ha una lunga lista di parole riservate, e qualsiasi attributo che ne corrisponde una non può comparire come nome nudo in un'espressione. Il placeholder schiva del tutto la riserva.

La trappola delle parole riservate

Ecco una tabella di articoli CMS — partition key BLOG#<blog>, sort key ARTICLE#<slug> — i cui attributi si leggono naturalmente ma si scontrano con parole riservate:

AttributoRiservato?Cosa contiene
statusdraft / published
namenome visualizzato autore
sizelunghezza in byte renderizzata
ttlscadenza archivio (epoch)
slugnoslug URL

status, name, size e ttl sono tutte nella lista delle parole riservate di AWS, quindi questo filtro fallisce sulla prima parola:

FilterExpression  status = :s

DynamoDB restituisce una ValidationException"Attribute name is a reserved keyword; reserved keyword: status". La soluzione è un name placeholder, mai rinominare l'attributo:

FilterExpression           #status = :s
ExpressionAttributeNames   { "#status": "status" }
ExpressionAttributeValues  { ":s": { "S": "published" } }

La trappola: slug non è riservata, quindi una query che hai testato contro slug funziona, e dai per scontato che la prossima farà lo stesso. Poi status la rompe. La lista completa si muove, quindi non memorizzarla — metti placeholder su ogni nome e non ti morderà mai.

Mappa ogni valore, sempre

I valori sono irrinunciabili: non c'è sintassi per un literal inline. Anche un semplice numero ottiene un placeholder. Questo update segna un articolo come pubblicato, ne timbra la dimensione e imposta un TTL di archivio a 30 giorni:

UpdateExpression:          SET #status = :s, #size = :sz, #ttl = :exp
ExpressionAttributeNames:  { "#status": "status", "#size": "size", "#ttl": "ttl" }
ExpressionAttributeValues: {
  ":s":   { "S": "published" },
  ":sz":  { "N": "20480" },
  ":exp": { "N": "1719792000" }
}

Nota che :sz e :exp sono inviati come stringhe N — il tipo number di DynamoDB è codificato sul filo come stringa. La mappa dei valori è anche dove riusi un valore tra clausole: definisci :s una volta, riferiscilo sia in una ConditionExpression sia in una FilterExpression.

Costruire queste due mappe a mano è dove si nascondono i typo. L' Expression Builder genera la stringa dell'espressione ed entrambe le mappe insieme, con i type-tag riempiti, così i placeholder non possono andare fuori sincrono.

Nomi per percorsi annidati e scomodi

Il placeholder # fa di più che schivare le parole riservate. La sintassi document-path usa punti e parentesi, quindi un attributo che contiene letteralmente un punto — diciamo una chiave di metadati og.title — è inindirizzabile senza un placeholder:

ProjectionExpression       #og
ExpressionAttributeNames   { "#og": "og.title" }

Senza di esso, DynamoDB legge og.title come "il campo title dentro la mappa og" — una cosa del tutto diversa. Stessa storia per nomi con spazi o cifre iniziali. Per l'annidamento, metti un placeholder su ogni segmento: #meta.#author con sia #meta sia #author definiti.

Nomi vs valori, fianco a fianco

#name:value
Sostituisceun nome di attributoun valore di attributo
MappaExpressionAttributeNamesExpressionAttributeValues
Prefisso#:
Necessario perparole riservate, punti, spazisempre — niente literal inline
Sbagliarne uno dà erroreValidationExceptionValidationException

Se un valore fosse tipizzato come nome, DynamoDB cercherebbe un attributo chiamato published e la tua condizione non corrisponderebbe mai come intendevi — quindi l'API fallisce a voce alta. Quella rigidità è una funzionalità: non c'è risposta silenziosamente sbagliata.

Trappole e prossimi passi

  • Dichiarare un placeholder che non usi — DynamoDB rifiuta voci inutilizzate in entrambe le mappe. Costruisci le mappe dall'espressione, non prima di essa.
  • Riusare :v dopo aver modificato l'espressione — togli una clausola e il suo valore può restare, scatenando l'errore di voce inutilizzata. Il builder li tiene in lockstep.
  • Assumere che un nome sia sicuro perché ha funzionato una volta — gli scontri con le parole riservate sono per-attributo. Metti placeholder uniformemente e smetti di indovinare.

Queste mappe compaiono in ogni percorso di scrittura, quindi si abbinano naturalmente al single-table design e al sapere quando fare Query vs Scan prima ancora di attaccare un filtro.

Genera l'espressione più entrambe le mappe con l' Expression Builder, poi prova DynoTable per eseguirle contro le tue tabelle e guardare i placeholder risolversi.

Aggiornato