A small search engine built from scratch in Python. It demonstrates the core information retrieval pipeline: tokenization, inverted indexing, Boolean and phrase queries, TF IDF ranking, and JSON document storage.
The implementation is intentionally compact enough to follow from source documents to ranked results without relying on an external search engine library.
flowchart LR
A[JSON document collection] --> B[Document store]
B --> C[Tokenizer]
C --> D[Inverted index]
E[Search query] --> F[Query parser]
F --> G[Candidate selection]
D --> G
G --> H[TF IDF ranker]
D --> H
H --> I[Ranked results]
An inverted index maps each term to the documents that contain it, along with its term frequency. This avoids scanning every document just to find candidate matches.
The included corpus supports queries such as:
python
python AND search
python OR language
"search systems"
- Text tokenization and normalization
- Stop word filtering
- Inverted index with term frequencies and document lengths
- AND and OR queries
- Quoted phrase queries
- TF IDF relevance scoring
- Deterministic result ordering
- JSON persistence that rebuilds the index from source documents
- Command line interface
- Unit, persistence, query, ranking, and CLI integration tests
- Continuous integration on Python 3.11, 3.12, 3.13, and 3.14
Create a virtual environment:
python -m venv .venvActivate it using the command for your shell.
macOS or Linux:
source .venv/bin/activateWindows PowerShell:
.venv\Scripts\Activate.ps1Install the project and development tools:
python -m pip install -e ".[dev]"Run the test suite and lint check:
python -m pytest
python -m ruff check src testsSearch the included collection:
search-engine examples/documents.json "python"Example output:
0.251804 1 Python is a programming language designed for readable software.
0.251804 4 Python can be used to build search systems.
The columns are score, document ID, and document text. Equal scores are ordered by ascending document ID. You can also run the CLI as a module:
python -m search_engine.cli examples/documents.json "python AND search"- Bare terms use OR semantics by default.
ANDrequires all query terms to match;ORaccepts matches from either side.- Mixing
ANDandORin the same query is rejected. - Quoted phrases must appear as adjacent tokens after tokenization and stop word filtering.
- A phrase combined with unquoted terms uses AND semantics unless you explicitly use
OR.
Phrase matching filters candidate documents. Results are ranked using the query terms, not phrase proximity or a learned relevance model.
The modules have distinct responsibilities:
document.pydefines the document record and validates its fields.tokenizer.pynormalizes text into searchable terms and removes stop words.index.pybuilds the inverted index and stores term frequencies and document lengths.query.pyparses Boolean and phrase queries and selects candidate documents.ranking.pycalculates TF IDF scores and orders results deterministically.engine.pycoordinates indexing, querying, and ranking through the public API.storage.pypersists source documents as JSON and rebuilds the index when loading.cli.pyexposes search through the command line.
The implementation uses Python dictionaries and sets so the retrieval structures remain visible rather than hidden behind a search framework.
For each unique query term present in a document, the ranker adds:
(term frequency / searchable document length)
* (log((number of documents + 1) / (document frequency + 1)) + 1)
Results are sorted by descending score and then ascending document ID. Deduplicating query terms prevents repeated words from artificially increasing a document's score.
The tests cover tokenization, index construction, duplicate IDs, document lengths, ranked retrieval, Boolean and phrase queries, empty queries, invalid input, JSON persistence, deterministic ties, repeated query terms, and the CLI workflow.
CI installs the package, runs pytest, and checks the source and tests with Ruff on Python 3.11, 3.12, 3.13, and 3.14.
This is an educational search engine, not a replacement for Lucene or Elasticsearch. Tokenization currently focuses on ASCII letters and digits, so multilingual text normalization is limited. Phrase matching operates on document text rather than a positional index, and the project has no benchmark results or claims about large-corpus performance.
Potential extensions include broader Unicode-aware tokenization, positional postings, reproducible benchmarks on a defined corpus, and experiments with larger collections.
MIT