Query is the efficient way to read many items and the operation most access patterns are built on.
Query reads items from one item collection: you must give an equality condition on the partition key, and you may add one condition on the sort key: =, <, <=, >, >=, between, or begins_with.
With the boto3 resource you build these with boto3.dynamodb.conditions.Key:
Key("customer_id").eq("c1") & Key("order_id").begins_with("2025-03")Results come back sorted by sort key, ascending by default; ScanIndexForward=False returns them descending, which together with Limit=1 is the idiom for "latest item" (most recent order, last reading of a sensor).
Because a query reads one contiguous range on one partition, its cost depends only on the size of the items it reads, not on the size of the table. That is why designs that turn access patterns into queries scale so well.
A query returns at most 1 MB of data per call. If there is more, the response contains LastEvaluatedKey and you must call again with ExclusiveStartKey (see pagination).
A FilterExpression can drop items after they are read, but you still pay for reading them; filters are a convenience, not a substitute for good keys.
Example
import boto3
from boto3.dynamodb.conditions import Key
table = boto3.resource("dynamodb").Table("Orders")
for oid in ["2025-01-09#A11", "2025-03-01#A17", "2025-03-14#A20"]:
table.put_item(Item={"customer_id": "c1", "order_id": oid})
march = table.query(
KeyConditionExpression=Key("customer_id").eq("c1")
& Key("order_id").begins_with("2025-03"))
print([i["order_id"] for i in march["Items"]])
# ['2025-03-01#A17', '2025-03-14#A20']
latest = table.query(KeyConditionExpression=Key("customer_id").eq("c1"),
ScanIndexForward=False, Limit=1)
print(latest["Items"][0]["order_id"]) # 2025-03-14#A20Remember
- Query needs partition key equality and allows one sort key condition
- Results are ordered by sort key; ScanIndexForward=False reverses
- Cost depends on items read, not table size
- FilterExpression runs after the read and does not reduce cost
Watch out for
- Expecting a FilterExpression to make a query cheaper; you pay for everything read before filtering.