Relationships

You reach for a JOIN because the UI shows a workspace beside its projects, and DynamoDB has no server-side join operator. The expensive mistake is fetching each side with separate GetItem calls: N+1 reads that grow with every row on screen.

Relationships in DynamoDB live in key design. You place related items in the same item collection so one Query returns them, or you write edge items that an index reads from the other side.

Composite sort keys let you read just the slice you need, and reference counts mean you never read a whole collection to count it.

What this section teaches you to do

  • Model one-to-many by co-locating children under a parent partition key.
  • Model many-to-many with edge items and an inverted GSI, so both sides list the other with one Query each.
  • Encode a hierarchy in the sort key so one begins_with query reads a subtree.
  • Keep a count on the parent in the same transaction as the child write, and use it to gate deletes.

Reading order

  1. One-to-many: children under one partition key; the base pattern every other relationship extends.
  2. Adjacency list: a follow graph as edge items, read one hop at a time in both directions, with the write cost and hot targets.
  3. Many-to-many: students and courses as edge items, with an inverted GSI for the course roster.
  4. Composite sort keys: a file path in the sort key, so one begins_with query reads a whole subtree.
  5. Reference counts: a like tally kept correct with one conditioned transaction, and the same count gating a delete.
0 of 6 readQuiz

A workspace at EntityRef = WS#acme with its projects at Detail = PROJ#2026-0042 paints the workspace screen from one Query.

A follow graph stores each follow once and reads "who follows Bob" from a GSI. Each read is one hop; for multi-hop traversals at millisecond latency, AWS points to Amazon Neptune.

Students and courses need an edge item per enrollment; skip that and you Scan for one direction.

A path such as root/photos/2026/ in the sort key makes a folder's subtree one begins_with query.

A count on the parent can gate a delete: attribute_not_exists(projectCount) OR projectCount = :zero refuses to remove a workspace that still has projects.

Each pattern trades write work for cheap reads. Pick the one that matches how often you list versus how often you reshape the graph.

Hands-on in DynoTable

Download DynoTable and open a table with parent/child keys. Run a Query on the parent partition and the grid returns the whole collection in sort order.

Right-click a row and pick Filter to a key prefix to see one entity type's rows on their own.

The SQL Workbench runs a JOIN across separate tables when each join lands on a simple-keyed table's partition key or on a GSI partition key. A self-join follows the same rule, so inside one single-table design it lands on a GSI partition key.

Within one table it still runs GROUP BY and aggregates, such as comparing stored counts with their children. Use it to validate a key design, not to paper over a missing access pattern.

For ad-hoc parent/child layouts, the single-table design planner proposes PK/SK placement from your stated reads.