Stop 14 · Retrieval-Augmented Generation & Vector DatabasesProjects · 2/2

Document Search Engine

Part of the AI Engineer Roadmap

In short

Sometimes people want the right document, not a chatbot's answer. A search engine over PDFs, notes or policies, ranked by meaning and filterable by type, is a practical project that teaches retrieval deeply.

A semantic search tool over a document collection — a simpler sibling to the Q&A chatbot that focuses purely on retrieval quality without generation.

The blob pulls open a drawer of a wooden library card catalogue and takes out a card.

Think of it like this

The library card catalogue

Old libraries had wooden cabinets of index cards. Each card pointed to a book, with the subject, author and shelf. You'd flip to the right drawer and find the card in seconds. Your search engine is a modern catalogue that also understands what you meant.

Watch it happen

One search request

User
Search API
Index

q=exam rules&dept=cse

1/6The user searches 'exam rules' and filters to the CSE department.

The big ideas

What you need to know

Results are documents

Search chunks, but show documents. Group hits by file and show the best snippet from each.

Filters matter

Department, year and file type filters make search useful. Store them as metadata at index time.

Hybrid by default

Users type codes, names and natural phrases. Combining keyword and vector search handles all three.

Fast feels smart

Aim for results in well under a second. Cache embeddings for popular queries.

See it in code

Walk through the code

python
1from collections import defaultdict2 3def search(collection, query: str, dept: str | None = None, k: int = 20):4    where = {"dept": dept} if dept else None5    hits = collection.query(query_texts=[query], n_results=k, where=where)6 7    best = defaultdict(lambda: None)8    for doc, meta, dist in zip(hits["documents"][0], hits["metadatas"][0], hits["distances"][0]):9        f = meta["file"]10        if best[f] is None or dist < best[f]["dist"]:11            best[f] = {"file": f, "page": meta["page"], "snippet": doc[:200], "dist": dist}12 13    return sorted(best.values(), key=lambda r: r["dist"])[:5]

1/3Apply the department filter inside the vector query.

Search chunks in Chroma with an optional filter, then keep the closest chunk per file as its snippet.

Save yourself the pain

Common mistakes

Oops

Showing 20 chunks where 15 come from the same PDF.

Do this instead

Group by document so users see a variety of relevant files, each with one strong snippet.

Oops

Forgetting to store page numbers at index time.

Do this instead

Add file and page metadata when chunking. You can't recover it later without re-indexing.

Your mission

First milestone: grouped results

~45 min

Quick check

Why group chunk results by document in a search engine?

Go deeper

  1. 1Index a real document collection and build a search UI with relevance-ranked results.
  2. 2Add filters (date, category, source) alongside semantic search.
  3. 3Evaluate ranking quality with a small set of known relevant results per query.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge