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.
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.
std::string query = "data";Skema Ranking TF-IDF #
Ranking memakai TF-IDF (Term Frequency × Inverse Document Frequency):
- TF(t, d) = frekuensi term
tdalam dokumend, dibagi total term did. Untuk simplifikasi, kita pakai frekuensi mentah (raw count). - IDF(t) =
log(N / df(t))di manaN= total dokumen dandf(t)= jumlah dokumen yang memuatt. - 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:
// 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 #
| Aspek | Nilai | Catatan |
|---|---|---|
| Jumlah dokumen | 5–100 | cukup untuk demonstrasi, tidak muat untuk produksi |
| Panjang dokumen | 5–50 kata | korpus mini |
| Tipe query | single word | tanpa boolean, phrase, atau wildcard |
| Bahasa | Indonesia / English | preprocessing tidak spesifik bahasa |
| Skema ranking | TF-IDF sederhana | tanpa 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.