Principiante6 min de lectura

Projection expressions en DynamoDB

Una projection expression es el SELECT col1, col2 de DynamoDB: una lista separada por comas de nombres de que le dice a GetItem, Query o Scan que devuelva solo esos atributos en lugar del item entero.

¿Reducen las projection expressions de DynamoDB el coste de lectura?

No. Un ProjectionExpression recorta el payload de la respuesta, no la capacidad de lectura que te facturan. DynamoDB lee el item completo del storage, mide el sobre su tamaño en disco y luego tira los atributos que no nombraste de camino a la salida. Para cortar de verdad el coste de lectura, usa un covering en su lugar.

  • Recorta el payload, no el coste de lectura. DynamoDB lee (y factura) el item completo del storage, y luego tira los atributos que no nombraste de camino a la salida. ProjectionExpression es una optimización de red, no de capacidad.
  • Es cómo traes un subconjunto público. Nombra los pocos atributos que un caller puede ver; el resto nunca sale de la tabla.
  • Usa placeholders #name para cualquier cosa que pueda ser reservada. Los nombres planos de atributo en la expresión chocan con las ~570 palabras reservadas de DynamoDB y fallan la petición.
  • Para ahorros reales de lectura, usa un covering index en su lugar. Un que proyecta solo las columnas que necesitas se lee a su propio tamaño (más pequeño).

Lo que ahorra de verdad

Si vienes de SQL, asumirías que SELECT a, b escanea menos que SELECT *. En DynamoDB esa intuición está mal. La unidad de capacidad de una lectura se calcula a partir del tamaño del item en disco, redondeado al siguiente 4 KB — antes de que se aplique la proyección. AWS es explícito: un ProjectionExpression no cambia la capacidad de lectura que consume una petición.1

Así que una proyección te ahorra dos cosas, ambas reales pero ambas aguas abajo de la lectura:

  • Bytes por el cable. Un item de 6 KB devuelto como dos atributos pequeños es una respuesta diminuta. En un Query que devuelve cientos de items, eso suma rápido.
  • Trabajo en el cliente. Menos que deserializar, menos que guardar en memoria, menos que filtrar a un log o a una respuesta de API por accidente.

Lo que no ahorra es el RCU. Ese es el pie: la gente llega a una proyección para cortar la factura, no ve cambio y concluye que DynamoDB está roto. No lo está — mediste la palanca equivocada.

Proyecta un perfil de usuario público

Digamos que llevas un directorio de usuarios. Cada perfil es un item, claveado para que puedas traer a una persona por handle:

PK = "PROFILE#ada"      (partition key)
SK = "PROFILE#ada"      (sort key — single-item collection)

El item está gordo. Lleva la cara pública de la cuenta más un montón de atributos privados y operacionales:

{
  "PK": "PROFILE#ada",
  "SK": "PROFILE#ada",
  "displayName": "Ada L.",
  "avatarUrl": "https://cdn.example.com/u/ada.png",
  "bio": "Builds things.",
  "emailAddress": "ada@example.com",
  "passwordResetToken": "…",
  "billingCustomerId": "cus_…",
  "lastLoginIp": "…",
  "internalRiskScore": 0.02
}

Una tarjeta de perfil público necesita tres campos. Traer el item entero significa que emailAddress, lastLoginIp e internalRiskScore viajan a un contexto que nunca debería verlos. Nombra solo el subconjunto público:

GetItem  PK = "PROFILE#ada"  SK = "PROFILE#ada"
ProjectionExpression: displayName, avatarUrl, bio

La respuesta lleva tres atributos. Los privados se quedan en la tabla — no filtrados por tu app después de llegar, sino nunca serializados en la respuesta. Esa es la victoria de seguridad, y es la que es difícil de deshacer una vez que un secreto ya ha cruzado una frontera.

Puedes montar y copiar esta petición exacta — names, placeholders y la llamada al SDK — en el DynamoDB Expression Builder, que emite el ProjectionExpression y el map de ExpressionAttributeNames por ti.

Añade o quita campos en el preset de abajo para ver cómo cambia el ProjectionExpression — solo vuelven los atributos listados:

Construye tu solicitud
Código generado
new QueryCommand({
  "TableName": "AuditLog",
  "KeyConditionExpression": "#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
  "ProjectionExpression": "#proj0, #proj1, #proj2",
  "ExpressionAttributeNames": {
    "#hashKey": "pk",
    "#rangeKey": "sk",
    "#proj0": "action",
    "#proj1": "actor",
    "#proj2": "createdAt"
  },
  "ExpressionAttributeValues": {
    ":hashKeyValue": {
      "S": "TENANT#acme"
    },
    ":rangeKeyValue": {
      "S": "EVENT#"
    }
  }
})

Escapa palabras reservadas con placeholders #

Una proyección limpia explota con palabras reservadas. DynamoDB reserva una lista larga — name, status, comment, size, timestamp y cientos más.2 Si un atributo que estás proyectando es uno de ellos, el nombre crudo en la expresión se rechaza.

Supongamos que el perfil también tiene un atributo status ("active", "suspended"). Esto falla:

ProjectionExpression   displayName, status

status está reservado. El arreglo es un expression attribute name — un placeholder con prefijo # mapeado al nombre real:

ProjectionExpression       displayName, #s
ExpressionAttributeNames   { "#s": "status" }

El mismo mecanismo llega a atributos anidados. Para sacar un solo campo de un map, o un elemento de una list, usa sintaxis de document-path — y pon placeholder a cada segmento, porque cualquiera puede estar reservado:

ProjectionExpression       #addr.#city, tags[0]
ExpressionAttributeNames   { "#addr": "address", "#city": "city" }

Una regla práctica: pon placeholder a todo. Nunca tienes que recordar sobre cuál de las ~570 palabras reservadas estás, y la expresión se lee igual de cualquier forma. Y si prefieres saber qué nombres son de verdad el problema, pégalos en el checker de reserved-words — marca las colisiones y emite el map de alias de ExpressionAttributeNames.

Cuándo un covering index gana a una proyección

Si de verdad necesitas cortar el coste de lectura — no solo el payload — la palanca es un Global Secondary Index que proyecta solo los atributos que lees. Un GSI es una copia aparte de los datos; eliges KEYS_ONLY, INCLUDE o ALL para su proyección.3 Un índice KEYS_ONLY o un INCLUDE estrecho es físicamente más pequeño por item, así que un Query contra él se mide a ese tamaño más pequeño.

Eso es un covering index: la query se responde del todo desde el índice, sin viaje de vuelta a la tabla base. Úsalo cuando un access pattern de lectura caliente solo necesita unos pocos atributos de items grandes.

ProjectionExpressionCovering GSI
Corta payload
Corta coste de lecturaNo — lee al tamaño del índice
Storage extraNingunoUna segunda copia de los campos proyectados
Coste de escritura extraNingunoLas escrituras se propagan al índice
Mejor paraOcultar campos privados; wins pequeñosLecturas calientes de unos pocos campos de items grandes

El índice te cuesta storage y capacidad de escritura para ahorrar capacidad de lectura. Merece la pena para una lectura frecuente de un trozo fino de un item pesado; no merece la pena para afeitar un GetItem one-off. Ver GSI vs LSI para elegir el tipo de índice, y cuándo una lectura de GSI puede estar stale antes de poner uno en el hot path.

Escollos y próximos pasos

  • No esperes una factura más pequeña. Una proyección sola nunca cambia el RCU. Si el número no se movió, ese es el comportamiento documentado, no un bug.
  • Pon placeholder a las palabras reservadas. Un name o status pelado en la expresión falla la petición — mapealo con #.
  • Incluye siempre los atributos de clave — añaden payload negligible y te dejan paginar o re-fetch el item.
  • Llega a un covering index solo cuando un patrón caliente lee unos pocos campos de items grandes; pesa primero el coste de escritura/storage.

Construye el ProjectionExpression y su map de attribute-name en el Expression Builder, y prueba DynoTable para lanzar estas proyecciones contra tus propias tablas y ver cómo se encoge la respuesta.


  1. Guía para desarrolladores de AWS DynamoDB, Using projection expressions in DynamoDB: la capacidad de lectura se basa en el tamaño del elemento antes de aplicar cualquier ProjectionExpression. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html
  2. Guía para desarrolladores de AWS DynamoDB, Reserved Words in DynamoDB. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
  3. Guía para desarrolladores de AWS DynamoDB, Attribute Projections (KEYS_ONLY / INCLUDE / ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html

Actualizado