Expression attribute names et values dans DynamoDB
Les expressions DynamoDB sont des templates : tu écris des placeholders, puis tu
fournis les vrais noms d'attribut et valeurs dans deux maps annexes. #name est un
placeholder de nom ; :value est un placeholder de valeur. Confonds les deux et
DynamoDB rejette tout l'appel.
Quelle est la différence entre #name et :value dans DynamoDB ?
#name est un placeholder pour un nom d'attribut, fourni via ExpressionAttributeNames ; :value est un placeholder pour une valeur d'attribut, fourni via ExpressionAttributeValues. Utilise #name pour esquiver les mots réservés, les points ou les espaces, et :value pour chaque littéral — DynamoDB n'inline jamais les valeurs. Ils ne sont pas interchangeables ; les intervertir lève une ValidationException.
#namesubstitue un nom d'attribut viaExpressionAttributeNames— utilise-le chaque fois qu'un attribut entre en conflit avec un mot réservé ou contient un point/une espace.:valuesubstitue une valeur viaExpressionAttributeValues— DynamoDB n'inline jamais de littéraux dans le texte d'expression, donc chaque valeur est un placeholder.- Ils ne sont pas interchangeables. Un
#là où un:appartient est uneValidationException, pas un no-op silencieux.
Venant de SQL, tu inlines les deux — WHERE status = 'published'. DynamoDB n'inline
aucun des deux. Cette séparation est ce qui fait trébucher chaque débutant.
Pourquoi les deux maps existent
En SQL la chaîne de requête porte tout : noms de colonnes, littéraux, opérateurs. DynamoDB sépare délibérément la forme de l'expression de ses données.
Les valeurs vont dans leur propre map pour que DynamoDB puisse typer chacune (S, N,
BOOL, …) et pour que le parseur n'ait jamais à deviner où finit une chaîne — il n'y a
pas de guillemets ni d'échappement à se tromper. Vois les types de données dans
DynamoDB pour la liste complète des type tags.
Les noms reçoivent le même traitement pour une raison différente : DynamoDB a une longue liste de mots réservés, et tout attribut correspondant à l'un d'eux ne peut pas apparaître comme nom nu dans une expression. Le placeholder esquive entièrement la réservation.
Le piège des mots réservés
Voici une table d'articles CMS — clé de partition BLOG#<blog>, clé de tri
ARTICLE#<slug> — dont les attributs se lisent naturellement mais entrent par hasard
en collision avec des mots réservés :
| Attribut | Réservé ? | Ce qu'il contient |
|---|---|---|
status | oui | draft / published |
name | oui | nom d'affichage de l'auteur |
size | oui | longueur en octets rendue |
ttl | oui | expiration d'archive (epoch) |
slug | non | slug d'URL |
status, name, size et ttl sont tous sur la liste des mots réservés d'AWS, donc
ce filtre échoue au premier mot :
FilterExpression status = :s
DynamoDB renvoie une ValidationException — « Attribute name is a reserved keyword;
reserved keyword: status ». Le correctif est un placeholder de nom, jamais renommer
l'attribut :
FilterExpression #status = :s
ExpressionAttributeNames { "#status": "status" }
ExpressionAttributeValues { ":s": { "S": "published" } }
Le piège : slug n'est pas réservé, donc une requête que tu as testée contre slug
marche, et tu supposes que la suivante marchera aussi. Puis status la casse. La liste
complète bouge, alors ne la mémorise pas — mets un placeholder sur chaque nom et tu ne
te fais jamais mordre.
Mappe chaque valeur, toujours
Les valeurs sont non négociables : il n'y a pas de syntaxe pour un littéral inline. Même un simple nombre reçoit un placeholder. Cette mise à jour marque un article comme publié, estampille sa taille, et pose un TTL d'archive de 30 jours :
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" }
}Note que :sz et :exp sont envoyés comme des chaînes N — le type number de
DynamoDB est encodé sur le fil comme une chaîne. La map de valeurs est aussi là où tu
réutilises une valeur à travers les clauses : définis :s une fois, référence-le dans
une ConditionExpression et une FilterExpression.
Construire ces deux maps à la main est là où se cachent les fautes de frappe. L'Expression Builder génère la chaîne d'expression et les deux maps ensemble, avec les type tags remplis, pour que les placeholders ne puissent pas se désynchroniser.
Les noms pour les chemins imbriqués et délicats
Le placeholder # fait plus qu'esquiver les mots réservés. La syntaxe de chemin de
document utilise des points et des crochets, donc un attribut qui contient littéralement
un point — disons une clé de métadonnée og.title — est inadressable sans un
placeholder :
ProjectionExpression #og
ExpressionAttributeNames { "#og": "og.title" }
Sans lui, DynamoDB lit og.title comme « le champ title à l'intérieur de la map og
» — une chose entièrement différente. Même histoire pour les noms avec des espaces ou
des chiffres en tête. Pour l'imbrication, tu mets un placeholder sur chaque segment :
#meta.#author avec #meta et #author tous deux définis.
Noms vs valeurs, côte à côte
#name | :value | |
|---|---|---|
| Substitue | un nom d'attribut | une valeur d'attribut |
| Map | ExpressionAttributeNames | ExpressionAttributeValues |
| Préfixe | # | : |
| Nécessaire pour | mots réservés, points, espaces | toujours — pas de littéraux inline |
| Le mauvais lève une erreur | ValidationException | ValidationException |
Si une valeur était typée comme un nom, DynamoDB chercherait un attribut appelé
published et ta condition ne matcherait jamais comme tu l'entendais — donc l'API
échoue bruyamment à la place. Cette rigueur est une fonctionnalité : il n'y a pas de
mauvaise réponse silencieuse.
Pièges et étapes suivantes
- Déclarer un placeholder que tu n'utilises pas — DynamoDB rejette les entrées inutilisées dans l'une ou l'autre map. Construis les maps à partir de l'expression, pas avant elle.
- Réutiliser
:vaprès avoir édité l'expression — retire une clause et sa valeur peut s'attarder, déclenchant l'erreur d'entrée inutilisée. Le builder les garde au pas. - Supposer qu'un nom est sûr parce qu'il a marché une fois — les collisions de mots réservés sont par attribut. Mets des placeholders uniformément et arrête de deviner.
Ces maps apparaissent dans chaque chemin d'écriture, donc elles se marient naturellement avec le single-table design et avec le fait de savoir quand faire un Query vs un Scan avant même d'attacher un filtre.
Génère l'expression plus les deux maps avec l'Expression Builder, puis essaie DynoTable pour les exécuter contre tes propres tables et regarder les placeholders se résoudre.