Skip to content
Forward Deployed

Forward Deployed
Lessons and contents
Vector Stores & Index OperationsLesson 1 of 7
7 lessons
Learn

Vector Stores & Index Operations: overview

Source
From the RAG & Agents Engineering course
By
Sourav Banerjee
Last updated
8 Nov 2026

Report an issue

A vector store is a database. Each entry holds a vector, the text it came from and that text's metadata, and you add, read, filter, delete and persist entries the way you would rows. The hard part is keeping the store in step with source files that keep changing. The T5.5 notebooks build on four ideas: approximate nearest-neighbour (ANN) search, collections and namespaces, metadata filters, and idempotent re-indexing.

The running example is the Larkspur Cloud handbook kept as a folder of Markdown files: billing.md, refunds.md, outages.md, servers.md and data.md, ten chunks in all, with the same chunk texts as the T5.1 lab. A sixth file, support.md, arrives later. The event that drives the module is a policy change: the refund window for annual plans drops from 30 days to 14. The golden question is How long do I have to get a full refund on an annual plan?, and after the change the index must return the 14-day rule and nothing else.

In short

  • A vector store can add, search, filter and delete. It cannot update text in place: new text means a new vector, so an update is a delete followed by an insert, and both need an ID that stays the same from one run to the next.
  • Scores mean different things on different backends. Chroma and FAISS return a distance from similarity_search_with_score (lower is closer), and Pinecone's cosine index returns a similarity (higher is closer). A threshold tuned on one is meaningless on another.
  • Large indexes use ANN structures such as HNSW and IVF, which trade a little recall for a lot of speed. LangChain's FAISS.from_documents builds an exact IndexFlatL2; Chroma builds HNSW.
  • A metadata filter applied before ranking returns up to k matches. One applied after ranking can return fewer, or none. LangChain's FAISS wrapper filters after fetching 20 candidates; PGVector filters in SQL before it ranks.
  • The Indexing API hashes every chunk and records the hash. In the lab, editing one refund chunk costs one embedding instead of ten, and cleanup="incremental" deletes the old 30-day chunk. With cleanup=None both rules stay in the store.

Mental model

The living index Sync lane: index() loads 10 chunks from the handbook folder and hashes each one from its text and source. The record manager already holds 9 of those hashes, so 9 are skipped and 1, the edited 14-day refund rule, is embedded and added. Every key is stamped with the run’s time, and incremental cleanup deletes the 30-day refund chunk, whose key the run never touched. Query lane: a question with a metadata filter searches the collection and ANN search returns the top k chunks. Sync: every index() run Query: every question Source folder 10 chunks Hashtext + source Record managerkey: hash, time 10 keys stamped Skip 9 skipped Embed and add 1 added: 14 days Clean up 1 deleted: 30 days Collection 10 chunks Question + filter ANN search, top k
  1. The loader returns the handbook folder as 10 chunks. refunds.md now says 14 days where it said 30.
  2. Each chunk gets an ID from a hash of its text and its source. Same text, same ID.
  3. The record manager already holds 9 of the 10 hashes. Those 9 are skipped and never re-embedded.
  4. One hash is new: the 14-day refund rule. Only that chunk is embedded and added to the collection.
  5. All 10 keys are stamped with this run’s time, the skipped ones included.
  6. Incremental cleanup lists keys from refunds.md older than the run. The 30-day chunk is one, so it is deleted.
  7. A question with a metadata filter searches the collection, and ANN search returns the top k chunks.

The store is the system of record for what retrieval can return, so every change in the source folder has to reach it. The record manager is what makes that cheap: it remembers each chunk's hash, so a run embeds only the chunks whose text or source changed, and its cleanup step deletes the chunks the folder no longer contains. A stale chunk left in the collection is as retrievable as a current one.

Interactive lab

The record manager and cleanup modes: open the file in a browser. Run index() once to embed all 10 chunks. Set refunds.md to 14 days and run with cleanup None: 1 chunk is added, none is deleted, and the store now holds the 30-day rule and the 14-day rule side by side. Switch to incremental and run again to delete the stale one. Then mark outages.md Deleted and run under incremental (nothing is deleted) and under full (its 2 chunks go).

How it connects

  • Before: T5.2 sets the source metadata the record manager groups by and the flat metadata types the stores accept; T5.3 decides what a chunk is, and so what gets hashed; T5.4 picks the embedding model, which has to match the index for as long as the index lives.
  • Next: T5.6 indexes more than one representation per document (parent documents, summaries), which needs the stable IDs from lesson 1 of T5.5; T6.1 builds retrieval strategies (MMR, thresholds, hybrid search) on top of as_retriever().

Key Takeaways

  • Give every chunk an ID derived from the document: its source and position, or a hash of its content. Without one you cannot update, delete or de-duplicate.
  • Read what a score means on your backend before you set a threshold, and log how many results a filtered search returned.
  • Filter before the search when the filter is selective; after-search filtering silently returns fewer than k.
  • Sync with the Indexing API and cleanup="incremental" keyed on source. Use full only when the loader returns the whole corpus, and never on a batch that might be empty.

Rate your confidence and keep notes on the module page.