0
0
DynamodbHow-ToBeginner · 4 min read

How to Store and Query Nested Data in DynamoDB

In DynamoDB, you can store nested data using Map and List data types inside an item attribute. To query nested data, use ExpressionAttributeNames with dot notation in your KeyConditionExpression or FilterExpression to access nested attributes.
📐

Syntax

DynamoDB supports nested data using Map and List types inside an item attribute. You define nested attributes as JSON-like structures.

To access nested attributes in queries, use dot notation with placeholders in ExpressionAttributeNames. For example, to access address.city, use #addr.#city with {"#addr": "address", "#city": "city"}.

json
PutItem {
  TableName: "Users",
  Item: {
    "UserId": { S: "123" },
    "Name": { S: "Alice" },
    "Address": { M: {
      "City": { S: "Seattle" },
      "Zip": { S: "98101" }
    }}
  }
}

Query {
  TableName: "Users",
  KeyConditionExpression: "UserId = :uid",
  FilterExpression: "#addr.#city = :city",
  ExpressionAttributeNames: { "#addr": "Address", "#city": "City" },
  ExpressionAttributeValues: { ":uid": { S: "123" }, ":city": { S: "Seattle" } }
}
💻

Example

This example shows how to store a user with nested address data and query users living in a specific city.

javascript
const { DynamoDBClient, PutItemCommand, QueryCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({ region: "us-west-2" });

async function run() {
  // Store nested data
  await client.send(new PutItemCommand({
    TableName: "Users",
    Item: {
      UserId: { S: "123" },
      Name: { S: "Alice" },
      Address: { M: {
        City: { S: "Seattle" },
        Zip: { S: "98101" }
      }}
    }
  }));

  // Query nested attribute
  const data = await client.send(new QueryCommand({
    TableName: "Users",
    KeyConditionExpression: "UserId = :uid",
    FilterExpression: "#addr.#city = :city",
    ExpressionAttributeNames: { "#addr": "Address", "#city": "City" },
    ExpressionAttributeValues: { ":uid": { S: "123" }, ":city": { S: "Seattle" } }
  }));

  console.log(JSON.stringify(data.Items, null, 2));
}

run();
Output
[ { "UserId": { "S": "123" }, "Name": { "S": "Alice" }, "Address": { "M": { "City": { "S": "Seattle" }, "Zip": { "S": "98101" } } } } ]
⚠️

Common Pitfalls

  • Trying to query nested attributes directly in KeyConditionExpression is not allowed; use FilterExpression instead.
  • For nested attributes, always use ExpressionAttributeNames to avoid reserved word conflicts.
  • Remember that FilterExpression filters results after the query, so it can be less efficient.
sql
/* Wrong: Trying to query nested attribute in KeyConditionExpression */
// KeyConditionExpression: "Address.City = :city"  <-- This will cause an error

/* Right: Use FilterExpression with ExpressionAttributeNames */
// FilterExpression: "#addr.#city = :city",
// ExpressionAttributeNames: { "#addr": "Address", "#city": "City" }
📊

Quick Reference

ConceptDescriptionExample
MapStores nested key-value pairs{"Address": {"City": "Seattle", "Zip": "98101"}}
ListStores ordered nested values{"Tags": ["red", "blue", "green"]}
ExpressionAttributeNamesPlaceholders for nested attribute names{"#addr": "Address", "#city": "City"}
Dot NotationAccess nested attributes in expressions"#addr.#city"
FilterExpressionFilter results by nested attribute"#addr.#city = :city"

Key Takeaways

Use DynamoDB Map and List types to store nested data inside item attributes.
Access nested attributes in queries using dot notation with ExpressionAttributeNames placeholders.
Use FilterExpression, not KeyConditionExpression, to filter by nested attributes.
Always use ExpressionAttributeNames to avoid reserved word conflicts with nested keys.
Filtering nested data happens after query, so design keys carefully for efficient access.