In This Post
- What changed
- Why AWS built this into DynamoDB
- Architecture
- Business value
- Security considerations
- Cost considerations
- Operational considerations
- Tradeoffs
- Implementation guidance
- Best practices
- Who should adopt, who should wait
- The two behaviours that will bite you
- Key takeaways
- Official AWS references
Executive summary
On 5 August 2026, AWS announced general availability of native vector search in Amazon DynamoDB. You store an embedding as an ordinary attribute on an item, create a vector index over that attribute, and run approximate nearest neighbour searches with the SearchVectors API.
It supports up to 4,096 dimensions, three distance functions β Cosine, Euclidean, and Dot product β and returns up to 100 ranked results per query, with optional filtering on attributes you nominate when the index is created. AWS states single-digit millisecond latency at 99%+ recall, with no storage limit on the index. Available in all commercial Regions and AWS GovCloud (US), under the usual pay-per-request model.
The architectural consequence is the interesting part: for a large class of retrieval systems, the separate vector database disappears. If your application already stores its operational data in DynamoDB, the embedding now sits on the same item, updated in the same write, with no synchronisation pipeline between them.
Two behaviours deserve attention before you build on it. Filter conditions accept exact-match values only β no BETWEEN, no BEGINS_WITH, no ranges. And for Cosine and Euclidean, a lower score means more similar, with 0 meaning identical. Both will produce plausible-looking but wrong results rather than errors.
What changed
DynamoDB gains a new index type. Previously an embedding could be stored in DynamoDB as a list of numbers, but nothing could search it β you replicated the data into OpenSearch, Aurora with pgvector, MemoryDB, S3 Vectors, or a third-party store, and searched there.
The vector index
You create a vector index over an attribute holding the embedding. At creation time you fix three things:
| Setting | Options | Notes |
|---|---|---|
| Dimensions | up to 4,096 | Must match your embedding model's output |
| Distance function | Cosine, Euclidean, Dot product | Cosine is recommended for text embeddings |
| Filterable attributes | non-vector attributes you nominate | Only these can narrow a query later |
| Partition key | optional | Recommended once the dataset is large |
These are index-definition choices, not query-time choices. An attribute you did not nominate as filterable cannot be used to scope a search, and the distance function cannot be varied per query.
The query
The SearchVectors API takes a query vector, a number of results to return β up to 100 β and optional filter conditions. Results come back ranked by similarity, with a score whose meaning depends on the distance function you chose.
Choosing the distance function
| Function | Use when |
|---|---|
| Cosine | Text embeddings. Direction matters, magnitude does not. |
| Euclidean | Vector magnitude is meaningful. |
| Dot product | Both direction and magnitude matter. |
The general rule from AWS is to match the distance function to the one your embedding model was trained with. Getting this wrong does not throw an error β it quietly degrades recall.
Why AWS built this into DynamoDB
Because the standard RAG architecture had a synchronisation problem nobody wanted to own.
The common shape: operational data in DynamoDB, embeddings in a separate vector store. That forces a pipeline between them, and the pipeline is where the difficulty accumulates:
- Two writes, one truth. Update a product description and you must re-embed and write to the vector store. Between those two writes the system is inconsistent, and search returns results based on text that no longer exists.
- Streams plus Lambda plus retries. The usual answer is DynamoDB Streams into a Lambda that re-embeds and upserts. That is a real distributed system with its own failure modes, dead-letter handling, and backfill story.
- Deletes are worse than updates. A deleted item that survives in the vector index returns results pointing at rows that are gone. Every team building this discovers it late.
- A second database to run. Capacity, patching, cost, access control, and a separate scaling story β often for a dataset that is mostly small.
Keeping the embedding on the item removes the pipeline rather than improving it. The write that updates the description is the same write that updates the vector. There is no window of inconsistency because there are no longer two systems to be inconsistent between.
Architecture
The model to hold: an embedding is now just an attribute, and a vector index is just another way to look at the table. Nothing about item structure, partitioning, or capacity changes conceptually.
Filtering is where designs go wrong
Filter conditions support exact-match values only. Range conditions such as BETWEEN and BEGINS_WITH are not supported. That single constraint shapes the data model more than anything else in this launch.
"Find products similar to this description, priced between $10 and $50" is not expressible as written. Neither is "documents similar to this one, from the last 30 days". Both are ordinary requirements in retrieval systems.
The workaround is to move the range into an equality by bucketing at write time. Store priceBand = "10-50" alongside the price, or ingestMonth = "2026-08" alongside the timestamp, and nominate that bucket as a filterable attribute. The query then filters on equality and the range is expressed by how you chose the buckets.
This is a familiar single-table-design move, and it has the familiar cost: the bucket boundaries are baked in at write time, so changing them means rewriting data. Choose them deliberately.
Over-fetch and post-filter
Because a query returns at most 100 results, and because filtering happens against a limited set of nominated attributes, precise queries often need a second pass in the application: retrieve the top N by similarity with the filters the index can express, then apply the remaining predicates in code.
That is workable but has a failure mode worth naming β if the post-filter is selective, the 100 results may not contain enough surviving matches, and the query silently returns fewer items than the user expected. The cap is on results returned by the index, not on results after your filtering.
Business value
One database instead of two. The clearest saving, and it is not only infrastructure cost β it is the pipeline, its on-call, its backfill scripts, and the class of bugs where search results reference deleted or stale rows.
Embeddings are consistent by construction. The vector updates in the same write as the data it describes. That removes an entire category of correctness problem rather than mitigating it.
Retrieval latency in the same envelope as the rest of the application. Single-digit milliseconds at 99%+ recall means retrieval stops being the slow step in an agent loop.
Available where regulated workloads are. All commercial Regions plus GovCloud (US) at GA, which is unusual for a feature this new and removes the "not in our Region" objection.
Security considerations
Embeddings are derived from your source data and should inherit its classification. It is tempting to treat a vector as opaque numbers, but embedding inversion research has repeatedly shown that meaningful content can be recovered from embeddings. If the source text is confidential, the embedding is confidential.
Keeping vectors in the table simplifies the access-control story. Previously the vector store had its own authentication, its own IAM or credential model, and often its own network exposure. One store means one policy surface β a genuine security improvement, provided you remember that SearchVectors is a read path that needs to be authorised like any other.
Filtering is not authorisation. Scoping a search with a filter on tenantId narrows results; it does not enforce isolation. If multi-tenancy matters, keep the existing IAM and partition-level controls doing that work. A missing filter in one code path should not be able to leak another tenant's documents.
Similarity search is an inference surface. An attacker with query access can probe a corpus by submitting crafted vectors and observing what comes back, even without read access to the items themselves. Rate limiting and audit apply to this path as they would to any search API.
Encryption and IAM behave as they always have β this is DynamoDB, and the vector index does not introduce a separate data store with separate defaults. That is much of the point.
Cost considerations
Pricing follows the usual DynamoDB model β pay-per-request, no infrastructure to provision. AWS did not publish specific vector-search rates in the announcement, so confirm current pricing on the DynamoDB pricing page before modelling; I have deliberately not put a per-request figure here that I could not verify.
The saving is a deleted component, not a cheaper query. The honest comparison is not "vector search in DynamoDB versus vector search in OpenSearch". It is "DynamoDB alone" versus "DynamoDB plus an OpenSearch domain plus a Streams-driven Lambda plus its dead-letter queue plus the engineering time that pipeline consumes". For small and mid-sized corpora, removing a provisioned OpenSearch domain is usually the dominant term.
Embeddings are large attributes, and item size still matters. A 1,536-dimension float embedding is a substantial addition to every item, and DynamoDB charges by data volume for both storage and throughput. Storing large vectors on items that are read frequently for non-vector reasons will raise the cost of those unrelated reads. If the embedding is only needed for search, consider whether it belongs on the hot item or on a separate one.
Re-embedding is a write amplification event. Changing embedding models means re-embedding the corpus and rewriting every item. That is a bulk write cost and should be budgeted as part of any model migration, not discovered during one.
Operational considerations
Index settings are creation-time decisions. Dimensions, distance function, and filterable attributes are fixed when the index is created. Changing any of them is a new index and a migration, so spend the time up front β particularly on the filterable attribute list, which is the one people under-specify.
Your embedding model and your index are coupled. The dimension count must match the model's output, and the distance function should match how the model was trained. Switching models is therefore an index change, a re-embedding job, and a cutover. Record which model version produced the vectors currently in the table; nothing in the index tells you.
Recall is a quality metric with no alarm attached. AWS quotes 99%+ recall, but a mismatched distance function or a poorly chosen dimension count degrades result quality without erroring, without latency changing, and without any CloudWatch metric moving. Build an evaluation set of known-good query and expected-result pairs and run it after any change to the index, the model, or the embedding pipeline.
The 100-result cap interacts with application-side filtering. If you post-filter, log how often the surviving set is smaller than requested. That number is your early warning that the cap is now the binding constraint.
Tradeoffs
Against OpenSearch. OpenSearch remains far more capable as a search engine: hybrid lexical and semantic scoring, rich filtering with ranges and boolean logic, aggregations, and relevance tuning. DynamoDB vector search does approximate nearest neighbour with equality filters. If your retrieval quality depends on combining BM25 with vector similarity, or on faceted range filtering, OpenSearch is still the answer.
Against Aurora with pgvector. Postgres gives you SQL β joins, arbitrary predicates, transactions across relational and vector data. If your retrieval query naturally includes a join or a range predicate, pgvector expresses it directly where DynamoDB requires bucketing. The trade is the operational profile of a relational database against DynamoDB's.
Against S3 Vectors. S3 Vectors targets large, cost-sensitive, less latency-critical corpora. DynamoDB targets vectors that sit next to operational data and are queried in a request path. Different shapes, and the choice is usually obvious once you ask whether the vector belongs to an item you already store.
Against keeping the pipeline you have. A working Streams-to-OpenSearch pipeline is not a reason to migrate on its own. The case for moving is strongest where the pipeline is causing correctness problems β stale or orphaned entries β rather than where it is merely additional infrastructure.
Implementation guidance
Prerequisites
- An embedding model with a known output dimension of 4,096 or fewer, and knowledge of the distance function it was trained with.
- A decision about which non-vector attributes must be filterable β this is fixed at index creation.
- Continuous values that need range filtering identified, so they can be bucketed at write time.
- An evaluation set of query and expected-result pairs, for measuring recall after changes.
Design the filters before the index
Write out every retrieval query the application needs, then mark which predicates are equality and which are ranges. Equality predicates become filterable attributes. Range predicates must become bucketed attributes, and the bucket granularity is a permanent decision:
{
"productId": "P-10023",
"category": "outdoor",
"price": 34.99,
"priceBand": "10-50",
"ingestMonth": "2026-08",
"descriptionEmbedding": [0.0132, -0.0871, ...]
}
category, priceBand, and ingestMonth are nominated as filterable. price stays on the item for display and for application-side filtering, but the index cannot range over it.
Interpret the score correctly
For Cosine and Euclidean, a lower score means more similar and 0 means identical. A relevance threshold is therefore an upper bound, not a lower one:
# Cosine or Euclidean: keep the close ones
results = [r for r in response["Items"] if r["score"] < 0.25]
# NOT this, which keeps the least similar results
# results = [r for r in response["Items"] if r["score"] > 0.75]
If you are porting threshold logic from a system that returned cosine similarity, the comparison operator has to flip. Nothing will tell you it did not.
Validate recall before you trust it. Run a fixed evaluation set through the index and check that known-correct documents appear in the expected positions. A mismatched distance function, a wrong dimension count, or an inverted threshold all produce results that look reasonable in isolation and are wrong in aggregate. This is the only cheap way to catch them.
Best practices
- Match the distance function to your embedding model's training. Cosine for most text embeddings.
- Enumerate every retrieval query before creating the index. Filterable attributes cannot be added later without a new index.
- Bucket continuous values at write time so equality filters can do the work ranges cannot.
- Treat the score as a distance. Lower is closer for Cosine and Euclidean. Write the comparison the right way round and comment it.
- Keep an evaluation set and run it after any change to the model, the index, or the pipeline.
- Record the embedding model version alongside the vector. The index will not tell you which model produced it.
- Watch item size. A large embedding on a frequently read item raises the cost of every unrelated read.
- Do not use filters as an isolation boundary. Keep IAM and partition design doing that.
Who should adopt, who should wait
Adopt
- Teams whose operational data is already in DynamoDB and who are running a Streams-to-vector-store pipeline alongside it. The pipeline is the thing being deleted.
- Agentic and RAG applications where retrieval sits in a latency-sensitive request path.
- Anyone who has been bitten by stale or orphaned vectors β this makes that class of bug structurally impossible.
- GovCloud workloads, which rarely get a capability like this at GA.
Wait
- Retrieval that needs range or prefix filtering and cannot reasonably be bucketed.
- Systems relying on hybrid lexical and semantic search, faceting, or relevance tuning. That is OpenSearch's job.
- Applications needing more than 100 results per query, or heavy post-filtering that would starve that cap.
- Teams whose data is not in DynamoDB. Moving a corpus into DynamoDB to use this is the tail wagging the dog.
- Anyone without a way to measure recall. You will not notice a wrong distance function otherwise.
The two behaviours that will bite you
Both produce wrong results rather than errors, which is what makes them worth a section of their own.
1. Lower score means more similar. For Cosine and Euclidean distance functions, a score of 0 means identical vectors, and larger values mean less similar. These are distances, not similarities.
Most vector databases and most tutorial code return cosine similarity, where 1.0 is identical and a threshold reads score > 0.8. Port that logic here unchanged and you will keep the least relevant results and discard the best ones. Every returned document will be a real document, the query will succeed, latency will look fine, and the answers will be nonsense. Write the threshold as an upper bound and leave a comment saying why.
2. Filter conditions accept exact-match values only. Range conditions such as BETWEEN and BEGINS_WITH are not supported.
This is the constraint most likely to be discovered after the data model is settled. "Similar documents from the last quarter" and "similar products under $50" both need bucketed attributes created at write time, and buckets chosen badly are expensive to change because changing them means rewriting the corpus.
The practical discipline is to write out the full list of retrieval queries β including the ones the product team has not asked for yet but obviously will β before the index exists. Filterable attributes and bucket granularity are index-definition decisions, and index-definition decisions are migrations.
Key takeaways
- Embeddings are stored as ordinary attributes and indexed in place. The separate vector store, and the pipeline feeding it, can often be deleted.
- Up to 4,096 dimensions; Cosine, Euclidean, or Dot product; up to 100 results per
SearchVectorscall. - Filters are exact-match only. No
BETWEEN, noBEGINS_WITH. Bucket continuous values at write time. - Lower score means more similar for Cosine and Euclidean; 0 is identical. Thresholds are upper bounds.
- Dimensions, distance function and filterable attributes are fixed at index creation. Changing them is a migration.
- Match the distance function to the embedding model's training, or recall degrades silently.
- GA in all commercial Regions and GovCloud (US); pay-per-request. Verify current rates before modelling.
- Embeddings inherit the classification of the text they encode. Treat them as sensitive.
- OpenSearch still wins for hybrid search, ranges and faceting. This is for vectors that belong to items you already store.
Official AWS references
- Amazon DynamoDB now supports real-time vector search
- Amazon DynamoDB now supports real-time vector search at any scale, AWS News Blog
- Amazon DynamoDB Developer Guide
- Service, account, and table quotas in Amazon DynamoDB
- Amazon DynamoDB pricing
- Choosing a database for your generative AI applications
- Vector indexes in Amazon S3
- Vector search for DynamoDB with zero-ETL for OpenSearch Service
Comments