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:

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

text
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 — SORTKategori cocok, harga berlawanan arah
{ kategori: 1 }YaPrefix index; arah tunggal cocok
{ harga: 1 }Tidak — SORTBukan 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:

js
db.buku.find({}, { _id: 0, judul: 1, kategori: 1, harga: 1 }).sort({ kategori: -1, harga: 1 })

Output yang Dihasilkan:

text
[
  { 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 } melayani sort({ kategori: 1, harga: -1 }) dan sort({ kategori: -1, harga: 1 }), tetapi tidak sort({ 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.