What this is
No new concepts in this lesson. This is a checkpoint: a small, real script built entirely out of ideas from Lessons 1 through 8, combined into one thing. If you can read the code and explain why every piece is there, you've mastered the Beginner tier. If any piece feels unfamiliar, that's a sign to revisit the lesson it came from before continuing to Intermediate.
What it does
Loads the same notes.txt used throughout this tier, embeds and stores it in Postgres with a category tag per chunk, then runs a handful of example searches: a plain semantic search, a category-filtered search, and a search after one note has been edited and re-embedded, printing each result set so you can see the whole lifecycle in one run.
Where each piece came from
conn.execute("CREATE EXTENSION IF NOT EXISTS vector")register_vector(conn)Lesson 1 (enabling the extension) and Lesson 3 (register_vector, teaching the connection to translate Python lists to and from vector).
CREATE TABLE notes ( id bigserial PRIMARY KEY, content text NOT NULL, category text NOT NULL, embedding vector(768))Lesson 3 (the vector(N) column type) and Lesson 7 (a plain metadata column living alongside it).
embeddings_model = GoogleGenerativeAIEmbeddings( model="models/gemini-embedding-001", output_dimensionality=EMBEDDING_DIMENSIONS,)vectors = embeddings_model.embed_documents(chunks)Lesson 4 (output_dimensionality, embed_documents, storing real embeddings instead of toy ones), combined with Lesson 2 (parameterized queries) and Lesson 4's executemany for the bulk insert.
def search(conn, embeddings_model, query, k, category=None): query_vector = Vector(embeddings_model.embed_query(query)) if category is None: return conn.execute( "SELECT content, embedding <=> %s AS distance FROM notes ORDER BY distance LIMIT %s", (query_vector, k), ).fetchall() return conn.execute( "SELECT content, embedding <=> %s AS distance FROM notes " "WHERE category = %s ORDER BY distance LIMIT %s", (query_vector, category, k), ).fetchall()Lesson 5 (<=>, the cosine distance operator), Lesson 6 (ORDER BY ... LIMIT k as the whole top-k search), and Lesson 7 (the optional category filter narrowing before ranking).
conn.execute( "UPDATE notes SET content = %s, embedding = %s WHERE id = %s", (new_content, new_vector, recipe_id),)Lesson 8 (always re-embedding together with any content edit).
Running the script produces: a plain search correctly finding the pizza dough chunk for a question about bread (no keyword overlap, same trick as LangChain Lesson 28), a category-filtered search finding the best match within garden even though it isn't the closest match overall, and a search result changing after a note is edited and re-embedded.
Checkpoint
- A full ingest-search-edit-reembed lifecycle over six real chunks, built entirely from Lessons 1-8.
- Category-filtered search correctly narrows before ranking, rather than ranking globally and filtering after.
- Editing a note and re-embedding it changes future search results; skipping the re-embed would not.
Try this yourself, without looking anything up: add a new chunk of your own with its own category, does it show up correctly when you search for something related? Then delete a note and confirm a search that used to return it no longer does.
If you can make these changes confidently, you're ready for the Intermediate tier, starting at Lesson 10, where six rows stop being the whole story and search actually needs to scale.