Database

MongoDB untuk Pemula: Database NoSQL

TOKEN

Panduan lengkap belajar MongoDB dari nol β€” konsep NoSQL vs SQL, documents & collections, CRUD operations, query lanjutan, indexes, aggregation pipeline, dan pengenalan Mongoose ODM

1. NoSQL vs SQL: Perbedaan Mendasar

MongoDB adalah database NoSQL berbasis dokumen yang menyimpan data dalam format BSON (Binary JSON). Berbeda dengan database relasional (SQL) yang menggunakan tabel dan baris, MongoDB menggunakan collections dan documents yang fleksibel dan tidak memerlukan schema tetap.

MongoDB pertama kali dirilis pada tahun 2009 oleh 10gen (sekarang MongoDB Inc.) dan kini menjadi salah satu database paling populer di dunia, digunakan oleh perusahaan seperti Forbes, Toyota, dan banyak startup teknologi. Kemampuannya dalam menangani data semi-terstruktur dan skalabilitas horizontal menjadikannya pilihan utama untuk aplikasi modern.

Perbandingan SQL vs NoSQL

Aspek SQL (Relasional) NoSQL (MongoDB)
Struktur DataTabel dengan baris dan kolomDocuments (JSON/BSON) dalam collections
SchemaKetat β€” harus didefinisikan di awalFleksibel β€” setiap document bisa berbeda
SkalabilitasVertikal (upgrade hardware)Horizontal (tambah server/sharding)
RelasiJOIN antar tabelEmbedding atau referencing manual
Bahasa QuerySQL (Structured Query Language)MongoDB Query Language (MQL)
ACIDβœ… Penuhβœ… Transaksi multi-document (v4.0+)
Kasus PenggunaanData terstruktur, laporan keuanganContent management, IoT, real-time analytics

Kapan Harus Memilih MongoDB?

πŸ’‘ Kapan Menggunakan MongoDB?
  • Schema berubah-ubah β€” Saat struktur data sering berubah atau belum ditentukan di awal pengembangan
  • Data hierarkis β€” Data bersarang (nested) seperti komentar dalam postingan blog
  • Skalabilitas tinggi β€” Aplikasi yang perlu menangani jutaan data dengan horizontal scaling
  • Development cepat β€” Prototyping dan MVP yang membutuhkan iterasi cepat
  • Real-time analytics β€” Data yang perlu diproses dan di-query secara real-time
Diagram: Perbandingan Struktur SQL vs NoSQL
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SQL (Relasional)                              β”‚
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚
β”‚  β”‚ Table: users    β”‚    β”‚ Table: posts    β”‚                     β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                     β”‚
β”‚  β”‚ id β”‚ name β”‚ age β”‚    β”‚idβ”‚user_idβ”‚title β”‚                     β”‚
β”‚  β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€                     β”‚
β”‚  β”‚ 1  β”‚ Budi β”‚ 25  β”‚    β”‚1 β”‚  1    β”‚ Halo β”‚                     β”‚
β”‚  β”‚ 2  β”‚ Ani  β”‚ 30  β”‚    β”‚2 β”‚  1    β”‚ Hi   β”‚                     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β”‚          ↕ JOIN ↕                                                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    NoSQL (MongoDB)                               β”‚
β”‚                                                                  β”‚
β”‚  Collection: users                                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚  β”‚ {                                            β”‚               β”‚
β”‚  β”‚   _id: ObjectId("..."),                      β”‚               β”‚
β”‚  β”‚   name: "Budi",                              β”‚               β”‚
β”‚  β”‚   age: 25,                                   β”‚               β”‚
β”‚  β”‚   posts: [                                   β”‚               β”‚
β”‚  β”‚     { title: "Halo", likes: 10 },            β”‚               β”‚
β”‚  β”‚     { title: "Hi", likes: 5 }                β”‚               β”‚
β”‚  β”‚   ]                                          β”‚               β”‚
β”‚  β”‚ }                                            β”‚               β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Instalasi & Setup MongoDB

Ada beberapa cara untuk memulai dengan MongoDB: instalasi lokal, Docker, atau menggunakan layanan cloud MongoDB Atlas yang menyediakan free tier hingga 512 MB storage.

Opsi 1: MongoDB Atlas (Rekomendasi Pemula)

MongoDB Atlas adalah layanan Database-as-a-Service (DBaaS) yang memungkinkan Anda menggunakan MongoDB tanpa instalasi apapun di komputer lokal:

  1. Buat akun gratis di mongodb.com/atlas
  2. Buat cluster M0 (gratis, 512 MB)
  3. Buat database user dan whitelist IP address Anda
  4. Dapatkan connection string untuk digunakan di aplikasi

Opsi 2: Docker (Untuk Development Lokal)

Bash
# Jalankan MongoDB dengan Docker
docker run -d \
  --name mongodb \
  -p 27017:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password123 \
  -v mongodb_data:/data/db \
  mongo:7

# Verifikasi container berjalan
docker ps | grep mongodb

# Masuk ke MongoDB shell
docker exec -it mongodb mongosh -u admin -p password123

Opsi 3: Instalasi Lokal

Bash
# Ubuntu / Debian
wget -qO- https://www.mongodb.org/static/pgp/server-7.0.asc | \
  sudo tee /etc/apt/trusted.gpg.d/server-7.0.asc
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu \
  jammy/mongodb-org/7.0 multiverse" | \
  sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt update && sudo apt install -y mongodb-org

# macOS dengan Homebrew
brew tap mongodb/brew
brew install mongodb-community@7.0

# Jalankan service
sudo systemctl start mongod    # Linux
brew services start mongodb-community@7.0  # macOS

Koneksi Pertama dengan mongosh

MongoDB Shell
# Masuk ke MongoDB shell
mongosh

# Atau koneksi ke Atlas
mongosh "mongodb+srv://cluster0.xxxxx.mongodb.net/" --username myUser

# Di dalam shell, cek status
db.runCommand({ ping: 1 })
// Output: { ok: 1 }

# Lihat semua database
show dbs

# Buat / pilih database
use belajar_mongo

# Lihat collection di database saat ini
show collections

3. Documents & Collections

Di MongoDB, data disimpan sebagai documents dalam format BSON (Binary JSON). Setiap document adalah sekumpulan key-value pairs yang dapat berisi berbagai tipe data. Documents dikelompokkan dalam collections β€” analoginya, collection seperti tabel di SQL, dan document seperti baris (row).

Struktur Document

BSON / JSON
{
  _id: ObjectId("64a7b2c3d4e5f6a7b8c9d0e1"),  // Auto-generated, unik
  nama: "Budi Santoso",
  email: "budi@example.com",
  umur: 25,
  aktif: true,
  alamat: {
    jalan: "Jl. Sudirman No. 123",
    kota: "Jakarta",
    kode_pos: "12190"
  },
  hobi: ["coding", "membaca", "gaming"],
  pendidikan: [
    { institusi: "Universitas Indonesia", tahun: 2022 },
    { institusi: "SMA Negeri 1", tahun: 2018 }
  ],
  created_at: ISODate("2026-06-25T10:30:00Z")
}

Tipe Data yang Didukung

Tipe Keterangan Contoh
StringTeks UTF-8"Hello World"
IntegerBilangan bulat (32/64 bit)42, NumberLong(9999999)
DoubleBilangan desimal3.14
BooleanTrue / Falsetrue
ObjectIdID unik 12-byte (default _id)ObjectId("64a7b2...")
DateTanggal dan waktuISODate("2026-06-25")
ArrayKumpulan nilai["a", "b", "c"]
ObjectDokumen bersarang{ key: "value" }
NullNilai kosongnull
Binary DataData binerBinData(0, "...")
⚠️ Perhatian: Ukuran Document

MongoDB memiliki batas maksimum 16 MB per document. Jika data Anda lebih besar dari itu, pertimbangkan untuk menggunakan GridFS (untuk file) atau memecah data ke beberapa documents dengan referencing.

Embedding vs Referencing

Salah satu keputusan penting dalam desain MongoDB adalah memilih antara embedding (menyimpan data bersama) atau referencing (menyimpan referensi ke document lain):

Pendekatan Kelebihan Kekurangan Cocok untuk
EmbeddingRead cepat, satu query dapat semua dataDocument bisa jadi besar, duplikasi dataData yang sering diakses bersama (1:few)
ReferencingData tidak duplik, document tetap kecilPerlu multiple queries / $lookupData banyak (1:many, many:many)

4. CRUD Operations

CRUD (Create, Read, Update, Delete) adalah operasi dasar yang harus dikuasai saat bekerja dengan database apapun. Berikut operasi CRUD di MongoDB lengkap dengan berbagai variasinya.

Create β€” Menyimpan Data

MongoDB Shell
// Insert satu document
db.users.insertOne({
  nama: "Budi Santoso",
  email: "budi@example.com",
  umur: 25,
  kota: "Jakarta",
  created_at: new Date()
})

// Insert banyak document sekaligus
db.users.insertMany([
  {
    nama: "Ani Wijaya",
    email: "ani@example.com",
    umur: 30,
    kota: "Bandung",
    created_at: new Date()
  },
  {
    nama: "Citra Dewi",
    email: "citra@example.com",
    umur: 22,
    kota: "Surabaya",
    created_at: new Date()
  },
  {
    nama: "Dedi Kurniawan",
    email: "dedi@example.com",
    umur: 28,
    kota: "Jakarta",
    created_at: new Date()
  }
])

// Output:
// {
//   acknowledged: true,
//   insertedIds: {
//     '0': ObjectId("..."),
//     '1': ObjectId("..."),
//     '2': ObjectId("...")
//   }
// }

Read β€” Membaca Data

MongoDB Shell
// Ambil semua document dalam collection
db.users.find()

// Ambil dengan format yang rapi (pretty print)
db.users.find().pretty()

// Cari berdasarkan field tertentu
db.users.find({ kota: "Jakarta" })

// Cari satu document saja
db.users.findOne({ email: "budi@example.com" })

// Cari berdasarkan ID
db.users.findOne({
  _id: ObjectId("64a7b2c3d4e5f6a7b8c9d0e1")
})

// Hitung jumlah document
db.users.countDocuments({ kota: "Jakarta" })
// Output: 2

// Cek apakah document ada
db.users.findOne({ email: "tidakada@example.com" })
// Output: null

Update β€” Memperbarui Data

MongoDB Shell
// Update satu document β€” $set mengubah field tertentu
db.users.updateOne(
  { email: "budi@example.com" },   // filter
  { $set: { umur: 26, kota: "Bandung" } }  // perubahan
)

// Update β€” $inc menambah nilai numerik
db.users.updateOne(
  { nama: "Budi Santoso" },
  { $inc: { umur: 1 } }  // umur += 1
)

// Update β€” $push menambah elemen ke array
db.users.updateOne(
  { nama: "Budi Santoso" },
  { $push: { hobi: "berenang" } }
)

// Update β€” $addToSet menambah tanpa duplikasi
db.users.updateOne(
  { nama: "Budi Santoso" },
  { $addToSet: { hobi: "coding" } }  // Tidak ditambah karena sudah ada
)

// Update banyak document sekaligus
db.users.updateMany(
  { kota: "Jakarta" },              // filter
  { $set: { status: "aktif" } }     // perubahan
)

// Replace seluruh document (kecuali _id)
db.users.replaceOne(
  { email: "budi@example.com" },
  {
    nama: "Budi Santoso",
    email: "budi@example.com",
    umur: 27,
    kota: "Yogyakarta",
    updated_at: new Date()
  }
)

Delete β€” Menghapus Data

MongoDB Shell
// Hapus satu document
db.users.deleteOne({ email: "dedi@example.com" })

// Hapus banyak document sesuai filter
db.users.deleteMany({ kota: "Jakarta" })

// Hapus SEMUA document dalam collection
db.users.deleteMany({})

// Hapus collection seluruhnya
db.users.drop()

// Hapus database (pilih dulu, lalu drop)
use temp_database
db.dropDatabase()

5. Query & Filtering Lanjutan

MongoDB menyediakan query operators yang sangat kaya untuk filtering data. Operator dimulai dengan tanda $ dan memungkinkan Anda membuat query yang kompleks dan fleksibel.

Comparison Operators

MongoDB Shell
// $eq (equal) β€” sama dengan
db.users.find({ kota: { $eq: "Jakarta" } })
// Sama dengan: db.users.find({ kota: "Jakarta" })

// $ne (not equal) β€” tidak sama dengan
db.users.find({ kota: { $ne: "Jakarta" } })

// $gt (greater than) β€” lebih besar dari
db.users.find({ umur: { $gt: 25 } })

// $gte (greater than or equal) β€” lebih besar atau sama dengan
db.users.find({ umur: { $gte: 25 } })

// $lt (less than) β€” kurang dari
db.users.find({ umur: { $lt: 30 } })

// $lte (less than or equal) β€” kurang atau sama dengan
db.users.find({ umur: { $lte: 25 } })

// $in β€” nilai ada dalam array
db.users.find({ kota: { $in: ["Jakarta", "Bandung", "Surabaya"] } })

// $nin (not in) β€” nilai tidak ada dalam array
db.users.find({ kota: { $nin: ["Jakarta", "Bandung"] } })

Logical Operators

MongoDB Shell
// $and β€” kedua kondisi harus terpenuhi
db.users.find({
  $and: [
    { umur: { $gte: 20 } },
    { kota: "Jakarta" }
  ]
})

// $or β€” salah satu kondisi terpenuhi
db.users.find({
  $or: [
    { umur: { $lt: 23 } },
    { kota: "Bandung" }
  ]
})

// $not β€” negasi kondisi
db.users.find({
  umur: { $not: { $gte: 30 } }
})

// $nor β€” tidak ada kondisi yang terpenuhi
db.users.find({
  $nor: [
    { kota: "Jakarta" },
    { umur: { $gt: 35 } }
  ]
})

// Kombinasi kompleks
db.users.find({
  $and: [
    { umur: { $gte: 20, $lte: 30 } },
    {
      $or: [
        { kota: "Jakarta" },
        { kota: "Bandung" }
      ]
    }
  ]
})

Projection, Sort & Pagination

MongoDB Shell
// Projection β€” pilih field yang ingin ditampilkan
db.users.find(
  {},
  { nama: 1, email: 1, _id: 0 }   // 1 = tampilkan, 0 = sembunyikan
)

// Sort β€” urutkan hasil
db.users.find().sort({ umur: 1 })    // Ascending (A→Z, 0→9)
db.users.find().sort({ umur: -1 })   // Descending (Z→A, 9→0)

// Skip & Limit β€” pagination
db.users.find()
  .sort({ nama: 1 })
  .skip(0)       // Lewati 0 dokumen (halaman 1)
  .limit(10)     // Ambil 10 dokumen

// Halaman ke-3 (10 per halaman)
db.users.find()
  .sort({ nama: 1 })
  .skip(20)      // Lewati 20 dokumen
  .limit(10)

// Element operators
db.users.find({ hobi: { $exists: true } })     // Field ada
db.users.find({ hobi: { $exists: false } })    // Field tidak ada
db.users.find({ umur: { $type: "int" } })      // Tipe data tertentu

// Regex β€” pencarian teks
db.users.find({ nama: { $regex: /Budi/i } })   // Case-insensitive
db.users.find({ email: { $regex: /@gmail/ } })

6. Indexes untuk Performa

Index di MongoDB berfungsi sama seperti indeks di buku β€” mempercepat proses pencarian data. Tanpa index, MongoDB harus melakukan collection scan (memeriksa semua document satu per satu), yang sangat lambat untuk collection besar.

⚠️ Peringatan Performa

Collection tanpa index yang tepat pada collection dengan jutaan document bisa membutuhkan waktu detik bahkan menit untuk satu query. Dengan index yang tepat, query yang sama bisa selesai dalam milidetik.

Jenis-jenis Index

MongoDB Shell
// Single Field Index β€” index pada satu field
db.users.createIndex({ email: 1 })         // Ascending
db.users.createIndex({ created_at: -1 })   // Descending

// Unique Index β€” memastikan nilai unik
db.users.createIndex({ email: 1 }, { unique: true })

// Compound Index β€” index pada beberapa field
db.users.createIndex({ kota: 1, umur: -1 })

// Text Index β€” untuk full-text search
db.posts.createIndex({ title: "text", content: "text" })

// Cari dengan text search
db.posts.find({ $text: { $search: "tutorial mongodb" } })

// Partial Index β€” index hanya untuk dokumen tertentu
db.users.createIndex(
  { email: 1 },
  { partialFilterExpression: { aktif: true } }
)

// TTL Index β€” auto-delete setelah waktu tertentu
db.sessions.createIndex(
  { created_at: 1 },
  { expireAfterSeconds: 3600 }  // Hapus setelah 1 jam
)

Mengelola Index

MongoDB Shell
// Lihat semua index pada collection
db.users.getIndexes()

// Hapus index
db.users.dropIndex({ email: 1 })
db.users.dropIndex("email_1")    // Berdasarkan nama

// Hapus semua index kecuali _id
db.users.dropIndexes()

// Explain β€” analisis performa query
db.users.find({ email: "budi@example.com" }).explain("executionStats")
// Perhatikan: totalDocsExamined β€” semakin kecil semakin baik
Diagram: Cara Kerja Index (B-Tree)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 B-Tree Index Structure                    β”‚
β”‚                                                          β”‚
β”‚                      [M]                                 β”‚
β”‚                     /    \                               β”‚
β”‚                 [D, H]    [P, T]                         β”‚
β”‚                / |  \    / |  \                          β”‚
β”‚              [A,C][F,G][J-L][N-O][R-S][V-X][Z]         β”‚
β”‚                                                          β”‚
β”‚  Cari "Jakarta":                                         β”‚
β”‚  Tanpa Index β†’ Scan semua 1,000,000 dokumen (lambat)    β”‚
β”‚  Dengan Index β†’ Traverse B-Tree ~20 langkah (cepat!)     β”‚
β”‚                                                          β”‚
β”‚  Complexity: O(log n) vs O(n)                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

7. Aggregation Pipeline

Aggregation Pipeline adalah fitur paling powerful di MongoDB untuk memproses dan menganalisis data. Konsepnya mirip dengan GROUP BY di SQL, tetapi jauh lebih fleksibel. Data mengalir melalui serangkaian stages yang memproses dan mentransformasi data secara berurutan.

Diagram: Aggregation Pipeline Flow
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Input   │───►│ $match  │───►│ $group  │───►│ $sort   │───►│ Output  β”‚
β”‚Collectionβ”‚    β”‚ Filter  β”‚    β”‚ Group & β”‚    β”‚ Urutkan β”‚    β”‚ Results β”‚
β”‚         β”‚    β”‚ data    β”‚    β”‚ Agregat β”‚    β”‚ hasil   β”‚    β”‚         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 1000 docs      500 docs       20 groups      20 groups      20 results

Contoh Aggregation Pipeline

MongoDB Shell
// Contoh collection "orders":
// { _id, user: "Budi", produk: "Laptop", harga: 12000000, jumlah: 1, status: "selesai" }
// { _id, user: "Ani",  produk: "Mouse",  harga: 150000,   jumlah: 2, status: "selesai" }
// { _id, user: "Budi", produk: "Keyboard",harga: 500000,  jumlah: 1, status: "pending" }

// 1. Hitung total pendapatan per user (hanya yang selesai)
db.orders.aggregate([
  // Stage 1: Filter hanya order yang selesai
  { $match: { status: "selesai" } },

  // Stage 2: Group per user, hitung total
  { $group: {
      _id: "$user",
      totalPendapatan: { $sum: { $multiply: ["$harga", "$jumlah"] } },
      jumlahOrder: { $sum: 1 },
      rataRataHarga: { $avg: "$harga" }
    }
  },

  // Stage 3: Urutkan dari pendapatan terbesar
  { $sort: { totalPendapatan: -1 } }
])

// 2. Lookup (JOIN) β€” gabungkan data dari collection lain
db.orders.aggregate([
  { $lookup: {
      from: "users",              // Collection yang digabung
      localField: "user",         // Field di collection ini
      foreignField: "nama",       // Field di collection lain
      as: "userData"              // Nama field hasil
    }
  },
  { $unwind: "$userData" }        // "Buka" array menjadi object
])

// 3. Analisis per kota β€” siapa yang paling banyak belanja?
db.orders.aggregate([
  { $lookup: {
      from: "users",
      localField: "user",
      foreignField: "nama",
      as: "userData"
    }
  },
  { $unwind: "$userData" },
  { $group: {
      _id: "$userData.kota",
      totalBelanja: { $sum: { $multiply: ["$harga", "$jumlah"] } },
      jumlahPembeli: { $addToSet: "$user" }
    }
  },
  { $project: {
      kota: "$_id",
      totalBelanja: 1,
      jumlahPembeli: { $size: "$jumlahPembeli" }
    }
  },
  { $sort: { totalBelanja: -1 } }
])

Common Aggregation Stages

Stage Fungsi SQL Equivalent
$matchFilter dokumenWHERE
$groupKelompokkan & agregasiGROUP BY
$sortUrutkan hasilORDER BY
$projectPilih / bentuk ulang fieldSELECT
$limitBatasi jumlah hasilLIMIT
$skipLewati N dokumenOFFSET
$lookupJOIN dengan collection lainLEFT JOIN
$unwindPecah array jadi dokumenβ€”
$addFieldsTambah field baruComputed column
$countHitung jumlah dokumenCOUNT(*)

8. Pengenalan Mongoose ODM

Mongoose adalah ODM (Object Data Modeling) library untuk MongoDB dan Node.js. Mongoose menyediakan schema-based solution untuk memodelkan aplikasi Anda, termasuk validasi data, type casting, dan business logic hooks.

Instalasi Mongoose

Bash
# Inisialisasi proyek Node.js
mkdir mongo-app && cd mongo-app
npm init -y

# Instal mongoose
npm install mongoose

# (Opsional) Untuk environment variables
npm install dotenv

Mendefinisikan Schema & Model

JavaScript β€” models/User.js
const mongoose = require('mongoose');

// Definisikan Schema β€” "blueprint" untuk data
const userSchema = new mongoose.Schema({
  nama: {
    type: String,
    required: [true, 'Nama wajib diisi'],
    trim: true,
    minlength: [3, 'Nama minimal 3 karakter'],
    maxlength: [100, 'Nama maksimal 100 karakter']
  },
  email: {
    type: String,
    required: [true, 'Email wajib diisi'],
    unique: true,
    lowercase: true,
    match: [/^\S+@\S+\.\S+$/, 'Format email tidak valid']
  },
  umur: {
    type: Number,
    min: [0, 'Umur tidak boleh negatif'],
    max: [150, 'Umur tidak valid']
  },
  role: {
    type: String,
    enum: ['user', 'admin', 'moderator'],
    default: 'user'
  },
  hobi: {
    type: [String],
    default: []
  },
  alamat: {
    jalan: String,
    kota: String,
    kode_pos: String
  },
  aktif: {
    type: Boolean,
    default: true
  }
}, {
  timestamps: true   // Auto tambah createdAt & updatedAt
});

// Virtual field β€” tidak disimpan di database
userSchema.virtual('infoLengkap').get(function() {
  return `${this.nama} (${this.email}) - ${this.umur} tahun`;
});

// Middleware (hook) sebelum save
userSchema.pre('save', function(next) {
  console.log(`Menyimpan user: ${this.nama}`);
  next();
});

// Static method
userSchema.statics.findByKota = function(kota) {
  return this.find({ 'alamat.kota': kota });
};

// Instance method
userSchema.methods.isActive = function() {
  return this.aktif === true;
};

// Buat Model dari Schema
const User = mongoose.model('User', userSchema);
module.exports = User;

CRUD dengan Mongoose

JavaScript β€” app.js
const mongoose = require('mongoose');
const User = require('./models/User');

// Koneksi ke MongoDB
mongoose.connect('mongodb://localhost:27017/belajar_mongo')
  .then(() => console.log('βœ… Terhubung ke MongoDB'))
  .catch(err => console.error('❌ Error:', err));

async function main() {
  // CREATE β€” buat user baru
  const user = new User({
    nama: 'Budi Santoso',
    email: 'budi@example.com',
    umur: 25,
    alamat: { jalan: 'Jl. Sudirman 123', kota: 'Jakarta' }
  });
  await user.save();
  console.log('User tersimpan:', user.infoLengkap);

  // CREATE β€” alternatif dengan create()
  const users = await User.create([
    { nama: 'Ani', email: 'ani@example.com', umur: 30 },
    { nama: 'Citra', email: 'citra@example.com', umur: 22 }
  ]);

  // READ β€” cari dengan filter
  const jakartaUsers = await User.find({ 'alamat.kota': 'Jakarta' });
  const satuUser = await User.findOne({ email: 'budi@example.com' });

  // READ β€” dengan chaining
  const hasil = await User
    .find({ umur: { $gte: 20 } })
    .select('nama email umur')
    .sort({ nama: 1 })
    .limit(10)
    .skip(0);

  // UPDATE
  await User.updateOne(
    { email: 'budi@example.com' },
    { $set: { umur: 26 } }
  );

  // Atau gunakan findByIdAndUpdate untuk return document baru
  const updated = await User.findByIdAndUpdate(
    user._id,
    { $set: { umur: 27 } },
    { new: true }  // Return document setelah update
  );

  // DELETE
  await User.deleteOne({ email: 'citra@example.com' });

  // Validasi error handling
  try {
    const invalid = new User({ nama: '', email: 'bad' });
    await invalid.save();
  } catch (err) {
    console.log('Validasi error:', err.errors.nama.message);
    // Output: "Nama wajib diisi"
  }
}

main();
πŸ’‘ Tips Mongoose
  • Gunakan lean() untuk query yang lebih cepat saat tidak perlu virtual methods
  • Manfaatkan populate() sebagai alternatif dari aggregation $lookup
  • Selalu tangkap error dari validasi untuk UX yang lebih baik
  • Gunakan select() untuk mengambil hanya field yang dibutuhkan (projection)

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang MongoDB:

Pertanyaan 1: Apa perbedaan utama antara database SQL dan NoSQL (MongoDB)?

a) SQL lebih cepat dari NoSQL
b) NoSQL menggunakan tabel, SQL menggunakan dokumen
c) SQL memiliki schema ketat (tabel), NoSQL fleksibel (dokumen tanpa schema tetap)
d) Tidak ada perbedaan yang signifikan

Pertanyaan 2: Operator MongoDB apa yang digunakan untuk mencari dokumen dengan umur lebih dari 25?

a) { umur: { $bigger: 25 } }
b) { umur: { $gt: 25 } }
c) { umur: > 25 }
d) { umur: { $greaterThan: 25 } }

Pertanyaan 3: Apa fungsi dari Index di MongoDB?

a) Mengenkripsi data untuk keamanan
b) Mempercepat proses pencarian dan query data
c) Menghapus data yang sudah kadaluarsa
d) Mengompresi ukuran database

Pertanyaan 4: Apa fungsi dari Aggregation Pipeline di MongoDB?

a) Menggabungkan beberapa database menjadi satu
b) Memproses dan mentransformasi data melalui serangkaian stages
c) Membuat backup otomatis database
d) Menghubungkan MongoDB dengan SQL database

Pertanyaan 5: Apa yang dilakukan Mongoose dalam proyek Node.js yang menggunakan MongoDB?

a) Menggantikan MongoDB dengan database SQL
b) Menyediakan schema, validasi, dan ODM layer untuk MongoDB
c) Membuat UI untuk mengelola database MongoDB
d) Mengkonversi MongoDB menjadi PostgreSQL
πŸ” Zoom
100%
🎨 Tema