Skip to content

Query Operations

db.query() is an async generator that yields paginated QueryResult[T] objects. Each page contains a list of validated model instances and an optional pagination token.

Basic query

from boto3.dynamodb.conditions import Key

async for page in db.query(
    Order,
    key_condition_expression=Key("order_id").eq("o1"),
):
    for item in page.items:
        print(item)

Parameters

Parameter Type Default Description
model type[T] DynamoModel subclass to query
key_condition_expression ConditionBase \| None None Key condition (required for most queries)
index_name str \| None None Name of a GSI or LSI to query
filter_expression ConditionBase \| None None Post-key filter applied after key lookup
limit int \| None None Max items to evaluate per page
exclusive_start_key dict \| None None Pagination token from a previous page
consistent_read bool False Strongly consistent reads
scan_index_forward bool True Sort ascending (True) or descending (False)
projection_expression list[ProjectionAttr] \| None None Project specific fields
return_consumed_capacity bool False Include consumed capacity in response

Pagination patterns

Stream all items

query() automatically fetches subsequent pages using LastEvaluatedKey. The generator handles pagination for you:

async for page in db.query(Order, key_condition_expression=Key("order_id").eq("o1")):
    for item in page.items:
        process(item)
# loop exits naturally when there are no more pages

Collect all items at once

all_items = []
async for page in db.query(Order, key_condition_expression=Key("order_id").eq("o1")):
    all_items.extend(page.items)

Single page with a continuation token

last_evaluated_key is None when there are no more pages:

async for page in db.query(Order, key_condition_expression=Key("order_id").eq("o1"), limit=25):
    items = page.items
    cursor = page.last_evaluated_key  # None on the last page
    break  # take one page, save cursor for next request

Resume from a cursor:

async for page in db.query(
    Order,
    key_condition_expression=Key("order_id").eq("o1"),
    limit=25,
    exclusive_start_key=cursor,
):
    items = page.items
    break

Filtering

filter_expression is applied after the key lookup, meaning DynamoDB still reads and charges for all key-matched items. It does not replace the key condition.

from boto3.dynamodb.conditions import Attr, Key

async for page in db.query(
    Order,
    key_condition_expression=Key("order_id").eq("o1"),
    filter_expression=Attr("total").gte(100),
):
    print(page.items)

Sort order

# Descending (newest first)
async for page in db.query(
    Order,
    key_condition_expression=Key("order_id").eq("o1"),
    scan_index_forward=False,
):
    print(page.items)

Querying an index

from boto3.dynamodb.conditions import Key

async for page in db.query(
    Order,
    index_name="order_by_status",
    key_condition_expression=Key("status").eq("pending"),
):
    print(page.items)

See Indexes for index definition.

Projections

from aiodynamodb import ProjectionAttr

async for page in db.query(
    Order,
    key_condition_expression=Key("order_id").eq("o1"),
    projection_expression=[ProjectionAttr("order_id"), ProjectionAttr("total")],
):
    print(page.items)

See Projections for details.

QueryResult

Each page yielded by query() is a QueryResult[T]:

Field Type Description
items list[T] Validated model instances for this page
last_evaluated_key dict \| None Pagination token; None on the last page