Expression attribute names y values en DynamoDB
Las expresiones de DynamoDB son plantillas: escribes marcadores, luego suministras los
nombres y valores de atributo reales en dos mapas aparte. #name es un marcador de
nombre; :value es un marcador de valor. Confunde los dos y DynamoDB rechaza toda la
llamada.
¿Cuál es la diferencia entre #name y :value en DynamoDB?
#name es un marcador para un nombre de atributo, suministrado a través de ExpressionAttributeNames; :value es un marcador para un valor de atributo, suministrado a través de ExpressionAttributeValues. Usa #name para esquivar palabras reservadas, puntos o espacios, y :value para cada literal — DynamoDB nunca incrusta valores. No son intercambiables; intercambiarlos lanza una ValidationException.
#namesustituye un nombre de atributo víaExpressionAttributeNames— úsalo siempre que un atributo choque con una palabra reservada o contenga un punto/espacio.:valuesustituye un valor víaExpressionAttributeValues— DynamoDB nunca incrusta literales en el texto de la expresión, así que cada valor es un marcador.- No son intercambiables. Un
#donde corresponde un:es unaValidationException, no un no-op silencioso.
Viniendo de SQL, incrustas ambos — WHERE status = 'published'. DynamoDB no incrusta
ninguno. Esa división es lo que confunde a cada recién llegado.
Por qué existen los dos mapas
En SQL la cadena de consulta lo lleva todo: nombres de columna, literales, operadores. DynamoDB separa deliberadamente la forma de la expresión de sus datos.
Los valores van en su propio mapa para que DynamoDB pueda tipar cada uno (S, N,
BOOL, …) y para que el analizador nunca tenga que adivinar dónde termina una cadena — no
hay comillas ni escapado que equivocar. Consulta tipos de datos en
DynamoDB para la lista completa de etiquetas de tipo.
Los nombres reciben el mismo trato por una razón distinta: DynamoDB tiene una larga lista de palabras reservadas, y cualquier atributo que coincida con una no puede aparecer como nombre pelado en una expresión. El marcador esquiva la reserva por completo.
La trampa de la palabra reservada
Aquí hay una tabla de artículos de un CMS — partition key BLOG#<blog>, sort key
ARTICLE#<slug> — cuyos atributos se leen de forma natural pero resulta que chocan con
palabras reservadas:
| Atributo | ¿Reservada? | Qué contiene |
|---|---|---|
status | sí | draft / published |
name | sí | nombre del autor |
size | sí | longitud en bytes renderizada |
ttl | sí | expiración del archivo (epoch) |
slug | no | slug de la URL |
status, name, size y ttl están todos en la lista de palabras reservadas de AWS, así
que este filtro falla en la primera palabra:
FilterExpression status = :s
DynamoDB devuelve una ValidationException — "Attribute name is a reserved keyword;
reserved keyword: status". El arreglo es un marcador de nombre, nunca renombrar el
atributo:
FilterExpression #status = :s
ExpressionAttributeNames { "#status": "status" }
ExpressionAttributeValues { ":s": { "S": "published" } }
La trampa: slug no está reservado, así que una consulta que probaste contra slug
funciona, y asumes que la siguiente también lo hará. Luego status la rompe. La lista
completa se mueve, así que no la memorices — pon un marcador en cada nombre y nunca te
muerden.
Mapea cada valor, siempre
Los valores son innegociables: no hay sintaxis para un literal incrustado. Incluso un número simple recibe un marcador. Esta actualización marca un artículo como publicado, sella su tamaño y establece un TTL de archivo de 30 días:
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" }
}Fíjate en que :sz y :exp se envían como cadenas N — el tipo número de DynamoDB se
codifica en el cable como una cadena. El mapa de valores es también donde reutilizas un
valor entre cláusulas: define :s una vez, referéncialo tanto en una ConditionExpression
como en una FilterExpression.
Construir estos dos mapas a mano es donde se esconden las erratas. El Constructor de expresiones genera la cadena de expresión y ambos mapas juntos, con las etiquetas de tipo rellenadas, para que los marcadores no puedan desincronizarse.
Nombres para rutas anidadas e incómodas
El marcador # hace más que esquivar palabras reservadas. La sintaxis de ruta de documento
usa puntos y corchetes, así que un atributo que literalmente contiene un punto — digamos
una clave de metadatos og.title — es indireccionable sin un marcador:
ProjectionExpression #og
ExpressionAttributeNames { "#og": "og.title" }
Sin él, DynamoDB lee og.title como "el campo title dentro del map og" — algo
completamente distinto. La misma historia para nombres con espacios o dígitos iniciales.
Para el anidamiento, pones un marcador en cada segmento: #meta.#author con tanto #meta
como #author definidos.
Nombres vs valores, lado a lado
#name | :value | |
|---|---|---|
| Sustituye | un nombre de atributo | un valor de atributo |
| Mapa | ExpressionAttributeNames | ExpressionAttributeValues |
| Prefijo | # | : |
| Necesario para | palabras reservadas, puntos, espacios | siempre — sin literales incrustados |
| El equivocado da error | ValidationException | ValidationException |
Si un valor se tipara como un nombre, DynamoDB buscaría un atributo llamado published y tu
condición nunca coincidiría como pretendías — así que la API falla en voz alta. Esa
rigurosidad es una función: no hay respuesta equivocada silenciosa.
Trampas y próximos pasos
- Declarar un marcador que no usas — DynamoDB rechaza las entradas no usadas en cualquiera de los mapas. Construye los mapas a partir de la expresión, no antes de ella.
- Reutilizar
:vtras editar la expresión — descarta una cláusula y su valor puede quedarse, disparando el error de entrada-no-usada. El constructor los mantiene a la par. - Asumir que un nombre es seguro porque funcionó una vez — los choques de palabras-reservadas son por atributo. Pon marcadores de forma uniforme y deja de adivinar.
Estos mapas aparecen en cada ruta de escritura, así que se emparejan de forma natural con diseño de tabla única y con saber cuándo hacer Query vs Scan antes de adjuntar siquiera un filtro.
Genera la expresión más ambos mapas con el Constructor de expresiones, luego prueba DynoTable para ejecutarlas contra tus propias tablas y ver los marcadores resolverse.