dynobase-icon
Dynobase

DynamoDB FilterExpression: Syntax, Examples and Read Costs

A. Yohan Malshika

Written by A. Yohan Malshika

Last updated: October 23rd, 2026

Rewriting the same filters again and again? Dynobase builds filters visually, with operators for real-world data, and saves parameterized favorites that ask for values when you run them. Filters and favorites in Dynobase

A DynamoDB FilterExpression removes nonmatching items from a Query or Scan response after DynamoDB reads them. It reduces the data returned to your application, but does not reduce consumed read capacity. Both the request's Limit and the 1 MB read-page boundary apply before the filter, so a response can contain no items and still have another page.

How a filter expression works

A filtered read has three stages:

  1. DynamoDB reads candidates. Query uses a partition key condition and an optional sort key condition; Scan evaluates items across the selected table or index.
  2. DynamoDB tests the filter against the candidates in that page.
  3. It returns matching items, counts and, when applicable, a continuation key.

Reading 100 KB and returning 10 KB after filtering still charges for the read work on the original candidates. The 1 MB boundary applies to each Query or Scan request before filtering, not to all DynamoDB operations or to the total result set. Follow the continuation key to read more.

A Query filter cannot contain the partition key or sort key of the queried table or index. Put those conditions in KeyConditionExpression. A Scan has no key condition and can filter on key attributes, but such a filter does not turn it into a targeted lookup. See Query vs Scan for choosing the access operation and the Scan guide for full-table jobs. AWS documents the processing order in its Query filter reference.

Comparison operators and logical syntax

Use attribute paths on one side and placeholders for values on the other. The following expressions assume #count aliases employee_count and each colon-prefixed placeholder is defined in ExpressionAttributeValues.

OperatorExampleMatch
=#count = :nEqual
<>#count <> :nNot equal
<#count < :nLess than
<=#count <= :nLess than or equal
>#count > :nGreater than
>=#count >= :nGreater than or equal
BETWEEN#count BETWEEN :low AND :highInclusive range
IN#status IN (:active, :paused)Equal to an entry in a list of values
AND#count >= :n AND #status = :activeBoth conditions
OR#status = :active OR #status = :pausedEither condition
NOTNOT contains(#tags, :tag)Inverse of a condition

IN accepts up to 100 values. It compares an attribute to multiple alternatives; contains checks whether a collection contains an element. They solve different problems. Comparisons are type-sensitive: the Number 10 and the String "10" are different values. Store counts as Numbers if you need numeric comparisons.

Use parentheses to express mixed logic explicitly:

Without the parentheses, AND takes precedence over OR. DynamoDB uses <> for not equal, not JavaScript's != or !==. It does not offer SQL LIKE or a regular-expression filter.

Filter functions

Function names are case-sensitive. The full grammar and supported types are in the AWS expression reference.

FunctionExamplePurpose
attribute_exists(path)attribute_exists(#owner)Attribute is present, including a stored NULL
attribute_not_exists(path)attribute_not_exists(#archivedAt)Attribute is absent
attribute_type(path, type)attribute_type(#count, :numberType)Attribute has the requested DynamoDB type
begins_with(path, prefix)begins_with(#name, :prefix)Value starts with the supplied prefix
contains(path, value)contains(#tags, :tag)String contains a substring, or list/set contains an element
size(path)size(#members) >= :minimumCompare an attribute's size to a number

For attribute_type, supply the type name as a string value, for example ":numberType": "N" with the document client. Supported type names are S, N, B, BOOL, NULL, M, L, SS, NS and BS.

size gives string length, binary length in bytes, the number of set elements, or the number of immediate children in a list or map. It does not measure the billable size of an entire item. contains on a string is case-sensitive; normalize a separate attribute at write time if the access pattern needs case-insensitive matching.

A missing attribute and a stored NULL are different. Use attribute_not_exists(#owner) for missing data and attribute_type(#owner, :nullType) with :nullType set to "NULL" for an explicit null.

FilterExpression examples with AWS SDK for JavaScript v3

These examples use a table named projects-manager, with String partition key PK and String sort key SK. Project items have attributes such as name, projectId, employee_count, status and tags. Install @aws-sdk/client-dynamodb and @aws-sdk/lib-dynamodb, configure an AWS Region and credentials, then run the code as an ES module.

Not equal: exclude one project name

The key condition selects the organization. The filter then excludes items named Project X within that organization's read page. This is one request, not a loop over every result.

The equivalent Scan request below is valid, but evaluates table items before checking the organization. Reuse client from the preceding example:

Use the Query version when the organization key is known. Scan does not accept KeyConditionExpression.

Contains: match a substring or a list element

For a tags list such as ["urgent", "internal"], change the expression to contains(#tags, :tag), the alias to "#tags": "tags", and the value to ":tag": "urgent". Supply the element itself, not a one-element list or set.

Multiple filters: combine contains and equality

If looking up projectId is a frequent operation, give it a key-based access pattern instead of repeatedly scanning and filtering.

Counts and Boolean values

This includes a count of exactly 10. Change >= to > for strictly more than 10. The Boolean value is true, not the string "true". A Boolean attribute by itself is not a complete filter condition; compare it to a Boolean placeholder.

Nested attributes, lists and special names

Use dot notation for maps and zero-based indexes for list positions. For an item with details.owner.email and a members list of maps:

This checks the first member's role, not every member. Filter expressions do not have a wildcard path such as members[*].role. For arbitrary list-element properties, reshape the data into a searchable attribute or process the returned list in your application.

Alias each path segment separately. "#email": "details.owner.email" means a single literal attribute name containing dots, not three nested attributes. Aliases also handle reserved words, spaces and hyphens. The AWS alias documentation explains that distinction. Dynobase's JSON path filtering can build nested filters from values you inspect in an item.

Limit, Count and empty pages

Suppose Limit is 25 and only two of the first 25 evaluated items pass the filter. The response can have ScannedCount: 25, Count: 2 and a LastEvaluatedKey. If none passes, Items can be empty with the same continuation requirement.

  • ScannedCount counts candidates evaluated in this request.
  • Count counts items remaining after its filter.
  • LastEvaluatedKey identifies where to resume; it does not promise another matching item.

Continue until the key is absent or empty. Stopping when Items.length is zero silently loses later matches. The pagination guide includes manual loops, SDK paginators and API cursor handling.

When to use filters, and when to change the model

Filters fit optional refinements on a small, well-targeted item collection: hiding archived projects, selecting a Boolean flag, or excluding a few records. They reduce network payload and keep simple predicate logic out of the application. ProjectionExpression further reduces returned attributes, but neither mechanism reduces read capacity for the same candidates.

A filter is a poor substitute for a key or index when most evaluated items are discarded. Inspect Count, ScannedCount and ConsumedCapacity across the whole request, then decide whether the predicate deserves an index or a different key structure. Do not interpret a low returned count as a cheap read.

Troubleshooting a filter that returns nothing

Check the stored attribute type and spelling first, including nested paths and capitalization. Then check that every #alias and :value has a matching entry, and remove unused entries when changing the expression. With the document client, use plain JavaScript values; the low-level client and AWS CLI require DynamoDB JSON type wrappers. Finally, inspect LastEvaluatedKey before deciding there are no matches.

Frequently Asked Questions

Does FilterExpression reduce DynamoDB read cost?

No. Query and Scan apply the filter after evaluating items, so the same candidates consume the same read capacity with or without the filter. It can reduce response payload.

Can I filter a partition key or sort key?

For Query, put the queried table or index key attributes in KeyConditionExpression. Scan permits filters on keys, but still reads candidates across the table or index.

Why does Limit return fewer items than requested?

Limit caps evaluated items before filtering, and the 1 MB read-page boundary can end a request earlier. Follow LastEvaluatedKey even when the returned Items array is empty.

How do I filter a nested attribute?

Use a document path with separate aliases for each map segment, such as #details.#owner.#email. List positions use zero-based brackets, such as #members[0].

What is the difference between contains and IN?

contains tests a substring or a member of a list or set. IN compares one attribute value against a list of alternative values.

Dynobase is a Professional GUI Client for DynamoDB

Start your 7-day free trial today

Product Features

Download
/
Changelog
/
Pricing
/
Member Portal
/
Privacy
/
EULA
/
Twitter
© 2026 Dynobase