“Pendapatan = harga × terjual”, “harga setelah diskon 20%”, “umur buku berapa hari” — angka-angka seperti ini jarang disimpan langsung; angka-angka itu dihitung saat dibaca. Operator aritmetika aggregation menghitung di dalam pipeline sehingga hasilnya bisa langsung diurutkan, difilter, atau digroup tanpa memutar data ke aplikasi. Chapter ini menghitung pendapatan dan diskon katalog buku, membulatkan rasio, lalu membedah trik lama aritmetika tanggal.

Peta materi Chapter ini:

  • Empat operasi dasar plus $mod dan $abs.
  • Pembulatan dengan $round dan $trunc beserta jumlah desimal.
  • Tanggal dalam $add/$subtract — trik milidetik dan batasannya.
  • Operator pendukung statistik: $exp, $ln, $sqrt.

Peta Operator #

OperatorPeranCatatan
$addpenjumlahanbeberapa operand; tanggal + milidetik menghasilkan tanggal
$subtractpengurangandua operand; tanggal − tanggal menghasilkan milidetik
$multiplyperkalianbeberapa operand
$dividepembagiandua operand; hasil pecahan presisi ganda
$modsisa bagidua operand, tanda mengikuti pembagi
$absnilai mutlaksatu operand
$round / $truncpembulatan / pemotonganargumen kedua: jumlah desimal
$exp / $ln / $sqrteksponen / log natural / akaruntuk statistik dan penskalaan

Siapkan data uji standar Section ini:

js
use latihan_agregasi2
db.dropDatabase()
db.buku.insertMany([
  { judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, kategori: "fiksi", terjual: 120, tanggal_terbit: ISODate("2017-02-01T00:00:00Z"), tag: ["fiksi", "sastra", "penghargaan"], ulasan: [ { pengulas: "Budi", bintang: 5 }, { pengulas: "Siti", bintang: 4 } ] },
  { judul: "Bumi", penulis: "Tere Liye", harga: 89000, kategori: "fiksi", terjual: 250, tanggal_terbit: ISODate("2014-09-02T00:00:00Z"), tag: ["fiksi", "remaja", "fantasi"], ulasan: [ { pengulas: "Dewi", bintang: 5 }, { pengulas: "Budi", bintang: 4 }, { pengulas: "Siti", bintang: 5 } ] },
  { judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, kategori: "nonfiksi", terjual: 180, tanggal_terbit: ISODate("2018-07-01T00:00:00Z"), tag: ["filsafat", "pengembangan diri"], ulasan: [ { pengulas: "Siti", bintang: 5 } ] },
  { judul: "Atomic Habits", penulis: "James Clear", harga: 115000, kategori: "nonfiksi", terjual: 310, tanggal_terbit: ISODate("2018-10-16T00:00:00Z"), tag: ["pengembangan diri", "terjemahan"], ulasan: [ { pengulas: "Budi", bintang: 5 }, { pengulas: "Dewi", bintang: 4 } ] },
  { judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, kategori: "fiksi", terjual: 120, tanggal_terbit: ISODate("2016-06-15T00:00:00Z"), tag: ["fiksi", "remaja"], ulasan: [ { pengulas: "Dewi", bintang: 4 } ] },
  { judul: "Bumi Manusia", penulis: "Pramoedya A. Toer", harga: 105000, kategori: "sastra", terjual: 150, tanggal_terbit: ISODate("1980-05-25T00:00:00Z"), tag: ["sastra", "sejarah"], ulasan: [ { pengulas: "Budi", bintang: 5 }, { pengulas: "Siti", bintang: 5 }, { pengulas: "Dewi", bintang: 4 } ] },
  { judul: "Hujan Bulan Juni", penulis: "Sapardi Djoko Damono", harga: 65000, kategori: "puisi", terjual: 75, tanggal_terbit: ISODate("1994-06-10T00:00:00Z"), tag: ["puisi", "sastra"] },
  { judul: "Ensiklopedia Sains Populer", penulis: "Tim Sains", harga: 250000, kategori: "referensi", terjual: 40, tanggal_terbit: ISODate("2020-03-05T00:00:00Z"), tag: ["referensi", "sains"], ulasan: [ { pengulas: "Dewi", bintang: 4 } ] }
])

Output yang Dihasilkan:

text
switched to db latihan_agregasi2
{ ok: 1, dropped: 'latihan_agregasi2' }
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6ad3a715584bd27baa672ee2'),
    '1': ObjectId('6ad3a715584bd27baa672ee3'),
    '2': ObjectId('6ad3a715584bd27baa672ee4'),
    '3': ObjectId('6ad3a715584bd27baa672ee5'),
    '4': ObjectId('6ad3a715584bd27baa672ee6'),
    '5': ObjectId('6ad3a715584bd27baa672ee7'),
    '6': ObjectId('6ad3a715584bd27baa672ee8'),
    '7': ObjectId('6ad3a715584bd27baa672ee9')
  }
}

Operasi Dasar: Hitung di $project dan $set #

Semua operator ini expression — langsung dipakai di $project, $set, bahkan sebagai kunci $group. Hitung metrik Atomic Habits dalam satu stage:

js
db.buku.aggregate([
  { $match: { judul: "Atomic Habits" } },
  { $project: { _id: 0, judul: 1, harga: 1, terjual: 1,
      pendapatan: { $multiply: ["$harga", "$terjual"] },
      hargaDiskon: { $multiply: ["$harga", 0.8] },
      selisihDariTarget: { $subtract: ["$terjual", 200] },
      jarakDariTarget: { $abs: { $subtract: ["$terjual", 200] } },
      sisaBagiLimaPuluh: { $mod: ["$terjual", 50] } } }
])

Output yang Dihasilkan:

text
[
  {
    judul: 'Atomic Habits',
    harga: 115000,
    terjual: 310,
    pendapatan: 35650000,
    hargaDiskon: 92000,
    selisihDariTarget: 110,
    jarakDariTarget: 110,
    sisaBagiLimaPuluh: 10
  }
]

Bacaannya: 115.000 × 310 eksemplar = 35.650.000 pendapatan; diskon 20% menurunkan harga ke 92.000; penjualan 310 melampaui target 200 dengan selisih +110; $abs menghapus tanda minus bila penjualan di bawah target; dan 310 modulo 50 menyisakan 10. $add dan $multiply menerima lebih dari dua operand sekaligus — { $add: ["$harga", 5000, 1000] } sah — sedangkan $subtract, $divide, dan $mod ketat dua operand.

Pembulatan: $round versus $trunc #

$divide menghasilkan pecahan presisi ganda yang panjang; $round dan $trunc merapikannya dengan argumen kedua berupa jumlah desimal. Bedanya: $round membulatkan ke nilai terdekat, $trunc memotong tanpa melihat sisanya:

js
db.buku.aggregate([
  { $match: { judul: "Atomic Habits" } },
  { $project: { _id: 0, judul: 1, terjual: 1,
      rasioPerMinggu: { $divide: ["$terjual", 7] },
      dibulatkan: { $round: [ { $divide: ["$terjual", 7] }, 1 ] },
      dipotong: { $trunc: [ { $divide: ["$terjual", 7] }, 1 ] } } }
])

Output yang Dihasilkan:

text
[
  {
    judul: 'Atomic Habits',
    terjual: 310,
    rasioPerMinggu: 44.285714285714285,
    dibulatkan: 44.3,
    dipotong: 44.2
  }
]

Rasio 310 ÷ 7 = 44.285714…, lalu berpisah jalan: $round menaikkannya ke 44.3 karena digit berikutnya (8) lima ke atas, sedangkan $trunc mengeraskan ke 44.2 dengan membuang semua sisa. Tanpa argumen kedua, keduanya bekerja pada satuan — hasil integer. Pilih $round untuk tampilan laporan; $trunc saat kebijakan bisnis menuntut pemotongan tetap (misalnya stok tidak dibulatkan naik).

Tanggal di Dalam $add dan $subtract #

BSON Date pada dasarnya adalah milidetik, sehingga $add dan $subtract bisa menanganinya — dengan dua aturan khusus. Tanggal ditambah angka milidetik menghasilkan tanggal baru; tanggal dikurangi tanggal menghasilkan milidetik:

js
db.buku.aggregate([
  { $match: { judul: "Rumah Kaca" } },
  { $project: { _id: 0, judul: 1, tanggal_terbit: 1,
      tigaPuluhHariKemudian: { $add: ["$tanggal_terbit", 2592000000] },
      umurHari: { $divide: [ { $subtract: [ISODate("2026-12-05T00:00:00Z"), "$tanggal_terbit"] }, 86400000 ] } } }
])

Output yang Dihasilkan:

text
[
  {
    judul: 'Rumah Kaca',
    tanggal_terbit: ISODate('2016-06-15T00:00:00.000Z'),
    tigaPuluhHariKemudian: ISODate('2016-07-15T00:00:00.000Z'),
    umurHari: 3825
  }
]

Angka 2.592.000.000 adalah 30 hari × 24 jam × 60 menit × 60 detik × 1.000 milidetik; ditambahkan ke 15 Juni 2016 menghasilkan 15 Juli 2016. Arah kedua: selisih 5 Desember 2026 dikurangi tanggal terbit menghasilkan milidetik, dibagi 86.400.000 (satu hari) menjadi umur 3.825 hari.

$exp, $ln, dan $sqrt #

Tiga operator ini jarang muncul di aplikasi CRUD, tetapi menjadi inti perhitungan statistik dan penskalaan: pertumbuhan majemuk memakai $exp, transformasi logaritmik memakai $ln, dan jarak Euclidean memakai $sqrt:

js
db.buku.aggregate([
  { $match: { judul: "Bumi" } },
  { $project: { _id: 0,
      euler: { $exp: 1 },
      hargaSkalaLog: { $ln: "$harga" },
      akarPendapatan: { $sqrt: { $multiply: ["$harga", "$terjual"] } } } }
])

Output yang Dihasilkan:

text
[
  {
    euler: 2.718281828459045,
    hargaSkalaLog: 11.396391648714276,
    akarPendapatan: 4716.990566028302
  }
]

$exp: 1 menghasilkan bilangan Euler ≈ 2,718; $ln mengubah harga 89.000 menjadi 11,4 — pada skala logaritmik, perbandingan harga antar-buku jadi berbasis rasio, bukan selisih; $sqrt atas pendapatan 22.250.000 menghasilkan 4.716,99, komponen biasa dalam perhitungan jarak dan varians.

Rangkuman #

  • $add/$multiply multi-operand; $subtract/$divide/$mod dua operand; $abs satu operand — semuanya expression di $project/$set.
  • $round membulatkan ke terdekat, $trunc memotong; argumen kedua mengatur jumlah desimal.
  • Tanggal + milidetik = tanggal baru; tanggal − tanggal = milidetik — tapi untuk kalender sungguhan pakai $dateAdd/$dateDiff.
  • $exp, $ln, $sqrt melayani kebutuhan statistik dan penskalaan.

Semua perhitungan sejauh ini berdiri sendiri per document. Bagaimana bila angka perlu dihitung lintas document tetangga — total berjalan, rata-rata bergerak, peringkat? Itu pekerjaan $setWindowFields, stage window functions yang kita bahas berikutnya.