Halaman katalog toko buku hampir tidak pernah menampilkan semua field: pengunjung cukup melihat judul dan harga, admin butuh urutan penjualan, dan pencarian harus menyaring rentang harga. Di mongosh kita sudah menulis filter dan projection sebagai document — dari JavaScript, keduanya tetap berbentuk object biasa yang dikirim sebagai argumen method find(). Perbedaannya hanya rumahnya: object literal JavaScript menggantikan document mongosh. Chapter ini memetakan pola filter, projection, dan rantai sort/limit/skip pada collection buku dengan field bersarang penerbit.

Filter Adalah Object Biasa #

Method find() menerima query document — object JavaScript yang isinya field beserta syaratnya. Karena driver mengirim object itu sebagai BSON, seluruh operator query yang kita kenal bekerja tanpa perubahan:

KebutuhanBentuk filter
Persamaan{ judul: "Bumi Manusia" }
Perbandingan{ harga: { $gte: 100000 } }
Beberapa nilai{ tags: { $in: ["fiksi", "sejarah"] } }
Field bersarang{ "penerbit.kota": "Yogyakarta" }
Kombinasi ATAU{ $or: [{ penulis: "James Clear" }, { harga: { $lt: 95000 } }] }

Dua perilaku perlu diingat kembali. Field bertipe array cocok dengan nilai tunggal — { tags: "fiksi" } menemukan setiap document yang array tags-nya memuat "fiksi". Dan penulisan "penerbit.kota" memakai dot notation untuk menjangkau field di dalam object bersarang; tanda kutip wajib agar JavaScript membacanya sebagai satu nama field, bukan ekspresi property.

Projection dan Dot Notation dalam Praktik #

Projection diberikan lewat opsi projection — bukan argumen kedua find() seperti di mongosh. File berikut menanam lima judul lengkap dengan penerbit bersarang, lalu mencoba lima pola query sekaligus:

js
// filter-proyeksi.js — filter operator, projection, dan dot notation
// Jalankan: MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node filter-proyeksi.js
const { MongoClient } = require("mongodb")

const uri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017/?directConnection=true"

async function main() {
  const client = new MongoClient(uri)
  await client.connect()
  const katalog = client.db("latihan_js").collection("buku")
  await katalog.drop().catch(() => {})

  await katalog.insertMany([
    { judul: "Laskar Pelangi", penulis: "Andrea Hirata", harga: 95000, terjual: 132, penerbit: { nama: "Bentang Pustaka", kota: "Yogyakarta" }, tags: ["fiksi", "remaja"] },
    { judul: "Bumi Manusia", penulis: "Pramoedya A. Toer", harga: 110000, terjual: 205, penerbit: { nama: "Hasta Mitra", kota: "Jakarta" }, tags: ["fiksi", "sejarah"] },
    { judul: "Atomic Habits", penulis: "James Clear", harga: 125000, terjual: 318, penerbit: { nama: "Gramedia", kota: "Jakarta" }, tags: ["pengembangan diri"] },
    { judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 90000, terjual: 276, penerbit: { nama: "Kompas", kota: "Jakarta" }, tags: ["pengembangan diri", "filsafat"] },
    { judul: "Cantik Itu Luka", penulis: "Eka Kurniawan", harga: 105000, terjual: 98, penerbit: { nama: "Gramedia", kota: "Jakarta" }, tags: ["fiksi", "sejarah"] },
  ])

  const proyeksi = { projection: { _id: 0, judul: 1, harga: 1 } }

  // 1. Operator perbandingan pada satu field
  const mahal = await katalog.find({ harga: { $gte: 100000 } }, proyeksi).toArray()
  console.log("harga >= 100000   :", mahal)

  // 2. Dot notation untuk field bersarang
  const gramedia = await katalog.find({ "penerbit.nama": "Gramedia" }, proyeksi).toArray()
  console.log("penerbit Gramedia:", gramedia)

  // 3. Array field cocok dengan nilai tunggal, atau $in untuk beberapa nilai
  const bertagFiksi = await katalog.find({ tags: "fiksi" }, { projection: { _id: 0, judul: 1 } }).toArray()
  console.log("tags berisi fiksi :", bertagFiksi)
  const bertagSejarah = await katalog.find({ tags: { $in: ["sejarah"] } }, { projection: { _id: 0, judul: 1 } }).toArray()
  console.log("tags $in sejarah  :", bertagSejarah)

  // 4. $or menggabungkan beberapa kemungkinan
  const murahAtauClear = await katalog.find(
    { $or: [{ penulis: "James Clear" }, { harga: { $lt: 95000 } }] },
    proyeksi
  ).toArray()
  console.log("$or penulis/harga :", murahAtauClear)

  await client.close()
}

main().catch(err => {
  console.error("GAGAL:", err.name, "-", err.message)
  process.exit(1)
})
bash
MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node filter-proyeksi.js

Output yang Dihasilkan:

text
harga >= 100000   : [
  { judul: 'Bumi Manusia', harga: 110000 },
  { judul: 'Atomic Habits', harga: 125000 },
  { judul: 'Cantik Itu Luka', harga: 105000 }
]
penerbit Gramedia: [
  { judul: 'Atomic Habits', harga: 125000 },
  { judul: 'Cantik Itu Luka', harga: 105000 }
]
tags berisi fiksi : [
  { judul: 'Laskar Pelangi' },
  { judul: 'Bumi Manusia' },
  { judul: 'Cantik Itu Luka' }
]
tags $in sejarah  : [ { judul: 'Bumi Manusia' }, { judul: 'Cantik Itu Luka' } ]
$or penulis/harga : [
  { judul: 'Atomic Habits', harga: 125000 },
  { judul: 'Filosofi Teras', harga: 90000 }
]

Mari kita bedah baris-barisnya: const proyeksi = { projection: { _id: 0, judul: 1, harga: 1 } } menyiapkan opsi proyeksi sekali lalu dipakai berulang — bentuk { _id: 0, judul: 1, harga: 1 } adalah inclusion (hanya judul dan harga) dengan satu pengecualian: _id yang secara bawaan selalu ikut dinyalakan bersama inclusion. Filter "penerbit.nama": "Gramedia" menunjukkan dot notation menembus satu lapis object; hasilnya dua judul dari Gramedia. Baris tags: "fiksi" dan tags: { $in: ["sejarah"] } memperlihatkan dua cara menyaring array — nilai tunggal cukup ditulis langsung, beberapa pilihan memakai $in. Terakhir, $or menerima array berisi object filter lengkap, dan document cukup cocok dengan salah satunya.

Menyusun Urutan dengan sort, limit, skip #

find() mengembalikan FindCursor yang belum mengambil data apa pun. Method sort(), limit(), dan skip() dirantai pada cursor sebelum hasil diambil — tiap method mengembalikan cursor itu sendiri sehingga bisa ditulis berurutan:

js
// urut-batasi-lewati.js — rantai cursor: sort, limit, skip
// Jalankan SETELAH filter-proyeksi.js (memakai data yang ditanamkan file itu):
// MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node urut-batasi-lewati.js
const { MongoClient } = require("mongodb")

const uri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017/?directConnection=true"

async function main() {
  const client = new MongoClient(uri)
  await client.connect()
  const katalog = client.db("latihan_js").collection("buku")
  const proyeksi = { projection: { _id: 0, judul: 1, terjual: 1 } }

  // sort + limit: tiga buku dengan penjualan tertinggi
  const terlaris = await katalog.find({}, proyeksi).sort({ terjual: -1 }).limit(3).toArray()
  console.log("3 terlaris :", terlaris)

  // skip + limit: "halaman 2" berisi data setelah tiga teratas
  const halaman2 = await katalog.find({}, proyeksi).sort({ terjual: -1 }).skip(3).limit(3).toArray()
  console.log("halaman 2  :", halaman2)

  // sort dua kunci: kota penerbit naik, lalu harga turun (dot notation di sort)
  const perKota = await katalog.find({}, { projection: { _id: 0, judul: 1, harga: 1 } })
    .sort({ "penerbit.kota": 1, harga: -1 })
    .toArray()
  console.log("per kota   :", perKota)

  await client.close()
}

main().catch(err => {
  console.error("GAGAL:", err.name, "-", err.message)
  process.exit(1)
})
bash
MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node urut-batasi-lewati.js

Output yang Dihasilkan:

text
3 terlaris : [
  { judul: 'Atomic Habits', terjual: 318 },
  { judul: 'Filosofi Teras', terjual: 276 },
  { judul: 'Bumi Manusia', terjual: 205 }
]
halaman 2  : [
  { judul: 'Laskar Pelangi', terjual: 132 },
  { judul: 'Cantik Itu Luka', terjual: 98 }
]
per kota   : [
  { judul: 'Atomic Habits', harga: 125000 },
  { judul: 'Bumi Manusia', harga: 110000 },
  { judul: 'Cantik Itu Luka', harga: 105000 },
  { judul: 'Filosofi Teras', harga: 90000 },
  { judul: 'Laskar Pelangi', harga: 95000 }
]

Urutan rantai tidak mengubah hasil untuk sort → limit → skip pada satu query, tetapi kebiasaan menulis sesuai urutan logika (urutkan, batasi, lewati) membuat maksud kode mudah dibaca. Nilai -1 pada sort({ terjual: -1 }) berarti menurun; 1 berarti menaik. Pada sort terakhir, dot notation dipakai untuk kunci pertama ("penerbit.kota" naik) dan kunci kedua (harga turun) hanya berlaku untuk document yang seri pada kunci pertama — terlihat pada empat judul Jakarta yang tersusun dari harga tertinggi.

Rangkuman #

  • Filter dan projection dikirim sebagai object JavaScript; seluruh operator query mongosh ($gte, $in, $or) berlaku tanpa perubahan.
  • Projection ditulis pada opsi projection; inclusion { judul: 1, harga: 1 } plus _id: 0 untuk memangkas field bawaan itu.
  • Dot notation ("penerbit.kota") menjangkau field bersarang di filter, projection, dan sort.
  • sort(), limit(), skip() dirantai pada FindCursor sebelum toArray() — skip-based pagination sederhana tetapi makin mahal pada halaman yang dalam.