Bagian 1 menetapkan dua rumah untuk komentar: collection comments menyimpan riwayat lengkap, field komentar_terbaru pada artikel menyimpan sepuluh yang terakhir. Bagian ini menerjemahkan rancangan tersebut menjadi fungsi yang menulis ke kedua rumah setiap kali pembaca berkomentar, lalu membuktikan lewat $lookup bahwa keduanya menghasilkan angka yang konsisten. Di sepanjang demo kita akan menyaksikan subset pattern bekerja: komentar ke-11 masuk, komentar paling lama terdorong keluar dari subset, sementara collection penuh tetap utuh.
Menyimpan di Dua Tempat Sekaligus #
| Penyimpanan | Isi | Dibaca oleh |
|---|---|---|
Collection comments | Seluruh komentar, tanpa batas | Moderasi, audit, $lookup |
Field komentar_terbaru | Maksimal 10 komentar terbaru | Halaman artikel |
Setiap komentar tersimpan dua kali: satu document di collection comments sebagai sumber kebenaran, dan satu salinan di depan array komentar_terbaru pada artikel. Duplikasi ini adalah harga yang dibayar subset pattern — sebagai gantinya, halaman artikel tampil dengan satu query tanpa sort, dan document artikel tidak pernah membengkak melebihi sepuluh komentar.
Empat Fungsi dan Skenario Demo #
| Fungsi | Peran |
|---|---|
resetKomentar() | Kosongkan collection comments dan hapus field subset |
tambahKomentar() | Insert ke comments lalu sematkan salinan di subset artikel |
statusKomentar() | Bandingkan jumlah di collection penuh versus subset |
semuaKomentar() | Gabungkan artikel dan comments lewat $lookup |
Demo mengalirkan dua belas komentar pembaca ke artikel Pengenalan Document Database, mencetak statusnya, lalu menambahkan komentar ke-13 dan mencetak statusnya lagi. Dua snapshot inilah bukti bahwa subset berhenti di sepuluh sedangkan collection penuh terus bertambah.
Skrip Comments #
// blog-comments.js — comments dengan subset pattern dan $lookup
// Jalankan setelah blog-artikel.js:
// mongosh "mongodb://127.0.0.1:27017/?directConnection=true" --file src/blog-comments.js
var blog = db.getSiblingDB("blog")
function resetKomentar() {
blog.comments.drop()
blog.artikel.updateMany({}, { $unset: { komentar_terbaru: "" } })
print("kondisi komentar direset")
}
function tambahKomentar(slugArtikel, nama, email, isi, tanggal) {
const artikel = blog.artikel.findOne({ slug: slugArtikel }, { _id: 1 })
if (!artikel) {
print("artikel tidak ditemukan:", slugArtikel)
return
}
const komentar = {
artikel_id: artikel._id, nama: nama, email: email, isi: isi, dibuat: tanggal,
}
blog.comments.insertOne(komentar)
blog.artikel.updateOne(
{ _id: artikel._id },
{ $push: {
komentar_terbaru: { $each: [komentar], $position: 0, $slice: 10 },
} }
)
}
function statusKomentar(slugArtikel) {
const artikel = blog.artikel.findOne({ slug: slugArtikel })
print("artikel:", artikel.judul)
print(" komentar tersimpan (comments) :", blog.comments.countDocuments({ artikel_id: artikel._id }))
print(" komentar disematkan (subset) :", artikel.komentar_terbaru.length)
print(" terbaru : \"" + artikel.komentar_terbaru[0].isi +
"\" oleh", artikel.komentar_terbaru[0].nama)
}
function semuaKomentar(slugArtikel) {
print("== gabung artikel dan comments ($lookup) ==")
blog.artikel.aggregate([
{ $match: { slug: slugArtikel } },
{ $lookup: {
from: "comments",
localField: "_id",
foreignField: "artikel_id",
pipeline: [{ $sort: { dibuat: -1 } }],
as: "semua_komentar",
} },
{ $project: {
_id: 0, judul: 1,
jumlah: { $size: "$semua_komentar" },
terbaru: { $arrayElemAt: ["$semua_komentar.isi", 0] },
terlama: { $arrayElemAt: ["$semua_komentar.isi", -1] },
} },
]).forEach(function (r) {
print(" ", r.judul, "| total", r.jumlah)
print(" terbaru:", r.terbaru)
print(" terlama:", r.terlama)
})
}
print("== demo comments ==")
resetKomentar()
const pembaca = [
["Budi Santoso", "[email protected]", "Materinya jelas, saya baru paham bedanya document dan baris.", ISODate("2026-01-13T10:00:00Z")],
["Andi Wijaya", "[email protected]", "Contoh BSON-nya membantu sekali.", ISODate("2026-01-15T10:00:00Z")],
["Rina Marlina", "[email protected]", "Ada rekomendasi buku lanjutan?", ISODate("2026-01-18T10:00:00Z")],
["Joko Susilo", "[email protected]", "Bagian index masih membingungkan buat saya.", ISODate("2026-01-22T10:00:00Z")],
["Dewi Lestari", "[email protected]", "Baru tahu kalau document bisa punya array.", ISODate("2026-01-25T10:00:00Z")],
["Putri Ayu", "[email protected]", "Penjelasannya runtut, terima kasih.", ISODate("2026-01-28T10:00:00Z")],
["Agus Salim", "[email protected]", "Saya pakai ini untuk tugas kampus.", ISODate("2026-02-02T10:00:00Z")],
["Maya Sari", "[email protected]", "Tolong bahas sharding juga ya.", ISODate("2026-02-06T10:00:00Z")],
["Fajar Nugroho", "[email protected]", "Ringkasan di akhir sangat membantu.", ISODate("2026-02-10T10:00:00Z")],
["Lina Kartika", "[email protected]", "Saya simpan halaman ini untuk dibaca ulang.", ISODate("2026-02-14T10:00:00Z")],
["Tono Hartono", "[email protected]", "Konsep schemaless ternyata begini.", ISODate("2026-02-18T10:00:00Z")],
["Siti Rahayu", "[email protected]", "Terima kasih atas masukannya, artikel lanjutan sedang disiapkan.", ISODate("2026-02-20T10:00:00Z")],
]
pembaca.forEach(function (p) {
tambahKomentar("pengenalan-document-database", p[0], p[1], p[2], p[3])
})
statusKomentar("pengenalan-document-database")
tambahKomentar(
"pengenalan-document-database", "Rina Marlina", "[email protected]",
"Kalau versi videonya ada?", ISODate("2026-03-01T10:00:00Z")
)
statusKomentar("pengenalan-document-database")
semuaKomentar("pengenalan-document-database")Jalankan setelah blog-artikel.js:
mongosh "mongodb://127.0.0.1:27017/?directConnection=true" --file src/blog-comments.jsOutput yang Dihasilkan:
== demo comments ==
kondisi komentar direset
artikel: Pengenalan Document Database
komentar tersimpan (comments) : 12
komentar disematkan (subset) : 10
terbaru : "Terima kasih atas masukannya, artikel lanjutan sedang disiapkan." oleh Siti Rahayu
artikel: Pengenalan Document Database
komentar tersimpan (comments) : 13
komentar disematkan (subset) : 10
terbaru : "Kalau versi videonya ada?" oleh Rina Marlina
== gabung artikel dan comments ($lookup) ==
Pengenalan Document Database | total 13
terbaru: Kalau versi videonya ada?
terlama: Materinya jelas, saya baru paham bedanya document dan baris.Bedah Fungsi demi Fungsi #
resetKomentar()mengembalikan kondisi Bagian 1:drop()mengosongkan collectioncomments, lalu$unsetmenghapus fieldkomentar_terbarudari semua artikel. Skrip menjadi aman dijalankan berulang — setiap eksekusi memulai dari keadaan bersih.tambahKomentar()bekerja dua langkah. Pertama,insertOnemenyimpan komentar lengkap ke collectioncommentsdenganartikel_idsebagai reference ke artikel. Kedua,updateOnepada artikel menyalin komentar yang sama ke depan array lewat$pushyang membawa$each,$position: 0, dan$slice: 10:$position: 0menyisipkan di elemen pertama sehingga komentar terbaru selalu berada paling depan, dan$slice: 10memangkas array kembali ke sepuluh — komentar paling lama terdorong keluar begitu batas terlampaui.statusKomentar()membuktikan kedua rumah berisi jumlah berbeda. Setelah dua belas komentar:commentsmenyimpan 12, subset 10. Setelah komentar ke-13 masuk:commentsmenjadi 13, subset tetap 10, dan isi terbarunya berganti menjadi komentar Rina — persis perilaku yang dijanjikan rancangan Bagian 1.semuaKomentar()menggabungkan kedua sisi lewat$lookupbentuk pipeline:localField: "_id"dipasangkan denganforeignField: "artikel_id", sementarapipelineberisi$sort: { dibuat: -1 }mengurutkan hasil penggabungan di sisi server. Stage$projectlalu menghitungjumlahdengan$size, mengambil komentar terbaru dengan$arrayElemAtindeks0, dan terlama dengan indeks-1— dot-path"$semua_komentar.isi"memproyeksikan array komentar menjadi array teks, sehingga kedua ujungnya bisa diambil tanpa loop.
Jelajah Lanjut #
Collection comments kini menyimpan 13 document — cukup untuk pertanyaan moderasi pertama: siapa pembaca yang paling sering berkomentar?
use blog
db.comments.aggregate([
{ $group: { _id: "$nama", jumlah: { $sum: 1 } } },
{ $match: { jumlah: { $gt: 1 } } }
])Output yang Dihasilkan:
switched to db blog
[ { _id: 'Rina Marlina', jumlah: 2 } ]Rina memang berkomentar dua kali: menanyakan rekomendasi buku pada 18 Januari, lalu versi video pada 1 Maret. Perhatikan dari mana jawaban itu datang — query menyentuh collection comments saja, dan pertanyaan semacam ini tidak mungkin dijawab dari subset di artikel karena komentar pertama Rina sudah terdorong keluar dari sepuluh yang tersimpan. Saat memilih pola penyimpanan, daftar pertanyaan masa depan sama pentingnya dengan query hari ini.
Apa Berikutnya #
Komentar kini mengalir dan halaman artikel tetap ringan berkat subset sepuluh. Tersisa satu keluhan klasik blog yang belum terjawab: daftar artikel yang terus bertambah. Bagian 4 menutup proyek dengan pagination keyset yang stabil, index compound yang membuatnya cepat, dan suite tes yang memverifikasi seluruh hasil kerja kita.