Query jarang gagal saat ditulis — galatnya muncul justru saat aplikasi berjalan dan server mencoba mengeksekusi filter yang salah bentuk. Kejanggalannya: MongoDB terkenal fleksibel terhadap bentuk document, tetapi sintaks query tetap ketat; operator yang salah tulis ditolak keras, sementara kesalahan tipe data justru diterima diam-diam dan mengembalikan hasil kosong. Chapter ini menguji lima bentuk galat query yang paling sering muncul — semuanya dengan pesan asli dari MongoDB 9.0 — lalu menutup dengan tabel rujukan gejala-perbaikan.
Peta materi Chapter ini:
- Operator yang tidak dikenal dan field yang berawalan
$. - Pola
$regexyang tidak valid. - Kebalikannya: kesalahan tipe yang tidak menghasilkan galat sama sekali.
- Projection yang mencampur mode inclusion dan exclusion.
Data Latihan #
Kita pakai collection buku kecil sebagai sasaran uji:
use latihan_trouble
db.buku.drop()
db.buku.insertMany([
{ judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, stok: 12 },
{ judul: "Bumi", penulis: "Tere Liye", harga: 89000, stok: 7 },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 105000, stok: 25 }
])Output yang Dihasilkan:
switched to db latihan_trouble
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad401ee65492c702cd6cc9c'),
'1': ObjectId('6ad401ee65492c702cd6cc9d'),
'2': ObjectId('6ad401ee65492c702cd6cc9e')
}
}Dua hal berbeda antar eksekusi dan wajar untuk tutorial: ObjectId dihasilkan otomatis, dan nilai balik drop() bernilai true baik collection ada maupun tidak — mongosh memang mengembalikan true untuk keduanya.
Operator yang Tidak Dikenal #
Satu huruf yang salah pada nama operator sudah cukup untuk menolak seluruh query:
db.buku.find({ $badOperator: 1 }).itcount()Output yang Dihasilkan:
Uncaught
MongoServerError[BadValue]: unknown top level operator: $badOperator. If you have a field name that starts with a '$' symbol, consider using $getField or $setField.Pesan ini perlu dibaca dua bagian. Bagian pertama menyebut operator asing $badOperator — server memperlakukan setiap kunci berawalan $ pada level teratas filter sebagai operator, lalu mencari namanya di daftar operator query. Bagian kedua adalah petunjuk aktif: bila maksud kita memang menanyakan field yang namanya berawalan $, MongoDB menyediakan $getField dan $setField sebagai jalurnya. Kasus nyata paling sering: $gte tertulis $get, atau $in tertulis $ini.
Kasus kedua memperlihatkan petunjuk itu bekerja. Filter berikut bermaksud mencari field bernama $harga:
db.buku.find({ "$harga": 98000 }).itcount()Output yang Dihasilkan:
Uncaught
MongoServerError[BadValue]: unknown top level operator: $harga. If you have a field name that starts with a '$' symbol, consider using $getField or $setField.Meski niatnya field, penulisan "$harga" sebagai kunci filter membuat server mengiranya operator — pesan yang sama persis muncul. Solusi terbaik adalah menghindari nama field berawalan $ sejak desain skema, tetapi bila data seperti itu sudah ada (misalnya hasil migrasi dari sistem lain), jalur resminya adalah $expr dengan $getField. Kita tambahkan satu document yang memang memuat field $harga:
db.buku.insertOne({ judul: "Kamus Dwibahasa", "$harga": 120000 })Output yang Dihasilkan:
{
acknowledged: true,
insertedId: ObjectId('6ad401ee65492c702cd6cc9f')
}Lalu tanyakan field itu secara eksplisit — $literal menjaga nama $harga tidak dianggap path field, dan $$ROOT menunjuk document yang sedang diperiksa:
db.buku.find(
{ $expr: { $eq: [{ $getField: { field: { $literal: "$harga" }, input: "$$ROOT" } }, 120000] } },
{ judul: 1, _id: 0 }
).toArray()Output yang Dihasilkan:
[ { judul: 'Kamus Dwibahasa' } ]Pola Regex yang Tidak Valid #
Operator $regex meneruskan pola ke mesin regular expression di sisi server, sehingga pola yang rusak ditolak saat eksekusi:
db.buku.find({ judul: { $regex: "[" } }).itcount()Output yang Dihasilkan:
Uncaught
MongoServerError[Location51091]: Regular expression is invalid: missing terminating ] for character classPesan galat dari PCRE cukup komunikatif: [ membuka character class yang tidak pernah ditutup. Perbaikannya tentu pada polanya ("\\[" bila maksudnya karakter kurung siku literal), dan karena pola biasa datang dari input pengguna, validasi di sisi aplikasi mencegah pola rusak sampai ke server.
Galat Diam-Diam: Tipe yang Tidak Cocok #
Bentuk paling licik dari query bermasalah tidak menghasilkan galat sama sekali. Bandingkan dua filter hampir identik — satu memakai string, satu memakai angka:
db.buku.find({ harga: { $gt: "50000" } }).itcount()
db.buku.find({ harga: { $gt: 50000 } }).itcount()
typeof db.buku.findOne().hargaOutput yang Dihasilkan:
0
3
numberBaris pertama mengembalikan 0 padahal tiga buku jelas lebih mahal dari 50000. Penyebabnya ada di urutan tipe BSON: perbandingan $gt hanya berlaku antar nilai dengan urutan tipe yang sama, dan string "50000" tidak pernah dibandingkan dengan angka 98000 — keduanya berada di “dunia” pengurutan yang berbeda. Baris kedua dengan angka mengembalikan 3 (seluruh buku), dan typeof menegaskan field harga memang bertipe number di database. Diagnosis untuk gejala “query hasilnya kosong padahal datanya ada” hampir selalu berakhir di sini: periksa tipe nilai di filter versus tipe field di document.
Projection yang Tidak Konsisten #
Projection hanya boleh bekerja dalam satu mode: menyertakan field (inclusion) atau mengecualikan field (exclusion) — tidak boleh keduanya sekaligus:
db.buku.find({}, { judul: 1, harga: 0 }).toArray()Output yang Dihasilkan:
Uncaught
MongoServerError[Location31254]: Cannot do exclusion on field harga in inclusion projectionPesan galatnya menyebut field harga sebagai penyebab: projection dimulai sebagai inclusion (karena judul: 1) lalu menemukan perintah exclusion harga: 0. Satu-satunya pengecualian yang diizinkan adalah _id: 0 — field _id memang selalu ikut secara bawaan sehingga boleh dikeluarkan dari mode mana pun. Bentuk yang benar untuk “judul saja tanpa _id”:
db.buku.find({}, { judul: 1, _id: 0 }).toArray()Output yang Dihasilkan:
[
{ judul: 'Laut Bercerita' },
{ judul: 'Bumi' },
{ judul: 'Filosofi Teras' },
{ judul: 'Kamus Dwibahasa' }
]Tabel Rujukan Cepat #
| Gejala | Penyebab | Perbaikan |
|---|---|---|
unknown top level operator: $x | Nama operator salah tulis | Periksa ejaan operator di dokumentasi; $gte, $in, $ne |
unknown top level operator padahal maksudnya field | Field berawalan $ ditulis sebagai kunci filter | Ganti nama field, atau $expr + $getField |
Regular expression is invalid | Pola $regex rusak | Perbaiki pola; escape karakter khusus; validasi input pengguna |
| Hasil kosong padahal data ada | Nilai filter bertipe berbeda dari field | Samakan tipe (angka vs string); cek dengan typeof |
Cannot do exclusion ... in inclusion projection | Projection campur 1 dan 0 | Pilih satu mode; pengecualian hanya untuk _id |
| Query lambat setelah “diperbaiki” | Filter cocok tipe tapi tak didukung index | Lanjut ke diagnosis Chapter Slow Queries |
Rangkuman #
- Kunci berawalan
$di level teratas filter selalu diperlakukan operator; field berawalan$harus lewat$getField. - Pola
$regexdivalidasi di sisi server — pesan PCRE langsung menunjuk karakter bermasalah. - Kesalahan tipe tidak menghasilkan galat: hasil kosong adalah gejalanya; bandingkan tipe filter dengan tipe field.
- Projection bekerja satu mode;
_idadalah satu-satunya pengecualian yang boleh dicampur.