Search engine penuh seperti Elasticsearch atau Lucene menangani jutaan dokumen, mendukung query boolean, phrase, fuzzy, dan ranking ratusan sinyal. Pada proyek ini kita membatasi cakupan secara agresif: satu kata sebagai query, ranking sederhana berbasis TF-IDF, dan korpus berukuran kecil yang muat di memori. Tujuannya adalah memahami fondasi — bukan menyaingi sistem produksi. Pada bagian ini kita menetapkan batas-batas tersebut.

Format Dokumen #

Korpus dimuat sebagai std::vector<std::string> di mana setiap elemen adalah satu dokumen teks utuh. Indeks vektor menjadi ID dokumen (docId). Tidak ada parsing format tambahan — dokumen adalah teks biasa.

cpp
std::vector<std::string> corpus = {
    "Belajar struktur data dengan C++.",
    "Pemrograman modern butuh disiplin.",
    "Struktur data adalah fondasi algoritma.",
    "Algoritma sorting dan searching penting.",
    "C++ mendukung STL yang kaya struktur data."
};

Untuk proyek ini, korpus berisi 5 dokumen. Pada produksi, korpus bisa dimuat dari file teks per baris atau dari JSONL.

Tipe Query #

Query yang didukung adalah single word — satu kata tanpa operator boolean, tanpa phrase, tanpa wildcard. Contoh: "data", "algoritma", "C++". Alasan pembatasan:

  • Simpelnya TF-IDF: skor untuk single word langsung dari rumus TF × IDF. Multi-word butuh kombinasi (sum, max, cosine similarity) yang di luar cakupan.
  • Mudah divalidasi: hasil query single word dapat diperiksa secara visual tanpa ambigu.
  • Cukup untuk konsep: tujuan proyek adalah memahami inverted index, bukan kompleksitas query.
cpp
std::string query = "data";

Skema Ranking TF-IDF #

Ranking memakai TF-IDF (Term Frequency × Inverse Document Frequency):

  • TF(t, d) = frekuensi term t dalam dokumen d, dibagi total term di d. Untuk simplifikasi, kita pakai frekuensi mentah (raw count).
  • IDF(t) = log(N / df(t)) di mana N = total dokumen dan df(t) = jumlah dokumen yang memuat t.
  • Skor(d, t) = TF(t, d) × IDF(t).

Dokumen dengan TF tinggi (kata sering muncul) dan IDF tinggi (kata langka di korpus) mendapat skor tertinggi.

Kontrak Antarmuka #

Berikut kontrak yang akan kita jaga pada Bagian 2-4:

cpp
// Bagian 2 — preprocessing
std::vector<std::string> tokenize(const std::string& text);

// Bagian 3 — inverted index
struct Posting {
    int docId;
    int termFreq;        // frekuensi term dalam dokumen
};
using InvertedIndex = std::unordered_map<std::string, std::vector<Posting>>;

InvertedIndex buildIndex(const std::vector<std::string>& corpus);

// Bagian 4 — query
struct SearchResult {
    int docId;
    double score;
};

std::vector<SearchResult> search(
    const InvertedIndex& index,
    const std::vector<std::string>& corpus,
    const std::string& word);

Diagram Alur #

Alur pemrosesan dari korpus hingga hasil query:

Setiap tahap di diagram diimplementasikan pada satu Bagian: tokenisasi di Bagian 2, inverted index di Bagian 3, dan query+TF-IDF di Bagian 4.

Ukuran Korpus dan Batasan #

AspekNilaiCatatan
Jumlah dokumen5–100cukup untuk demonstrasi, tidak muat untuk produksi
Panjang dokumen5–50 katakorpus mini
Tipe querysingle wordtanpa boolean, phrase, atau wildcard
BahasaIndonesia / Englishpreprocessing tidak spesifik bahasa
Skema rankingTF-IDF sederhanatanpa learning-to-rank

Ringkasan Bagian 1 #

Kita telah menetapkan: korpus sebagai std::vector<std::string>, query single word, ranking TF-IDF sederhana, dan kontrak antarmuka tokenize → buildIndex → search. Batasan yang kita akui: query single word, ranking sederhana, dan korpus kecil di memori. Pada Bagian 2 kita membangun pipeline preprocessing yang menghasilkan token bersih.