Adding Semantic Search to an Existing DynamoDB Table with Vector Indexes A developer demonstrates how to add semantic search to an existing DynamoDB table using the new Vector Search feature, eliminating the need for separate search infrastructure. The approach combines recipe fields into a single text string, generates embeddings with Amazon Bedrock's Titan Text Embeddings V2 model, and stores them directly in DynamoDB for natural language queries. Whenever someone asks me to add search to an application, I try to find ways around it. The implementation itself isn't the problem, it's everything that comes with it: extra components to manage, more failure points, and the constant challenge of keeping data in sync. For the past few years, I've worked a lot with DynamoDB and with the introduction of Vector Search I feel a lot more comfortable to add this type of functionality. I wrote last week about why AWS released another vector store https://www.andmore.dev/blog/why-did-aws-release-another-vector-store and where DynamoDB Vector Search fits in the landscape. In this post I want to show you how you can take an existing DynamoDB table and add vector search to it. The current API uses a serverless setup. It includes SAM https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html for infrastructure, API Gateway in front, Lambda functions behind, and DynamoDB for storage. The API contains plain CRUD operations to manage recipes: create, read, update, delete, and list. The complexity arises when you want to add filters to query exactly for what you need. In DynamoDB this means you need to add Global Secondary Indexes GSI for every permutation... this is really not scalable. So the only option until now was to have a data pipeline to index the data separately and provide search. That is not the case anymore With vector search in DynamoDB, we can store vector embeddings alongside our data and search them directly. Now, users can search our recipes using natural language queries and find recipes based on the meaning, not just exact keyword matches. Semantic search works by turning text into embeddings, which are lists of numbers that represent the meaning of the text. Two pieces of text that mean similar things end up close together in the vector space, so "spicy chicken stew" lands near "hot and hearty poultry dish" even though they share almost no words. The closeness is what lets you search by intent instead of by keyword. To store an embedding we first need to generate one. For that you need an embedding model, which converts your text into a numerical representation. I picked Amazon Bedrock's Titan Text Embeddings V2 https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html model. It produces 1024-dimension https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/vector-search-concepts.html vector-search-concepts-dimensions vectors, returns them normalized, and pairs naturally with cosine similarity https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/vector-search-concepts.html vector-search-concepts-similarity . The first thing you need to figure out is what text you actually want to embed. Our recipes use structured data, that's why I created a single string that combines key fields: name, description, cuisine, dietary tags, and ingredients. Combining them into a single representation means a search can match on any of those fields at once. js function buildEmbeddingText recipe: RecipeInput : string { const ingredientNames = recipe.ingredients.map i = i.name .join ", " ; const dietaryInfo = recipe.dietary?.length ? Dietary: ${recipe.dietary.join ", " }. : ""; return recipe.name, recipe.description, Cuisine: ${recipe.cuisine}. , dietaryInfo, Ingredients: ${ingredientNames}. , Prep time: ${recipe.prepTimeMinutes} minutes. Cook time: ${recipe.cookTimeMinutes} minutes. , .filter Boolean .join " " ; } async function generateEmbedding text: string : Promise