Dua tahap paling sederhana di seluruh pipeline juga dua tahap paling strategis: $limit memotong aliran setelah N document, $skip membuang N document pertama. Kesederhanaan ini menipu — karena keduanya bekerja pada posisi dalam aliran, bukan pada isi document, penempatannya menentukan apa yang dibatasi dan apa yang dibuang. Chapter ini membedah pola top-N, pola pagination, dan jebakan urutan tahap yang membuat hasil berubah total.

$limit dan Pola Top-N #

Pola paling produktif dari $limit adalah trio $match → $sort → $limit: cari, urutkan, potong. Kita langsung praktik pada data toko buku:

js
use latihan_agregasi
db.dropDatabase()

Output yang Dihasilkan:

text
switched to db latihan_agregasi
{ ok: 1, dropped: 'latihan_agregasi' }
js
db.buku.insertMany([
  { _id: 1, judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, kategori: "Fiksi", tag: ["sastra", "novel"], stok: 12, tahun_terbit: 2017, terjual: [ { bulan: "Januari", jumlah: 14 }, { bulan: "Februari", jumlah: 9 }, { bulan: "Maret", jumlah: 11 } ] },
  { _id: 2, judul: "Bumi", penulis: "Tere Liye", harga: 89000, kategori: "Fiksi", tag: ["novel", "fantasi"], stok: 20, tahun_terbit: 2014, terjual: [ { bulan: "Januari", jumlah: 21 }, { bulan: "Februari", jumlah: 17 }, { bulan: "Maret", jumlah: 25 } ] },
  { _id: 3, judul: "Supernova: Ksatria, Puteri, dan Bintang Jatuh", penulis: "Dee Lestari", harga: 95000, kategori: "Fiksi", tag: ["novel", "sains"], stok: 6, tahun_terbit: 2001, terjual: [ { bulan: "Januari", jumlah: 7 }, { bulan: "Februari", jumlah: 5 } ] },
  { _id: 4, judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, kategori: "Nonfiksi", tag: ["filsafat", "pengembangan diri"], stok: 8, tahun_terbit: 2018, terjual: [ { bulan: "Januari", jumlah: 12 }, { bulan: "Februari", jumlah: 15 }, { bulan: "Maret", jumlah: 10 } ] },
  { _id: 5, judul: "Atomic Habits", penulis: "James Clear", harga: 115000, kategori: "Nonfiksi", tag: ["pengembangan diri", "kebiasaan"], stok: 15, tahun_terbit: 2018, terjual: [ { bulan: "Januari", jumlah: 18 }, { bulan: "Februari", jumlah: 22 }, { bulan: "Maret", jumlah: 16 } ] },
  { _id: 6, judul: "Kamus Bahasa Indonesia", penulis: "Badan Bahasa", harga: 55000, kategori: "Referensi", tag: ["referensi", "bahasa"], stok: 30, tahun_terbit: 2016, terjual: [] }
])

Output yang Dihasilkan:

text
{
  acknowledged: true,
  insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6 }
}
js
db.buku.aggregate([
  { $match: { kategori: { $ne: "Referensi" } } },
  { $sort: { harga: -1 } },
  { $limit: 3 },
  { $project: { _id: 0, judul: 1, harga: 1 } }
])

Output yang Dihasilkan:

text
[
  { judul: 'Atomic Habits', harga: 115000 },
  { judul: 'Laut Bercerita', harga: 98000 },
  {
    judul: 'Supernova: Ksatria, Puteri, dan Bintang Jatuh',
    harga: 95000
  }
]

Tiga buku termahal di luar kategori Referensi — bentuk “top 3” yang akrab di setiap laporan. Ada bonus performa yang tak terlihat di output: ketika $sort langsung diikuti $limit, MongoDB menggabungkan keduanya sehingga pengurutan hanya memelihara tiga document teratas di memori, bukan seluruh hasil $match. Pada collection besar, penggabungan ini mengubah pola top-N dari mahal menjadi murah.

$skip dan Pagination #

$skip: N membuang N document pertama dan meneruskan sisanya. Dipasangkan dengan $limit, inilah mesin pagination — halaman k berukuran s dirumuskan skip (k-1) × s lalu limit s:

js
db.buku.aggregate([
  { $sort: { harga: -1 } },
  { $skip: 3 },
  { $limit: 3 },
  { $project: { _id: 0, judul: 1, harga: 1 } }
])

Output yang Dihasilkan:

text
[
  { judul: 'Filosofi Teras', harga: 89000 },
  { judul: 'Bumi', harga: 89000 },
  { judul: 'Kamus Bahasa Indonesia', harga: 55000 }
]

Dengan ukuran halaman 3, pipeline ini mengambil halaman kedua: document peringkat ke-4 sampai ke-6 dari daftar harga menurun. Urutan penulisannya punya makna — $skip sebelum $limit berarti “lompati 3, ambil 3 berikutnya”. Menukar keduanya menghasilkan halaman yang berbeda: $limit: 3 lebih dulu memotong tiga teratas, lalu $skip: 3 membuang semuanya dan hasilnya kosong.

Satu syarat penting pagination: $skip memotong posisi, dan posisi hanya stabil bila urutannya pasti. Selalu awali dengan $sort yang kuncinya unik (atau tambahkan _id sebagai pemutus seri) — tanpa itu, isi “halaman” bisa berubah antar eksekusi.

Posisi Tahap Mengubah Makna #

Karena $limit dan $skip bekerja pada aliran document, hasilnya bergantung pada tahap apa yang sudah dilewati sebelumnya. Perhatikan dua pipeline ini — keduanya berisi tahap yang sama, hanya urutannya yang berbeda:

js
db.buku.aggregate([
  { $limit: 2 },
  { $unwind: "$tag" },
  { $project: { _id: 0, judul: 1, tag: 1 } }
])
db.buku.aggregate([
  { $unwind: "$tag" },
  { $limit: 2 },
  { $project: { _id: 0, judul: 1, tag: 1 } }
])

Output yang Dihasilkan:

text
[
  { judul: 'Laut Bercerita', tag: 'sastra' },
  { judul: 'Laut Bercerita', tag: 'novel' },
  { judul: 'Bumi', tag: 'novel' },
  { judul: 'Bumi', tag: 'fantasi' }
]
[
  { judul: 'Laut Bercerita', tag: 'sastra' },
  { judul: 'Laut Bercerita', tag: 'novel' }
]

Bedah perbedaannya:

  1. Pipeline pertama ($limit sebelum $unwind): dua buku pertama dipotong lebih dulu, baru $unwind memecah masing-masing menjadi document per tag — hasilnya semua tag dari dua buku itu (4 document).
  2. Pipeline kedua ($unwind sebelum $limit): seluruh buku dipecah menjadi aliran tag lebih dulu, lalu potongan diambil — hasilnya dua tag pertama dari aliran tersebut (2 document).

Pertanyaan “lima penulis teratas menurut jumlah buku” dan “lima document pertama lalu dihitung penulisnya” membutuhkan urutan yang berbeda — dan begitulah cara membaca pipeline: tentukan dulu objek yang dihitung, lalu tempatkan pembatas di sisi yang benar. Kita akan bertemu lagi dengan pola ini di Chapter $unwind, tempat pecahan array menjadi document memperbanyak aliran.

Rangkuman #

  • $limit: N memotong aliran setelah N document; $skip: N membuang N document pertama.
  • Pola top-N $match → $sort → $limit mendapat optimasi khusus: pengurutan hanya memelihara N teratas di memori.
  • Pagination: $sort (kunci unik) → $skip (k-1)×s → $limit s; $skip yang besar tetap memproses document yang dibuang.
  • Urutan terhadap tahap lain menentukan makna: $limit sebelum $unwind membatasi document asli, sesudahnya membatasi hasil pecahan.