“Semua buku di bawah Rp75.000”, “harga antara Rp89.000 sampai Rp98.000”, “karya Tere Liye atau James Clear” — pertanyaan seperti ini tidak lagi bisa dijawab dengan kesamaan nilai saja. Kita butuh perbandingan: lebih dari, kurang dari, dan keanggotaan dalam daftar. MongoDB menyediakannya sebagai comparison operators yang ditulis di dalam filter, dan delapan di antaranya dipakai hampir setiap hari. Chapter ini menguji semuanya satu per satu — termasuk dua perilaku mengejutkan dari $ne dan $nin yang sering menjebak pemula.
Peta materi Chapter ini:
- Delapan comparison operators:
$eq,$ne,$gt,$gte,$lt,$lte,$in,$nin. - Rentang nilai dengan menggabungkan dua operator pada satu field.
- Jebakan
$nedan$ninterhadap document tanpa field.
Delapan Operator dan Bentuknya #
Semua comparison operators ditulis sebagai nilai dari field yang disyaratkan: { field: { operator: nilai } }. Ringkasannya:
| Operator | Arti | Contoh |
|---|---|---|
$eq | sama dengan | { harga: { $eq: 89000 } } |
$ne | tidak sama dengan | { kategori: { $ne: "fiksi" } } |
$gt | lebih dari | { harga: { $gt: 90000 } } |
$gte | lebih dari atau sama dengan | { harga: { $gte: 89000 } } |
$lt | kurang dari | { harga: { $lt: 75000 } } |
$lte | kurang dari atau sama dengan | { harga: { $lte: 72000 } } |
$in | salah satu dari daftar | { penulis: { $in: ["Tere Liye", "James Clear"] } } |
$nin | bukan salah satu dari daftar | { penulis: { $nin: ["Tere Liye", "James Clear"] } } |
Siapkan data uji — sama dengan Chapter sebelumnya, dengan satu pengecualian yang disengaja: Kamus Bahasa Indonesia tidak memiliki field kategori maupun rating, dan perannya akan terlihat sebentar lagi:
use latihan_crud
db.buku.drop()Output yang Dihasilkan:
switched to db latihan_crud
truedb.buku.insertMany([
{ judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, stok: 12, kategori: "fiksi", tag: ["fiksi", "sastra"], rating: 4.7 },
{ judul: "Bumi", penulis: "Tere Liye", harga: 89000, stok: 25, kategori: "fiksi", tag: ["fiksi", "remaja"], rating: 4.5 },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, stok: 18, kategori: "nonfiksi", tag: ["filsafat", "pengembangan diri"], rating: 4.8 },
{ judul: "Atomic Habits", penulis: "James Clear", harga: 115000, stok: 30, kategori: "nonfiksi", tag: ["pengembangan diri", "terjemahan"], rating: 4.6 },
{ judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, stok: 9, kategori: "fiksi", tag: ["fiksi", "remaja"], rating: 4.3 },
{ judul: "Kamus Bahasa Indonesia", penulis: "Badan Bahasa", harga: 55000, stok: 4, tag: ["referensi", "bahasa"] }
])Output yang Dihasilkan:
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad387d6a2e4ea6232f1a7f0'),
'1': ObjectId('6ad387d6a2e4ea6232f1a7f1'),
'2': ObjectId('6ad387d6a2e4ea6232f1a7f2'),
'3': ObjectId('6ad387d6a2e4ea6232f1a7f3'),
'4': ObjectId('6ad387d6a2e4ea6232f1a7f4'),
'5': ObjectId('6ad387d6a2e4ea6232f1a7f5')
}
}$eq: Kesamaan yang Eksplisit #
Operator $eq adalah bentuk panjang dari kesamaan biasa. Dua filter ini maknanya identik — “kategori harus fiksi”:
db.buku.find({ kategori: { $eq: "fiksi" } }, { judul: 1 })Output yang Dihasilkan:
[
{ _id: ObjectId('6ad387d6a2e4ea6232f1a7f0'), judul: 'Laut Bercerita' },
{ _id: ObjectId('6ad387d6a2e4ea6232f1a7f1'), judul: 'Bumi' },
{ _id: ObjectId('6ad387d6a2e4ea6232f1a7f4'), judul: 'Rumah Kaca' }
]Hasilnya sama persis dengan { kategori: "fiksi" } dari Chapter Query Filters. Kapan $eq berguna? Terutama saat nilai perlu dibangun secara dinamis di kode aplikasi, atau saat operator lain di slot yang sama ingin ditukar hanya dengan mengganti nama operator. Untuk penulisan sehari-hari, bentuk pendek tetap lebih lazim.
Rentang Nilai: $gt, $gte, $lt, $lte #
Empat operator ini menguji urutan nilai. Lebih dari Rp90.000:
db.buku.find({ harga: { $gt: 90000 } }, { judul: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f0'),
judul: 'Laut Bercerita',
harga: 98000
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f3'),
judul: 'Atomic Habits',
harga: 115000
}
]Bumi dan Filosofi Teras yang tepat 89000 tidak ikut karena $gt menuntut lebih dari; ganti $gte maka keduanya masuk. Dua operator juga boleh digabung dalam satu object untuk membentuk rentang — harga antara 89000 dan 98000:
db.buku.find({ harga: { $gte: 89000, $lte: 98000 } }, { judul: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f0'),
judul: 'Laut Bercerita',
harga: 98000
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f1'),
judul: 'Bumi',
harga: 89000
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f2'),
judul: 'Filosofi Teras',
harga: 89000
}
]Tiga buku berada di dalam rentang, batas atas dan bawah ikut termasuk. Untuk syarat “di bawah”, $lt memotong tanpa menyertakan batas:
db.buku.find({ harga: { $lt: 75000 } }, { judul: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f4'),
judul: 'Rumah Kaca',
harga: 72000
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f5'),
judul: 'Kamus Bahasa Indonesia',
harga: 55000
}
]Perbandingan tidak berlaku lintas tipe: { harga: { $gt: "90000" } } memakai string, sementara seluruh harga di katalog adalah angka, sehingga query berikut menemukan nol document:
db.buku.find({ harga: { $gt: "90000" } }).count()Output yang Dihasilkan:
0Perilaku ini disebut type bracketing: perbandingan hanya berlangsung antar nilai dari keluarga tipe yang sama (angka dengan angka, string dengan string). String dibandingkan urutan kamus, dan tanggal pakai ISODate — keduanya penting saat data kita bukan angka.
Daftar Nilai: $in dan $nin #
$in menjawab “salah satu dari” tanpa perlu menyusun $or panjang. Semua buku dari dua penulis sekaligus:
db.buku.find({ penulis: { $in: ["Tere Liye", "James Clear"] } }, { judul: 1, penulis: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f1'),
judul: 'Bumi',
penulis: 'Tere Liye'
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f3'),
judul: 'Atomic Habits',
penulis: 'James Clear'
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f4'),
judul: 'Rumah Kaca',
penulis: 'Tere Liye'
}
]Kebalikannya, $nin mengambil semua yang tidak tercantum:
db.buku.find({ penulis: { $nin: ["Tere Liye", "James Clear"] } }, { judul: 1, penulis: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f0'),
judul: 'Laut Bercerita',
penulis: 'Leila S. Chudori'
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f2'),
judul: 'Filosofi Teras',
penulis: 'Henry Manampiring'
},
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f5'),
judul: 'Kamus Bahasa Indonesia',
penulis: 'Badan Bahasa'
}
]$ne dan $nin Menangkap Document tanpa Field #
Sekarang jebakan yang dijanjikan di awal Chapter. Kita cari “semua buku yang kategorinya bukan fiksi”:
db.buku.find({ kategori: { $ne: "fiksi" } }, { judul: 1 })Output yang Dihasilkan:
[
{ _id: ObjectId('6ad387d6a2e4ea6232f1a7f2'), judul: 'Filosofi Teras' },
{ _id: ObjectId('6ad387d6a2e4ea6232f1a7f3'), judul: 'Atomic Habits' },
{
_id: ObjectId('6ad387d6a2e4ea6232f1a7f5'),
judul: 'Kamus Bahasa Indonesia'
}
]Filosofi Teras dan Atomic Habits masuk akal — keduanya nonfiksi. Tetapi Kamus Bahasa Indonesia juga ikut, padahal buku tersebut tidak punya field kategori sama sekali. Inilah perilaku $ne dan $nin: keduanya membaca “tidak sama dengan nilai X” sebagai “bernilai apa pun selain X, atau field-nya tidak ada”. Document tanpa field tersebut selalu lolos.
Dari sisi performa juga ada konsekuensi: kesamaan ($eq, $in) memanfaatkan index dengan baik, sedangkan $ne, $nin, dan operator negasi lain memaksa server memeriksa jauh lebih banyak document karena hasilnya tersebar di mana-mana. Detailnya kita bahas di Section Indexing; untuk sekarang, jadikan catatan desain — filter negasi itu mahal.
Sebagai penutup, bersihkan database latihan:
db.dropDatabase()Output yang Dihasilkan:
{ ok: 1, dropped: 'latihan_crud' }Rangkuman #
- Comparison operators ditulis
{ field: { operator: nilai } };$eqadalah bentuk panjang kesamaan biasa. $gt/$gte/$lt/$ltemenguji urutan; dua operator boleh digabung untuk rentang seperti{ $gte: 89000, $lte: 98000 }.- Perbandingan berlaku antar tipe yang sama — string tidak dibandingkan dengan angka (type bracketing).
$indan$ninmenguji keanggotaan daftar nilai, pengganti ringkas untuk$orberulang.$nedan$ninjuga menangkap document yang tidak memiliki field tersebut; tambahkan$existsbila itu bukan maksud kita.
Perbandingan sudah lengkap. Chapter berikutnya menggabungkan semua syarat menjadi logika majemuk — $and, $or, $nor, dan $not.