CodeRigor

LESSON

Capstone: a complete searchable notes tool

Compose corpus validation, indexing, ranking and plain-text results; run the downloadable CLI and evaluate it against bundled relevance judgments.

The capstone connects the pieces into a small tool you can actually use. Supply local JSONL notes, type a query and receive ranked JSON result records. The same library runs in graded exercises; the downloadable lab adds the command-line boundary, a small corpus, a judgment set and executable tests. Nothing needs a model subscription, database service or network connection.

Composition is where earlier policies are easiest to lose. A hardcoded k hides useful results; a hardcoded snippet width breaks an output budget; matching titles instead of bodies changes the indexed field. Keep the existing stages visible and pass the caller’s parameters through unchanged.

One explicit search pipeline
start / 3
The CLI reads a local snapshot; deterministic stages produce ranked, bounded result data.
One explicit search pipeline
The CLI reads a local snapshot; deterministic stages produce ranked, bounded result data.

The library’s search_notes function receives in-memory records. Local file reads belong in main, where malformed JSON, missing files and useful exit codes can be handled deliberately. That split lets unit tests cover search without creating files, while separate CLI tests verify the actual user command and its failure behavior.

Use the bundled corpus first. It includes repeated words, an empty document, a Unicode case-fold example and a note whose title matches a query but whose body does not. Those are not arbitrary edge cases: they pin the boundaries that make this particular search engine explainable.

Python
import json
line = '{"id":"n1","title":"Indexing","text":"red red blue"}'
record = json.loads(line)
print(record['id'], len(record['text'].split()))

Keep the relevance queries and labels as data beside the corpus. The lab’s evaluate command reports per-query precision, recall and reciprocal rank plus their arithmetic means. Those means are descriptive summaries of the supplied queries; they do not establish performance on an unseen population. Try one ranking or tokenization change at a time, retain the old outputs, and explain both improvements and regressions.

The current implementation rebuilds an in-memory index for each search call. That favors a small transparent program over scale. A long-running process could cache a snapshot and index, but would then need explicit update, invalidation and memory-budget policies. Those are useful extensions to specify after the baseline is correct.

A useful experiment keeps its exam fixed
start / 3
Change one implementation choice while retaining the same corpus, queries and independent judgments.
A useful experiment keeps its exam fixed
Change one implementation choice while retaining the same corpus, queries and independent judgments.
Python
metrics = [{'reciprocal_rank': 1.0}, {'reciprocal_rank': 0.5}]
mean_rr = sum(row['reciprocal_rank'] for row in metrics) / len(metrics)
print(mean_rr)

Download the lab and run its unittest suite, a search command and the evaluation command from the README. Then add one new query with independent judgments and an input case that challenges your current tokenizer. Keep the expected behavior in a test. You now have a complete free lexical tool and a measured starting point for the optional semantic-search continuation.

Download the complete lab. The archive includes runnable code, tests, fixtures and a README.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Use the text representation and TF–IDF sections. The lab states its own exact tokenization and weighting conventions.