Syarat nyata jarang berdiri sendiri. “Buku nonfiksi atau harga di atas seratus ribu”, “murah tapi stoknya masih banyak”, “bukan karya penulis ini” — kalimat-kalimat tersebut menggabungkan beberapa kondisi menjadi satu keputusan. MongoDB menyediakan empat logical operators untuk menyusun gabungan itu: $or, $nor, $and, dan $not. Chapter ini menguji keempatnya dan menyoroti satu kasus ketika $and bukan pilihan gaya melainkan keharusan — akibat aturan object JavaScript itu sendiri.
Peta materi Chapter ini:
$ordan$noruntuk alternatif syarat.- AND implisit versus
$andeksplisit, termasuk kapan$andwajib. $notuntuk meniadakan satu operator.
$or: Cukup Satu Syarat Benar #
$or menerima array berisi beberapa filter; document cocok bila minimal satu filter di dalamnya terpenuhi. Siapkan katalog yang sama dengan Chapter sebelumnya — ingat bahwa Kamus Bahasa Indonesia sengaja tanpa field kategori dan rating:
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('6ad38852820b6760a4ccb30f'),
'1': ObjectId('6ad38852820b6760a4ccb310'),
'2': ObjectId('6ad38852820b6760a4ccb311'),
'3': ObjectId('6ad38852820b6760a4ccb312'),
'4': ObjectId('6ad38852820b6760a4ccb313'),
'5': ObjectId('6ad38852820b6760a4ccb314')
}
}Query pertama: buku nonfiksi, atau buku apa pun yang harganya di bawah 75000:
db.buku.find({
$or: [
{ kategori: "nonfiksi" },
{ harga: { $lt: 75000 } }
]
}, { judul: 1, kategori: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad38852820b6760a4ccb311'),
judul: 'Filosofi Teras',
harga: 89000,
kategori: 'nonfiksi'
},
{
_id: ObjectId('6ad38852820b6760a4ccb312'),
judul: 'Atomic Habits',
harga: 115000,
kategori: 'nonfiksi'
},
{
_id: ObjectId('6ad38852820b6760a4ccb313'),
judul: 'Rumah Kaca',
harga: 72000,
kategori: 'fiksi'
},
{
_id: ObjectId('6ad38852820b6760a4ccb314'),
judul: 'Kamus Bahasa Indonesia',
harga: 55000
}
]Empat document lolos lewat jalur yang berbeda-beda: Filosofi Teras dan Atomic Habits lewat kategori, Rumah Kaca dan Kamus Bahasa Indonesia lewat harga. Kamus Bahasa Indonesia sekali lagi lolos tanpa punya field kategori — tapi kali ini karena syarat keduanya, harga, yang terpenuhi.
$nor: Semua Syarat Harus Salah #
$nor adalah kebalikan persis $or: document cocok bila semua syarat di dalamnya salah. Query yang sama persis, operator diganti:
db.buku.find({
$nor: [
{ kategori: "nonfiksi" },
{ harga: { $lt: 75000 } }
]
}, { judul: 1, kategori: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad38852820b6760a4ccb30f'),
judul: 'Laut Bercerita',
harga: 98000,
kategori: 'fiksi'
},
{
_id: ObjectId('6ad38852820b6760a4ccb310'),
judul: 'Bumi',
harga: 89000,
kategori: 'fiksi'
}
]Hanya dua buku fiksi yang harganya 75000 ke atas yang selamat: Laut Bercerita dan Bumi. $nor jarang jadi pilihan pertama — pembacaannya berat karena dua kali peniadaan — tetapi sulit digantikan saat pengecualian memang didefinisikan sebagai “bukan ini dan bukan itu”.
$and: Kapan Harus Eksplisit #
Filter dengan beberapa field — seperti { kategori: "nonfiksi", harga: 89000 } di Chapter Query Filters — sudah menghasilkan AND secara implisit. Bentuk eksplisit $and menerima array filter sebagaimana $or:
db.buku.find({
$and: [
{ harga: { $ne: 89000 } },
{ harga: { $lt: 100000 } }
]
}, { judul: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad38852820b6760a4ccb30f'),
judul: 'Laut Bercerita',
harga: 98000
},
{
_id: ObjectId('6ad38852820b6760a4ccb313'),
judul: 'Rumah Kaca',
harga: 72000
},
{
_id: ObjectId('6ad38852820b6760a4ccb314'),
judul: 'Kamus Bahasa Indonesia',
harga: 55000
}
]Contoh ini menarik karena kedua syaratnya menyasar field yang sama: harga. Rentang pada satu field memang bisa ditulis padat seperti { $gte: 89000, $lte: 98000 }, tetapi gabungan seperti “bukan 89000 dan di bawah 100000” mengharuskan field harga muncul dua kali — dan di sinilah batas AND implisit: satu object JavaScript hanya boleh memiliki satu kunci harga.
Batas itu menjadi masalah sesungguhnya ketika operator yang ingin diulang adalah $or itu sendiri. Misalnya kita mencari buku yang (nonfiksi atau di atas 100000) dan (stok di atas 20 atau rating minimal 4.8). Tanpa $and, kita tergoda menulis dua kunci $or dalam satu object:
db.buku.find({
$or: [ { kategori: "nonfiksi" }, { harga: { $gt: 100000 } } ],
$or: [ { stok: { $gt: 20 } }, { rating: { $gte: 4.8 } } ]
}, { judul: 1, kategori: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad38852820b6760a4ccb310'),
judul: 'Bumi',
harga: 89000,
kategori: 'fiksi'
},
{
_id: ObjectId('6ad38852820b6760a4ccb311'),
judul: 'Filosofi Teras',
harga: 89000,
kategori: 'nonfiksi'
},
{
_id: ObjectId('6ad38852820b6760a4ccb312'),
judul: 'Atomic Habits',
harga: 115000,
kategori: 'nonfiksi'
}
]Query berjalan tanpa galat — dan justru itulah masalahnya. Lihat baris pertama hasil: Bumi ikut masuk padahal field kategori-nya sendiri tercetak fiksi, dan harganya 89000 — bukti bahwa kelompok syarat pertama seolah-olah tidak pernah ada. Penyebabnya: kunci $or kedua menimpa yang pertama secara diam-diam, sehingga yang dieksekusi server hanya kelompok stok dan rating. Bentuk yang benar membungkus kedua kelompok dengan $and:
db.buku.find({
$and: [
{ $or: [ { kategori: "nonfiksi" }, { harga: { $gt: 100000 } } ] },
{ $or: [ { stok: { $gt: 20 } }, { rating: { $gte: 4.8 } } ] }
]
}, { judul: 1 })Output yang Dihasilkan:
[
{ _id: ObjectId('6ad38852820b6760a4ccb311'), judul: 'Filosofi Teras' },
{ _id: ObjectId('6ad38852820b6760a4ccb312'), judul: 'Atomic Habits' }
]Hasil yang tersisa hanya Filosofi Teras dan Atomic Habits — keduanya memang lolos kelompok pertama (nonfiksi) sekaligus kelompok kedua.
$not: Meniadakan Satu Operator #
$not bekerja di level field: operator ini membungkus satu operator document lalu membalik hasilnya. Buku yang harganya tidak lebih dari 80000:
db.buku.find({ harga: { $not: { $gt: 80000 } } }, { judul: 1, harga: 1 })Output yang Dihasilkan:
[
{
_id: ObjectId('6ad38852820b6760a4ccb313'),
judul: 'Rumah Kaca',
harga: 72000
},
{
_id: ObjectId('6ad38852820b6760a4ccb314'),
judul: 'Kamus Bahasa Indonesia',
harga: 55000
}
]Secara hasil, { $not: { $gt: 80000 } } setara dengan { $lte: 80000 } — tetapi $not berguna saat operator yang perlu dibalik bukan perbandingan sederhana, misalnya $regex atau $mod. Dua aturan syntaksnya ketat. Pertama, argumen $not harus operator document atau regex; nilai polos ditolak:
db.buku.find({ harga: { $not: 89000 } })Output yang Dihasilkan:
Uncaught
MongoServerError[BadValue]: $not argument must be a regex or an objectKedua, $not mewarisi perilaku $ne terhadap field yang tidak ada: document tanpa field harga akan ikut lolos { harga: { $not: { $gt: 80000 } } }. Di katalog kita semua buku punya harga sehingga contoh tidak menunjukkan kasus itu, tetapi ingat pola ini saat bekerja dengan data yang belum seragam.
Sebagai penutup, bersihkan database latihan:
db.dropDatabase()Output yang Dihasilkan:
{ ok: 1, dropped: 'latihan_crud' }Rangkuman #
| Operator | Bentuk | Document cocok bila |
|---|---|---|
$or | array of filter | minimal satu syarat benar |
$nor | array of filter | semua syarat salah |
$and | array of filter | semua syarat benar |
$not | operator document | operator di dalamnya tidak terpenuhi |
- Beberapa field dalam satu filter sudah di-AND-kan implisit;
$andeksplisit tidak diperlukan untuk itu. $andwajib saat field atau operator yang sama harus muncul dua kali — terutama dua kelompok$or— karena kunci object JavaScript tidak boleh ganda.$notmembungkus operator document (atau regex), menolak nilai polos, dan menangkap document tanpa field tersebut.$noradalah negasi$or: semua syarat harus salah agar document lolos.
Syarat tunggal, perbandingan, dan logika majemuk sudah lengkap. Chapter berikutnya menutup bagian query dengan tiga operator khusus field array: $all, $elemMatch, dan $size.