sort() pada collection besar adalah operasi mahal: MongoDB harus mengumpulkan seluruh hasil, mengurutkannya di memori, dan menolak bekerja melewati batas 100 MB. Kabar baiknya, index sudah terurut sejak lahir — B-tree menyimpan kunci dalam urutan pasti, sehingga pemindaian index bisa menghasilkan data siap-urut tanpa kerja tambahan. Syaratnya satu: arah sort harus cocok dengan arah index. Chapter ini memetakan aturan kecocokan tersebut, terutama pada compound index yang arah field-nya boleh berbeda-beda.
Peta materi Chapter ini:
- Pemindaian maju dan mundur: satu index melayani dua arah sort.
- Aturan arah pada compound index — dicocokkan dan dibuktikan lewat
explain(). - Sort pada prefix index dan kasus yang jatuh ke tahap
SORT.
Pemindaian Maju dan Mundur #
Single field index { harga: 1 } menyimpan nilai menaik, tetapi tidak terkunci pada satu arah — MongoDB bisa memindainya mundur. Di Chapter Single Field Index kita melihat direction: 'backward' ketika menjalankan sort({ harga: -1 }). Dua arah sort dilayani satu index; tidak perlu membuat { harga: -1 } terpisah. Aturan ini penting untuk dipahami sebelum melangkah ke compound index, karena di sana arah tidak lagi bebas.
Arah Compound yang Harus Cocok #
Compound index db.buku.createIndex({ kategori: 1, harga: -1 }) mengurutkan data menurut kategori menaik, lalu harga menurun di dalam tiap kategori. Mari siapkan index tersebut dan uji lima variasi sort sekaligus:
use latihan_index
db.buku.drop()
db.buku.insertMany([
{ judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, kategori: "fiksi" },
{ judul: "Bumi", penulis: "Tere Liye", harga: 89000, kategori: "fiksi" },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, kategori: "nonfiksi" },
{ judul: "Atomic Habits", penulis: "James Clear", harga: 115000, kategori: "nonfiksi" },
{ judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, kategori: "fiksi" },
{ judul: "Bumi Manusia", penulis: "Pramoedya A. Toer", harga: 105000, kategori: "sastra" },
{ judul: "Hujan Bulan Juni", penulis: "Sapardi Djoko Damono", harga: 65000, kategori: "puisi" },
{ judul: "Ensiklopedia Sains Populer", penulis: "Tim Sains", harga: 250000, kategori: "referensi" }
])
db.buku.createIndex({ kategori: 1, harga: -1 })
function cariIXSCAN(tahap) {
while (tahap) {
if (tahap.stage === "IXSCAN") return tahap
tahap = tahap.inputStage
}
return null
}
function ringkasSort(urutan) {
const rencana = db.buku.find({}).sort(urutan).explain()
const puncak = rencana.queryPlanner.winningPlan.stage
const pindai = cariIXSCAN(rencana.queryPlanner.winningPlan)
return pindai ? puncak + " (" + pindai.direction + ")" : puncak
}
printjson({
samaPersis: ringkasSort({ kategori: 1, harga: -1 }),
terbalikSeluruhnya: ringkasSort({ kategori: -1, harga: 1 }),
arahBercampur: ringkasSort({ kategori: 1, harga: 1 }),
prefixSaja: ringkasSort({ kategori: 1 }),
nonPrefix: ringkasSort({ harga: 1 })
})Output yang Dihasilkan:
switched to db latihan_index
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bef0dc4b00afe4311652'),
'1': ObjectId('6ad3bef0dc4b00afe4311653'),
'2': ObjectId('6ad3bef0dc4b00afe4311654'),
'3': ObjectId('6ad3bef0dc4b00afe4311655'),
'4': ObjectId('6ad3bef0dc4b00afe4311656'),
'5': ObjectId('6ad3bef0dc4b00afe4311657'),
'6': ObjectId('6ad3bef0dc4b00afe4311658'),
'7': ObjectId('6ad3bef0dc4b00afe4311659')
}
}
kategori_1_harga_-1
[Function: cariIXSCAN]
[Function: ringkasSort]
{
samaPersis: 'FETCH (forward)',
terbalikSeluruhnya: 'FETCH (backward)',
arahBercampur: 'SORT',
prefixSaja: 'FETCH (forward)',
nonPrefix: 'SORT'
}Tiga dari lima sort terlayani index (tahap teratas FETCH, tanpa SORT), dua sisanya jatuh ke SORT. Hasil lengkapnya:
sort() | Terlayani index? | Alasan |
|---|---|---|
{ kategori: 1, harga: -1 } | Ya — maju (forward) | Arah persis sama dengan index |
{ kategori: -1, harga: 1 } | Ya — mundur (backward) | Kebalikan penuh dari index |
{ kategori: 1, harga: 1 } | Tidak — SORT | Kategori cocok, harga berlawanan arah |
{ kategori: 1 } | Ya | Prefix index; arah tunggal cocok |
{ harga: 1 } | Tidak — SORT | Bukan prefix — aturan Chapter Compound Index |
Aturannya kini terlihat utuh: sort terlayani index bila urutan field-nya membentuk prefix index dan arahnya seluruhnya sama, atau seluruhnya terbalik. Kasus { kategori: 1, harga: 1 } gagal justru karena setengah cocok — B-tree tidak bisa diurutkan “menaik untuk field pertama, menaik juga untuk field kedua” ketika strukturnya menyimpan field kedua menurun.
Melihat Hasil Sort Terbalik Seluruhnya #
Sort { kategori: -1, harga: 1 } menarik untuk diamati karena membuktikan pemindaian mundur bekerja pada data nyata — kategori tampil menurun sementara harga di dalamnya menaik:
db.buku.find({}, { _id: 0, judul: 1, kategori: 1, harga: 1 }).sort({ kategori: -1, harga: 1 })Output yang Dihasilkan:
[
{ judul: 'Bumi Manusia', harga: 105000, kategori: 'sastra' },
{
judul: 'Ensiklopedia Sains Populer',
harga: 250000,
kategori: 'referensi'
},
{ judul: 'Hujan Bulan Juni', harga: 65000, kategori: 'puisi' },
{ judul: 'Filosofi Teras', harga: 89000, kategori: 'nonfiksi' },
{ judul: 'Atomic Habits', harga: 115000, kategori: 'nonfiksi' },
{ judul: 'Rumah Kaca', harga: 72000, kategori: 'fiksi' },
{ judul: 'Bumi', harga: 89000, kategori: 'fiksi' },
{ judul: 'Laut Bercerita', harga: 98000, kategori: 'fiksi' }
]Kategori berjalan dari sastra hingga fiksi, dan di dalam nonfiksi maupun fiksi harga tersusun dari yang termurah. Seluruh urutan ini keluar langsung dari pemindaian mundur index — nol pengurutan di memori.
Rangkuman #
- Index terurut bisa melayani
sort()tanpa pengurutan memori, dengan syarat field sort membentuk prefix index dan arahnya cocok. - Dua kecocokan yang sah: arah seluruhnya sama (pindai maju) atau seluruhnya terbalik (pindai mundur,
direction: 'backward'). - Index
{ kategori: 1, harga: -1 }melayanisort({ kategori: 1, harga: -1 })dansort({ kategori: -1, harga: 1 }), tetapi tidaksort({ kategori: 1, harga: 1 }). - Sort pada prefix index (
{ kategori: 1 }) tetap terlayani; sort yang tidak diawali field terdepan tidak. - Pilih arah index dengan sadar mengikuti pola sort aplikasi yang paling sering berjalan.