Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Vector Store Setup and Similarity Search

Introduction

In our last lesson, we explored the theoretical underpinnings of vector databases, focusing on how they solve the challenge of searching through millions of embeddings efficiently. We covered the limitations of brute-force search and the clever solutions provided by Approximate Nearest Neighbor (ANN) indexes like HNSW and IVF.

Today, we transition from theory to practice. Your goal for this lesson is to set up a vector store (using ChromaDB) and perform a similarity search on a corpus of documents. This hands-on experience is a crucial step in our journey, as vector stores are the backbone of the Retrieval-Augmented Generation (RAG) systems we will build in the next module.

We will walk through the entire process:

  • Installing and setting up ChromaDB.
  • Creating a "collection" to hold our data.
  • Adding documents and letting ChromaDB handle the embedding process for us.
  • Querying the collection to find semantically similar documents.
  • Using an external embedding model (from OpenAI) for more control.

By the end of this lesson, you'll have a functional understanding of how to use a vector store, one of the most fundamental components in the modern LLM application stack.

What is a Vector Store?

Before we dive into the code, let's solidify our understanding of what a vector store is and what it does. In essence, it's a database purpose-built for storing, managing, and searching high-dimensional vector embeddings.

ChromaDB Basics and Implementation Thumbnail
This diagram shows the typical workflow involving a vector database like ChromaDB. An application embeds a query, uses the vector store to find relevant documents, and then passes those documents as context to an LLM to generate an answer.

To get a concise overview of what vector stores do and why they are necessary, please read the following brief introduction.

What Are Vector Stores?

This article from DataCamp provides a clear and simple explanation of what vector stores are and their role in the AI ecosystem.

Read the introductory section and the part titled 'What Are Vector Stores?'. This will set the context for our practical work with ChromaDB.

Getting Started with ChromaDB

We will use ChromaDB, a popular open-source vector store designed to be simple and developer-friendly. It's perfect for getting started because it can run directly within your Python environment without needing a separate server, but it can also scale to more robust production setups.

The Core Workflow

The process of using ChromaDB follows a clear, logical sequence, which is summarized nicely in the resource below.

How does Chroma DB work?

This short section from the DataCamp article outlines the three main steps we will be following today.

Read the subsection titled 'How does Chroma DB work?'. This will give you a mental model for the code we're about to write.

Now, let's turn this workflow into code. We'll follow a comprehensive video tutorial that walks through each step. I will guide you through the key parts, providing code snippets and explanations along the way.

Step 1: Installation and Client Initialization

First, you'll need to install the chromadb library. As a software engineer, this will be familiar territory.

pip install chromadb

Once installed, the first step in your code is to create a client. The client is your main entry point for interacting with the database. ChromaDB offers two main types of clients:

  1. chromadb.Client(): An in-memory client. Fast for testing, but all data is lost when your program exits.
  2. chromadb.PersistentClient(path="..."): A client that saves data to a specified directory on your disk. This is what you'll typically use to ensure your data persists between sessions.

Let's watch how to do this.

Chroma - Vector Database for LLM Applications | OpenAI integration

This video from BugBytes provides an excellent, step-by-step guide to using ChromaDB. We'll start with the initial setup.

Watch from 01:36 to 02:36. This part covers installing ChromaDB and creating a client object.

Here is the corresponding code to create a persistent client:

import chromadb




# The path is where the database will be stored on disk.
client = chromadb.PersistentClient(path="/path/to/your/db") 

Step 2: Creating a Collection and Adding Documents

With a client, you can now create a collection. Think of a collection as a table in a relational database. It's where you'll store your embeddings, the original text documents, and any associated metadata.

When adding documents, you need to provide:

  • documents: A list of strings (your text corpus).
  • ids: A list of unique string identifiers for each document.

ChromaDB simplifies things by having a default embedding model (all-MiniLM-L6-v2). If you just add documents, it will automatically download this model, convert your text to embeddings, and store them for you.

Chroma - Vector Database for LLM Applications | OpenAI integration

Let's continue with the video to see how to create a collection and add our first documents.

Watch from 02:36 to 04:52. Pay attention to the get_or_create_collection method and the collection.add() method. Notice how ChromaDB automatically downloads an embedding model.

Here's the code to create a collection and add two simple documents:




# Get a collection object, creating it if it doesn't exist.
collection = client.get_or_create_collection(name="my_first_collection")




# Add documents to the collection.
collection.add(
    documents=[
        "This is a document about pineapple.",
        "This is a document about oranges."
    ],
    ids=["id1", "id2"]
)

Step 3: Performing a Similarity Search

Now for the most important part: querying. Once your data is in the collection, you can use the collection.query() method to find the documents that are most semantically similar to your query text.

Key parameters for query():

  • query_texts: A list of strings you want to search for.
  • n_results: The number of similar documents to return for each query.

ChromaDB embeds your query text using the same model as the collection and then performs an efficient ANN search to find the closest matches.

Chroma - Vector Database for LLM Applications | OpenAI integration

The next segment of the video demonstrates how to perform a query and interpret the results.

Watch from 06:04 to 08:46. Focus on how the collection.query() method is used and what the returned results object contains (especially the 'documents' and 'distances' keys).

Here's how you'd query the collection we just made:

results = collection.query(
    query_texts=["fruit from hawaii"],
    n_results=1
)

print(results)

The output will be a dictionary containing the IDs, documents, metadata, and distances of the most similar items. The distances are a measure of dissimilarity (e.g., L2 distance); a smaller distance means a closer match.

Using External Embedding Models

The default model is convenient, but in many real-world applications, you'll want to use a specific, high-performance model, like one from OpenAI. This gives you more control and ensures consistency if you're using the same model elsewhere in your app.

The workflow changes slightly:

  1. You generate the embeddings for your documents yourself using the external model's API.
  2. You add both the documents and the pre-computed embeddings to the collection.
  3. When you query, you embed the query text yourself and pass the resulting query_embeddings to the query method.

Let's see this in action. The video demonstrates this with OpenAI's text-embedding-3-small model.

Chroma - Vector Database for LLM Applications | OpenAI integration

This extended segment demonstrates the full workflow of using an external embedding model with ChromaDB.

This is a longer but crucial segment. Watch from 13:04 to 28:18. It covers: Setting up the OpenAI embedding function (13:04 - 16:07). Adding a document with an explicit embedding (18:08 - 19:45). Querying with an explicit embedding (19:53 - 21:34). Scaling up to add and query 50 documents (21:34 - 28:18). Focus on the difference in the collection.add() and collection.query() calls when you provide the embeddings explicitly.

This process gives you full control over the vector representations of your data, which is essential for building robust RAG systems.

For a different perspective and a complete, text-based example of building a collection and querying it, you can refer to the following article. It uses a dataset of car reviews and integrates the results with an LLM, which is a great preview of what we'll be doing next.

Practical Example: Add Context for a Large Language Model (LLM)

This Real Python article provides a full, practical example from start to finish. It's a great resource to solidify your understanding or review the concepts later.

Skim through the sections 'Prepare and Inspect Your Dataset', 'Create a Collection and Add Reviews', and the subsequent code blocks. Note how it follows the same fundamental steps: prepare data, build a collection, and run queries. You don't need to run this code now, but see how the patterns are the same.

Conclusion

In this lesson, you've bridged the gap between the theory of vector search and the practical application of a vector store. You now have the foundational skills to manage and search embedding data.

Key Takeaways:

  • ChromaDB is a powerful yet easy-to-use vector store for Python developers.
  • The core components are the Client (your connection to the DB) and the Collection (where data is stored).
  • You can add documents using collection.add(), providing documents and unique ids.
  • ChromaDB can automatically embed your text, or you can provide pre-computed embeddings for more control.
  • Similarity search is performed with collection.query(), which finds the n_results most similar documents to your query_texts.
  • Using a PersistentClient is crucial for saving your database to disk so you don't have to re-process your data.

You have now successfully set up a vector store and used it to retrieve documents based on semantic meaning. This retrieval step is the "R" in RAG.

In the next module, Building RAG Systems, we will take the documents you've learned to retrieve today and combine them with a user's prompt to an LLM. Our first lesson will explain the complete Retrieval-Augmented Generation (RAG) pattern and how it helps LLMs generate more accurate, fact-based responses while mitigating hallucination.

Can't find a good explanation? Sign up and we'll make it for you

Sign up