Sebelum satu pun query dijalankan, kualitas pencarian sudah ditentukan oleh schema. Field yang tidak ada tidak bisa dicari; field yang salah tempat membuat index membengkak tanpa manfaat. Bagian ini merancang collection katalog untuk toko buku dan alat tulis: kita memisahkan field teks bebas dari field filter terstruktur, memilih field mana yang masuk text index, lalu mengisi 60 produk sebagai bahan kerja empat bagian ke depan.
Pola Akses Katalog Pencarian #
Pengunjung toko kita datang dengan dua pertanyaan. Pertama: “ada produk bernama atau berdeskripsi seperti ini?” — dijawab pencarian teks. Kedua: “tampilkan buku kategori tertentu di rentang harga ini” — dijawab filter terstruktur. Schema berikut memisahkan keduanya sejak awal:
| Field | Tipe | Peran dalam pencarian |
|---|---|---|
nama | string | Teks bebas — kandidat utama text index |
deskripsi | string | Teks bebas — memperkaya kata kunci yang cocok |
kategori | string | Filter tepat: "buku" atau "alat tulis" |
harga | number | Filter rentang dan urutan harga |
tag | array string | Pencocokan tepat multi nilai |
Pemisahan ini bukan kosmetik. Pencarian teks memakai text index; filter kategori + harga memakai compound index B-tree; tag cukup dilayani index multikey. Tiga mesin berbeda, tiga kelompok field berbeda — menyatukannya dalam satu field kata_kunci justru membuat semua query lambat.
Tipe datanya ikut menentukan apa yang bisa dipertanyakan. harga disimpan sebagai number — bukan string berpemisah titik — sehingga rentang $gte/$lte dan sort({ harga: 1 }) membandingkan nilai, bukan urutan alfabetis. kategori adalah string pendek yang diulang di banyak document, cocok untuk equality; tag berupa array yang satu produk boleh memuat beberapa nilai sekaligus.
Memilih Field untuk Text Index #
Text index kita mencakup nama dan deskripsi, dan hanya keduanya:
db.katalog.createIndex(
{ nama: "text", deskripsi: "text" },
{ default_language: "none" }
)Tiga keputusan ada di dalamnya:
namadandeskripsidigabung satu index — satu collection hanya boleh memiliki satu text index (kita buktikan penolakannya di Bagian 3), jadi semua field teks yang ingin dicari harus masuk sejak awal.tagsengaja di luar — tag adalah pencocokan tepat (tag: "sekolah"), bukan teks bebas; memasukkannya ke text index hanya menambah token tanpa menambah relevansi.default_language: "none"— katalog kita berbahasa Indonesia, dan daftar bahasa text index tidak memuatnya. Modenonemematikan stemming dan stop words bahasa Inggris sehingga kata dicocokkan mentah — persis seperti yang kita butuhkan, seperti yang kita pelajari di chapter Text Index.
Skrip Seed #
// katalog-seed.js — data awal proyek pencarian produk
// Jalankan dari folder proyek:
// mongosh "mongodb://127.0.0.1:27017/?directConnection=true" --file src/katalog-seed.js
var dbp = db.getSiblingDB("proyek_pencarian")
function resetDatabase() {
dbp.dropDatabase()
print("database direset: proyek_pencarian")
}
function idProduk(n) {
return ObjectId("67c1000000000000000000" + n.toString().padStart(2, "0"))
}
function produk(n, nama, deskripsi, kategori, harga, tag) {
return { _id: idProduk(n), nama: nama, deskripsi: deskripsi, kategori: kategori, harga: harga, tag: tag }
}
function isiKatalog() {
const hasil = dbp.katalog.insertMany([
// --- buku (34) ---
produk(1, "Belajar MongoDB dari Nol", "Panduan praktis database document untuk pemula dengan contoh langsung.", "buku", 89000, ["pemrograman", "database"]),
produk(2, "Pemrograman JavaScript Modern", "Membahas sintaks terbaru, async, dan pola desain aplikasi web.", "buku", 115000, ["pemrograman", "javascript"]),
produk(3, "Python untuk Analisis Data", "Pengolahan data dan visualisasi dengan pustaka Python.", "buku", 125000, ["pemrograman", "data"]),
produk(4, "Struktur Data dan Algoritma", "Latihan berpikir komputasional dengan contoh kode terurai.", "buku", 98000, ["pemrograman", "informatika"]),
produk(5, "Desain Database Relasional", "Normalisasi dan pemodelan data untuk aplikasi skala besar.", "buku", 105000, ["database", "desain"]),
produk(6, "Merancang Antarmuka Pengguna", "Prinsip desain antarmuka yang jelas dan mudah dipakai.", "buku", 135000, ["desain", "produk"]),
produk(7, "Tipografi untuk Desainer", "Memilih dan menyusun huruf dalam media cetak dan digital.", "buku", 87000, ["desain", "tipografi"]),
produk(8, "Teknik Sketsa dengan Pensil untuk Pemula", "Latihan garis, arsir, dan bayangan langkah demi langkah.", "buku", 76000, ["seni", "sketsa"]),
produk(9, "Panduan Menggambar untuk Pemula", "Tutorial menggambar objek sehari-hari dengan pensil dan kertas.", "buku", 72000, ["seni", "tutorial"]),
produk(10, "Sejarah Nusantara Lengkap", "Perjalanan kerajaan hingga era kemerdekaan dalam satu jilid.", "buku", 150000, ["sejarah", "referensi"]),
produk(11, "Atlas Dunia Edisi Berwarna", "Peta detail benua dan negara lengkap dengan data statistik.", "buku", 175000, ["referensi", "atlas"]),
produk(12, "Kamus Bahasa Indonesia", "Rujukan ejaan dan makna kata sebanyak 80 ribu lema.", "buku", 95000, ["referensi", "bahasa"]),
produk(13, "Matematika Diskrit", "Logika, himpunan, dan teori graf untuk mahasiswa informatika.", "buku", 110000, ["matematika", "kuliah"]),
produk(14, "Kalkulus Dasar", "Turunan dan integral disertai ratusan soal latihan.", "buku", 89000, ["matematika", "kuliah"]),
produk(15, "Fisika untuk SMA", "Ringkasan materi gerak dan listrik magnet untuk persiapan ujian.", "buku", 78000, ["sains", "sekolah"]),
produk(16, "Kimia Organik Pengantar", "Struktur dan reaksi senyawa karbon untuk pemula.", "buku", 92000, ["sains", "kuliah"]),
produk(17, "Biologi Sel dan Molekuler", "Seluk-beluk sel makhluk hidup dengan ilustrasi berwarna.", "buku", 128000, ["sains", "kuliah"]),
produk(18, "Menulis Fiksi Secara Bertahap", "Dari ide cerita hingga revisi naskah bersama editor.", "buku", 83000, ["menulis", "fiksi"]),
produk(19, "Menulis Puisi Setiap Hari", "Latihan rutin menumbuhkan kebiasaan menulis puisi.", "buku", 65000, ["menulis", "puisi"]),
produk(20, "Tata Bahasa Baku", "Pedoman ejaan dan tata kalimat bahasa Indonesia.", "buku", 68000, ["bahasa", "referensi"]),
produk(21, "Resep Masakan Rumahan", "Seratus resep sayur dan lauk untuk keluarga.", "buku", 97000, ["memasak", "resep"]),
produk(22, "Kue Praktis Tanpa Oven", "Cara membuat kue kukus dan goreng dalam 30 menit.", "buku", 75000, ["memasak", "kue"]),
produk(23, "Kelola Keuangan Pribadi", "Anggaran, tabungan, dan investasi untuk usia muda.", "buku", 88000, ["keuangan", "pengembangan diri"]),
produk(24, "Psikologi Kognitif Pengantar", "Bagaimana manusia memproses informasi dan mengambil keputusan.", "buku", 112000, ["psikologi", "kuliah"]),
produk(25, "Novel: Senja di Pelabuhan", "Kisah nelayan muda memilih arah hidup di kota pesisir.", "buku", 69000, ["fiksi", "novel"]),
produk(26, "Novel: Rumah di Ujung Jalan", "Drama keluarga tentang rumah warisan yang diperebutkan.", "buku", 71000, ["fiksi", "novel"]),
produk(27, "Kumpulan Puisi Hujan", "Puisi-puisi tentang hujan, kota, dan kerinduan.", "buku", 54000, ["puisi", "sastra"]),
produk(28, "Biografi Penemu Dunia", "Kisah dua puluh penemu yang mengubah cara hidup manusia.", "buku", 118000, ["biografi", "sejarah"]),
produk(29, "Ensiklopedia Sains Anak", "Jawaban ilmiah atas pertanyaan anak dengan gambar menarik.", "buku", 145000, ["sains", "anak"]),
produk(30, "Buku Aktivitas Anak: Mewarnai", "Berisi latihan mewarnai dengan pensil warna dan krayon.", "buku", 35000, ["anak", "aktivitas"]),
produk(31, "Panduan Belajar Cepat", "Teknik mencatat dan mengulang materi secara efektif.", "buku", 59000, ["pengembangan diri", "belajar"]),
produk(32, "Produktivitas Tanpa Gadget", "Membangun kebiasaan fokus di tengah gangguan digital.", "buku", 77000, ["pengembangan diri", "produktivitas"]),
produk(33, "Statistika Terapan", "Statistika deskriptif dan inferensial untuk penelitian.", "buku", 102000, ["matematika", "kuliah"]),
produk(34, "Geografi Wilayah Pesisir", "Pola ruang pesisir dan pengelolaan sumber daya laut.", "buku", 86000, ["geografi", "kuliah"]),
// --- alat tulis (26) ---
produk(35, "Pensil Kayu HB", "Kayu jati dengan inti hitam pekat dan tajam tahan lama.", "alat tulis", 6000, ["sekolah", "menulis"]),
produk(36, "Pensil Mekanik 0.5 mm", "Pensil isi ulang dengan tombol dorong dan penghapus ujung.", "alat tulis", 25000, ["sekolah", "teknis"]),
produk(37, "Pensil Warna 24 Warna", "Set pensil warna halus untuk mewarnai dan menggambar.", "alat tulis", 45000, ["anak", "seni"]),
produk(38, "Rautan Pensil Dua Lubang", "Dua ukuran mata rautan untuk pensil tebal dan tipis.", "alat tulis", 8000, ["sekolah", "teknis"]),
produk(39, "Buku Tulis 58 Lembar", "Kertas rata mulus untuk pensil.", "alat tulis", 5500, ["sekolah", "menulis"]),
produk(40, "Buku Gambar A4 Blanko", "Kertas 120 gram tanpa garis untuk sketsa dan gambar bebas.", "alat tulis", 6500, ["seni", "sekolah"]),
produk(41, "Kertas Gambar A3 160 gram", "Kertas tebal yang cocok untuk pensil, tinta, dan cat air.", "alat tulis", 12000, ["seni", "teknis"]),
produk(42, "Pulpen Gel Hitam 0.5", "Tinta gel cepat kering dengan pegangan empuk.", "alat tulis", 9500, ["kantor", "menulis"]),
produk(43, "Pulpen Bola Biru", "Ujung bola 1 mm dengan tinta biru pekat.", "alat tulis", 4500, ["kantor", "menulis"]),
produk(44, "Spidol Permanen Hitam", "Tinta permanen menempel di kertas, karton, dan plastik.", "alat tulis", 12000, ["kantor", "presentasi"]),
produk(45, "Spidol Whiteboard Bulat", "Tinta kering mudah dihapus dari papan tulis putih.", "alat tulis", 11000, ["kantor", "presentasi"]),
produk(46, "Penghapus Karet Putih", "Karet lembut menghapus bekas pensil tanpa merusak kertas.", "alat tulis", 6000, ["sekolah", "menulis"]),
produk(47, "Stabilo Highlighter Kuning", "Ujung lebar menyorot teks dengan tinta kuning terang.", "alat tulis", 14000, ["kantor", "belajar"]),
produk(48, "Penggaris Besi 30 cm", "Besi tahan bengkok dengan skala jelas.", "alat tulis", 8500, ["sekolah", "teknis"]),
produk(49, "Penggaris Segitiga Set", "Dua penggaris segitiga untuk menggambar sudut.", "alat tulis", 7500, ["teknis", "seni"]),
produk(50, "Busur Derajat 15 cm", "Mengukur sudut dengan skala jelas dan badan bening.", "alat tulis", 7000, ["teknis", "sekolah"]),
produk(51, "Jangka Sikat", "Jangka berkaki logam untuk menggambar lingkaran.", "alat tulis", 13500, ["teknis", "seni"]),
produk(52, "Gunting Kertas Stainless", "Baja tahan karat dengan pegangan empuk kanan kiri.", "alat tulis", 18000, ["kantor", "seni"]),
produk(53, "Lem Batang 21 gram", "Lem batang tanpa bau untuk kertas dan karton.", "alat tulis", 9500, ["kantor", "sekolah"]),
produk(54, "Stapler Besar 40 Lembar", "Stapler logam dengan anvil datar untuk menjilid tebal.", "alat tulis", 32000, ["kantor", "arsip"]),
produk(55, "Isi Stapler 24/6", "Paku staples isi ulang sepuluh ribu lembar per kotak.", "alat tulis", 7500, ["kantor", "arsip"]),
produk(56, "Map Plastik Kancing", "Map plastik tebal dengan kancing pengunci dokumen.", "alat tulis", 4500, ["kantor", "arsip"]),
produk(57, "Binder Ring 2 inci", "Binder keras memuat ratusan lembar berlubang.", "alat tulis", 38000, ["kantor", "arsip"]),
produk(58, "Amplop Kertas Kecil", "Amplop kertas merekat untuk surat dan dokumen kecil.", "alat tulis", 3500, ["kantor", "arsip"]),
produk(59, "Kertas HVS A4 70 gram", "Kertas putih bersih untuk printer dan fotokopi.", "alat tulis", 55000, ["kantor", "sekolah"]),
produk(60, "Papan Tulis Magnetik Kecil", "Papan tulis magnetik dengan dua penanda bulat.", "alat tulis", 42000, ["kantor", "sekolah"]),
])
print("produk dimasukkan:", Object.keys(hasil.insertedIds).length)
print("kategori terdaftar:", dbp.katalog.distinct("kategori").join(", "))
}
function buatTextIndex() {
dbp.katalog.createIndex(
{ nama: "text", deskripsi: "text" },
{ default_language: "none" }
)
print("index dibuat:", dbp.katalog.getIndexes()
.map(function (i) { return i.name })
.join(", "))
}
function verifikasiPencarian() {
const jumlah = dbp.katalog.countDocuments({ $text: { $search: "pensil" } })
print("uji cepat $search 'pensil' menemukan:", jumlah, "produk")
print("contoh document (Pensil Kayu HB):")
printjson(dbp.katalog.findOne({ _id: idProduk(35) }, { _id: 0 }))
}
resetDatabase()
isiKatalog()
buatTextIndex()
verifikasiPencarian()Jalankan dari folder proyek:
mongosh "mongodb://127.0.0.1:27017/?directConnection=true" --file src/katalog-seed.jsOutput yang Dihasilkan:
database direset: proyek_pencarian
produk dimasukkan: 60
kategori terdaftar: alat tulis, buku
index dibuat: _id_, nama_text_deskripsi_text
uji cepat $search 'pensil' menemukan: 10 produk
contoh document (Pensil Kayu HB):
{
nama: 'Pensil Kayu HB',
deskripsi: 'Kayu jati dengan inti hitam pekat dan tajam tahan lama.',
kategori: 'alat tulis',
harga: 6000,
tag: [
'sekolah',
'menulis'
]
}Bedah Skrip #
idProduk(n)merangkaiObjectIdtetap dari pola67c1...plus nomor dua digit._idyang menetap membuat seluruh output proyek ini dapat dibandingkan antar eksekusi — pola yang sama dengan proyek blog. Nomor 1–34 adalah buku, 35–60 alat tulis.produk(n, nama, deskripsi, ...)adalah pabrik document: satu baris per produk menjaga 60 entri tetap terbaca sebagai tabel. Harga sengaja memuat nilai kembar (misalnya dua produk Rp6.000) — bahan uji tie-breaker pagination di Bagian 4.isiKatalog()memasukkan 60 produk sekaligus. Kata kunci demo tersebar sadar:pensilmuncul di lima nama dan lima deskripsi,mekanikhanya di satu produk, dan frasapensil warnahanya di dua document — setiap query di bagian berikutnya punya jawaban yang pasti.buatTextIndex()membuat text index tanpa bobot.getIndexes()kemudian mencetak dua nama:_id_bawaan dannama_text_deskripsi_text— nama yang dirangkai otomatis dari field yang di-index.verifikasiPencarian()menutup seed dengan uji cepat:$search "pensil"menemukan 10 produk, membuktikan index aktif dan tokenisasi bekerja pada teks bahasa Indonesia.
Apa Berikutnya #
Katalog dan text index siap. Bagian 2 menggunakannya penuh: kata tunggal, beberapa kata sekaligus, frasa eksak, negasi, skor relevansi, lalu memadukan semuanya dengan filter kategori dan harga dalam satu query.