accesspatterns.dev
← All models

commerce

Customer Orders

A customer's order history, newest first, in one partition.

The canonical one-to-many collection. Orders share their customer's partition and sort by the date they were placed, so a customer's whole history is one Query and the recent end of it is the cheap end.

The model

Order

One order, sorted by the date it was placed.

pk
CUSTOMER#<customerId>
sk
ORDER#<placedOn>#<orderId>

Attributes: total (N), status (S), placedAt (N)

Customer

The customer record, at the head of its own collection.

pk
CUSTOMER#<customerId>
sk
CUSTOMER#<customerId>

Attributes: name (S), email (S)

Access patterns

  • QueryRead a customer and their orders

    The customer record and their whole order history, in one read.

  • GetItemRead one order

    A single order, by customer and order key.

Design notes

The date goes in the sort key, not just in an attributewhy

placedAt is on the item too, but the ordering comes from the sort key. A date attribute can only be filtered - every order read, then most of them discarded - while a date in the sort key is part of the key condition, so the rows you skip are never read. That is the same distinction the filter-trap lesson makes, applied to a collection that grows forever.

Newest first is a read-time choicetrade-off

The rows are stored ascending. Production passes ScanIndexForward=false to walk the collection backwards, so the most recent orders arrive first and a Limit of 20 costs twenty reads rather than the customer's entire history. That direction has a cost of its own: the customer record sorts first, so a descending read reaches it last, and an account page wanting the person plus their newest orders takes a separate GetItem for the header. The console here reads the collection as stored; direction is a property of the request, not of the table.

The customer sits in its own collectionwhy

CUSTOMER#c-ada appears as both partition and sort key, so the customer record sorts before every ORDER# in the same partition. One Query returns the person and their history together, which is what an account page needs, and the same read serves a page that only wants the person by stopping at the first row.

Windowing is the design, not the demotrade-off

A real order page asks for a date range - between two ORDER# prefixes - or the most recent N. The preview resolves only the partition half of a key condition, so the console reads the whole collection instead. The technique is unchanged and it is what the sort key is for; this page just cannot show you the narrower read.

Taught in the course

customer-orders
Try an example

The customer record and their whole order history, in one read.

PK(pk)
SK(sk)
placedAt
status
total
email
name
CUSTOMER#c-ada
CUSTOMER#c-adaS
ada@example.comS
Ada OkaforS
CUSTOMER#c-ada
ORDER#2025-10-08#o-1180S
1759881600N
refundedS
76.25N
CUSTOMER#c-ada
ORDER#2026-05-10#o-3344S
1778371200N
deliveredS
12.5N
CUSTOMER#c-ada
ORDER#2026-07-24#o-4870S
1784851200N
deliveredS
129N
CUSTOMER#c-ada
ORDER#2026-08-12#o-5121S
1786492800N
shippedS
48.9N
CUSTOMER#c-raj
CUSTOMER#c-rajS
raj@example.comS
Raj PatelS
CUSTOMER#c-raj
ORDER#2026-06-17#o-4102S
1781654400N
deliveredS
19.99N
CUSTOMER#c-raj
ORDER#2026-08-08#o-5044S
1786147200N
shippedS
234N

Run an operation to see the raw engine response.

table customer-orderskeys PK / SKitems 8

The in-browser engine is a preview build. Transactions, vector search, streams, tags and TTL are among the operations it doesn't implement yet.