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 #

PenyimpananIsiDibaca oleh
Collection commentsSeluruh komentar, tanpa batasModerasi, audit, $lookup
Field komentar_terbaruMaksimal 10 komentar terbaruHalaman 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 #

FungsiPeran
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 #

js
// 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:

bash
mongosh "mongodb://127.0.0.1:27017/?directConnection=true" --file src/blog-comments.js

Output yang Dihasilkan:

text
== 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 #

  1. resetKomentar() mengembalikan kondisi Bagian 1: drop() mengosongkan collection comments, lalu $unset menghapus field komentar_terbaru dari semua artikel. Skrip menjadi aman dijalankan berulang — setiap eksekusi memulai dari keadaan bersih.
  2. tambahKomentar() bekerja dua langkah. Pertama, insertOne menyimpan komentar lengkap ke collection comments dengan artikel_id sebagai reference ke artikel. Kedua, updateOne pada artikel menyalin komentar yang sama ke depan array lewat $push yang membawa $each, $position: 0, dan $slice: 10: $position: 0 menyisipkan di elemen pertama sehingga komentar terbaru selalu berada paling depan, dan $slice: 10 memangkas array kembali ke sepuluh — komentar paling lama terdorong keluar begitu batas terlampaui.
  3. statusKomentar() membuktikan kedua rumah berisi jumlah berbeda. Setelah dua belas komentar: comments menyimpan 12, subset 10. Setelah komentar ke-13 masuk: comments menjadi 13, subset tetap 10, dan isi terbarunya berganti menjadi komentar Rina — persis perilaku yang dijanjikan rancangan Bagian 1.
  4. semuaKomentar() menggabungkan kedua sisi lewat $lookup bentuk pipeline: localField: "_id" dipasangkan dengan foreignField: "artikel_id", sementara pipeline berisi $sort: { dibuat: -1 } mengurutkan hasil penggabungan di sisi server. Stage $project lalu menghitung jumlah dengan $size, mengambil komentar terbaru dengan $arrayElemAt indeks 0, 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?

js
use blog
db.comments.aggregate([
  { $group: { _id: "$nama", jumlah: { $sum: 1 } } },
  { $match: { jumlah: { $gt: 1 } } }
])

Output yang Dihasilkan:

text
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.