Python Search Engine

Python CLI Information Retrieval BM25 Inverted Index pickle NLP

O Python Search Engine é um motor de busca handmade em Python, construído do zero com armazenamento local e interface de linha de comando. Implementa um índice invertido binário serializado com pickle e o algoritmo de ranqueamento BM25 para indexar e recuperar documentos de texto (.txt) — sem dependências de motores externos (Lucene, Elasticsearch, etc). The Python Search Engine is a handmade search engine in Python, built from scratch with local storage and a command-line interface. It implements a binary inverted index serialized with pickle and the BM25 ranking algorithm to index and retrieve text documents (.txt) — with no external engine dependencies (Lucene, Elasticsearch, etc).

Ver no GitHubView on GitHub

Escopo da Fase 1Phase 1 scope

Layout do armazenamentoStorage layout

.search_data/
  docs/
    <doc_id>.txt
  index/
    inverted_index.pkl
    meta.json

Cada documento adicionado recebe um doc_id sequencial e é copiado para docs/. O índice invertido (inverted_index.pkl) mapeia termos → postings e fica serializado em binário; metadados (estatísticas de coleção, comprimento médio de documento, etc) ficam em meta.json. Each document gets a sequential doc_id and is copied into docs/. The inverted index (inverted_index.pkl) maps terms → postings and is serialized in binary; collection metadata (statistics, average document length, etc) lives in meta.json.

Comandos da CLICLI commands

ComandoCommand FunçãoFunction
initInicializa o armazenamento (.search_data/).Initializes the storage (.search_data/).
add --file <path>Adiciona um documento a partir de um arquivo.Adds a document from a file.
add --text "..."Adiciona texto bruto diretamente.Adds raw text directly.
add --dir <path>Adiciona, recursivamente, todos os .txt de um diretório.Recursively adds all .txt files in a directory.
search --query "..." --top-k NBusca a query e retorna os N documentos mais relevantes (BM25).Searches the query and returns the top-N most relevant documents (BM25).
listLista todos os documentos indexados.Lists all indexed documents.
statsMostra estatísticas da coleção.Shows collection statistics.
remove --doc-id NRemove um documento pelo doc_id.Removes a document by doc_id.
--data-dir <path>Opcional. Usa um diretório de dados customizado em qualquer subcomando.Optional. Uses a custom data directory on any subcommand.

Instalação e usoInstall and usage

1. Clonar o repositório1. Clone the repository

$ git clone https://github.com/guilhermefelipetto/python-search-engine.git
$ cd python-search-engine

2. Inicializar o armazenamento2. Initialize the storage

$ python search_cli.py init

3. Criar uma pasta de testes local3. Create a local test folder

$ mkdir local_docs
local_docs/ está no .gitignore, então pode jogar qualquer documento de teste ali sem risco de comitar. local_docs/ is in .gitignore, so you can drop any test document there without risk of committing it.

4. Adicionar documentos4. Add documents

# A partir de um arquivoFrom a file
$ python search_cli.py add --file ./local_docs/my_doc.txt

# Texto bruto direto na CLIRaw text inline
$ python search_cli.py add --text "python bm25 inverted index"

# Diretório inteiro (recursivo)Entire directory (recursive)
$ python search_cli.py add --dir ./local_docs

5. Buscar5. Search

$ python search_cli.py search --query "python search" --top-k 5

Se nenhum documento estiver indexado, a CLI emite um aviso e encerra. If no document is indexed, the CLI prints a warning and exits.

6. Outras operações6. Other operations

$ python search_cli.py list                 # lista documentoslist documents
$ python search_cli.py stats                # estatísticas da coleçãocollection stats
$ python search_cli.py remove --doc-id 3    # remove um documentoremove a document

Diretório de dados customizado (opcional)Custom data directory (optional)

$ python search_cli.py --data-dir ./my_local_engine init

Como funciona internamenteHow it works internally

Quando um documento é adicionado, o texto é tokenizado, stopwords são removidas e cada termo é registrado no índice invertido (estrutura que mapeia termo → lista de (doc_id, frequência, ...)). A estrutura é serializada em binário com pickle, o que torna a leitura e escrita extremamente rápidas em comparação a formatos textuais como JSON. When a document is added, the text is tokenized, stopwords are removed, and each term is registered in the inverted index (a structure that maps term → list of (doc_id, frequency, ...)). The structure is serialized in binary with pickle, which makes reads and writes far faster than text-based formats like JSON.

Durante a busca, o algoritmo BM25 calcula um score de relevância para cada documento que contém termos da query, ponderando: (i) a frequência do termo no documento (TF saturada), (ii) a raridade do termo na coleção (IDF) e (iii) o comprimento do documento relativo à média (normalização). Os top-k documentos com maior score são retornados. During search, the BM25 algorithm computes a relevance score for each document containing query terms, weighting: (i) term frequency in the document (saturated TF), (ii) term rarity across the collection (IDF), and (iii) document length relative to the collection average (length normalization). The top-k documents with highest scores are returned.

MotivaçãoMotivation

Este projeto foi desenvolvido para estudar, na prática, os fundamentos de Recuperação de Informação. Construir um motor de busca do zero é a forma mais direta de internalizar como funcionam a tokenização, a construção do índice invertido e a matemática por trás de algoritmos clássicos de ranqueamento como o BM25 — coisas que normalmente ficam escondidas atrás da API de motores prontos. A escolha por pickle ao invés de JSON foi deliberada: o objetivo era também experimentar serialização binária e o trade-off de velocidade × portabilidade. This project was built to study the fundamentals of Information Retrieval hands-on. Building a search engine from scratch is the most direct way to internalize how tokenization, inverted index construction, and the math behind classic ranking algorithms like BM25 actually work — things normally hidden behind a high-level search engine API. The choice of pickle over JSON was deliberate: part of the goal was experimenting with binary serialization and its speed × portability trade-off.