Intermediário5 min de leitura

Expression Attribute Names e Values no DynamoDB

Expressões do DynamoDB são templates: você escreve placeholders, depois fornece os nomes de atributo e valores reais em dois mapas auxiliares. #name é um placeholder de nome; :value é um placeholder de valor. Confunda os dois e o DynamoDB rejeita a chamada inteira.

Qual é a diferença entre #name e :value no DynamoDB?

#name é um placeholder para um nome de atributo, fornecido através de ExpressionAttributeNames; :value é um placeholder para um valor de atributo, fornecido através de ExpressionAttributeValues. Use #name para escapar de palavras reservadas, pontos ou espaços, e :value para todo literal — o DynamoDB nunca embute valores no texto. Eles não são intercambiáveis; trocar um pelo outro dispara uma ValidationException.

  • #name substitui um nome de atributo via ExpressionAttributeNames — use-o sempre que um atributo colidir com uma palavra reservada ou contiver um ponto/espaço.
  • :value substitui um valor via ExpressionAttributeValues — o DynamoDB nunca embute literais no texto da expressão, então todo valor é um placeholder.
  • Eles não são intercambiáveis. Um # onde um : pertence é uma ValidationException, não um no-op silencioso.

Vindo do SQL, você embute os dois — WHERE status = 'published'. O DynamoDB não embute nenhum. Essa divisão é a coisa que tropeça todo novato.

Por que os dois mapas existem

No SQL a string de consulta carrega tudo: nomes de coluna, literais, operadores. O DynamoDB deliberadamente separa a forma da expressão dos seus dados.

Valores vão no seu próprio mapa para que o DynamoDB possa tipar cada um (S, N, BOOL, …) e para que o parser nunca tenha que adivinhar onde uma string termina — não há aspas nem escaping para errar. Veja tipos de dados no DynamoDB para a lista completa de type-tags.

Nomes recebem o mesmo tratamento por uma razão diferente: o DynamoDB tem uma longa lista de palavras reservadas, e qualquer atributo que corresponda a uma não pode aparecer como nome cru em uma expressão. O placeholder desvia da reserva por completo.

A cilada das palavras reservadas

Aqui está uma tabela de artigos de um CMS — chave de partição BLOG#<blog>, chave de ordenação ARTICLE#<slug> — cujos atributos se leem naturalmente mas por acaso colidem com palavras reservadas:

AtributoReservado?O que contém
statussimdraft / published
namesimnome de exibição do autor
sizesimtamanho em bytes renderizado
ttlsimexpiração do arquivamento (epoch)
slugnãoslug da URL

status, name, size e ttl estão todos na lista de palavras reservadas da AWS, então este filtro falha na primeira palavra:

FilterExpression  status = :s

O DynamoDB retorna uma ValidationException"Attribute name is a reserved keyword; reserved keyword: status". A correção é um placeholder de nome, nunca renomear o atributo:

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

A cilada: slug não é reservado, então uma consulta que você testou contra slug funciona, e você assume que a próxima também vai. Aí status a quebra. A lista completa muda, então não a memorize — coloque placeholder em todo nome e você nunca é mordido.

Mapeie todo valor, sempre

Valores são inegociáveis: não há sintaxe para um literal inline. Até um número simples ganha um placeholder. Este update marca um artigo como publicado, carimba seu tamanho, e define um TTL de arquivamento de 30 dias:

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 e :exp são enviados como strings N — o tipo number do DynamoDB é codificado no fio como string. O mapa de valores também é onde você reutiliza um valor entre cláusulas: defina :s uma vez, referencie-o tanto em uma ConditionExpression quanto em uma FilterExpression.

Construir esses dois mapas à mão é onde erros de digitação se escondem. O Expression Builder gera a string da expressão e os dois mapas juntos, com os type-tags preenchidos, para que os placeholders não saiam de sincronia.

Nomes para caminhos aninhados e estranhos

O placeholder # faz mais do que desviar de palavras reservadas. A sintaxe de document-path usa pontos e colchetes, então um atributo que literalmente contém um ponto — digamos uma chave de metadados og.title — é inendereçável sem um placeholder:

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

Sem ele, o DynamoDB lê og.title como "o campo title dentro do map og" — uma coisa inteiramente diferente. Mesma história para nomes com espaços ou dígitos iniciais. Para aninhamento, você coloca placeholder em cada segmento: #meta.#author com ambos #meta e #author definidos.

Nomes vs valores, lado a lado

#name:value
Substituium nome de atributoum valor de atributo
MapaExpressionAttributeNamesExpressionAttributeValues
Prefixo#:
Necessário parapalavras reservadas, pontos, espaçossempre — sem literais inline
O errado dá erroValidationExceptionValidationException

Se um valor fosse tipado como nome, o DynamoDB procuraria um atributo chamado published e sua condição nunca corresponderia do jeito que você quis — então a API falha alto em vez disso. Essa rigidez é um recurso: não há resposta errada silenciosa.

Armadilhas e próximos passos

  • Declarar um placeholder que você não usa — o DynamoDB rejeita entradas não usadas em qualquer um dos mapas. Construa os mapas a partir da expressão, não antes dela.
  • Reutilizar :v depois de editar a expressão — remova uma cláusula e seu valor pode permanecer, disparando o erro de entrada não usada. O builder os mantém em sincronia.
  • Assumir que um nome é seguro porque funcionou uma vez — colisões de palavra reservada são por atributo. Coloque placeholder uniformemente e pare de adivinhar.

Esses mapas aparecem em todo caminho de escrita, então combinam naturalmente com single-table design e com saber quando usar Query vs Scan antes de você anexar um filtro.

Gere a expressão mais os dois mapas com o Expression Builder, depois experimente o DynoTable para rodá-los contra suas próprias tabelas e ver os placeholders se resolverem.

Atualizado