Relasi one-to-one adalah bentuk relasi paling sederhana: satu entitas berpasangan dengan tepat satu entitas lain — satu pengguna punya satu profil, satu buku punya satu data cetak. Justru karena sederhana, keputusannya sering disepelekan: hampir selalu kita bisa embed, tetapi ada tiga alasan spesifik untuk tetap memisahkan document. Chapter ini membedah keduanya dengan domain toko buku: profil pengguna dan data kredensialnya.
Default: Satu Document #
Untuk relasi one-to-one yang datanya kecil dan selalu dibaca bersama, embed adalah pilihan bawaan — subdocument di dalam induknya, tanpa field referensi, tanpa query kedua:
use latihan_modeling
db.dropDatabase()
db.pengguna.insertMany([
{
_id: 1, nama: "Budi", email: "[email protected]",
profil: { telepon: "0812-3456-7890", kota: "Bandung", alamat: "Jl. Merdeka 10" }
},
{
_id: 2, nama: "Siti", email: "[email protected]",
profil: { telepon: "0813-9876-5432", kota: "Jakarta", alamat: "Jl. Sudirman 45" }
}
])
db.pengguna.findOne({ _id: 1 })Output yang Dihasilkan:
switched to db latihan_modeling
{ ok: 1, dropped: 'latihan_modeling' }
{ acknowledged: true, insertedIds: { '0': 1, '1': 2 } }
{
_id: 1,
nama: 'Budi',
email: '[email protected]',
profil: {
telepon: '0812-3456-7890',
kota: 'Bandung',
alamat: 'Jl. Merdeka 10'
}
}Satu findOne mengembalikan pengguna lengkap dengan profilnya — persis pola akses halaman akun. Subdocument profil tidak punya _id sendiri dan tidak akan pernah ditempatkan di collection lain, karena konteks kehidupannya memang melekat pada induknya.
Tiga hal membuat bentuk ini cocok untuk profil:
- Satu document, satu tulis atomic — memperbarui nomor telepon berarti mengubah subdocument di dalam document yang sama; tidak ada pasangan document yang harus tetap sinkron.
- Path bertitik untuk query —
{ "profil.kota": "Bandung" }menanyakan isi subdocument tanpa sintaks khusus; kita sudah memakainya sejak Section CRUD. - Projection bisa menyusutkan —
{ "profil.telepon": 1 }mengambil satu bagian profil saja saat halaman lain cuma butuh itu.
Ukuran juga bersahabat: profil yang wajar hanya puluhan byte, jadi embed tidak pernah mendekati batas 16 MB. Relasi satu-ke-satu yang kecil dan tenang seperti ini adalah kasus termudah di seluruh pemodelan data.
Tiga Alasan Memisahkan Document #
Embed bukan satu-satunya jawaban. Tiga kondisi berikut membuat document terpisah lebih tepat:
- Data sensitif — hash kata sandi dan token sebaiknya tidak ikut terbawa setiap kali profil pengguna dibaca. Collection terpisah memungkinkan kontrol akses berbeda dan memperkecil permukaan bocornya: query profil tidak pernah menyentuh collection kredensial.
- Ukuran besar — satu buku boleh punya deskripsi panjang berisi ratusan kilobyte teks dan lampiran. Menyatukannya membuat setiap query buku — termasuk yang hanya butuh
juduldanharga— memindahkan data besar itu dari disk ke memori. - Pola akses berbeda — data yang ditulis jauh lebih sering daripada dibaca induknya (misalnya token sesi yang diperbarui setiap login) enak dipelihara sendiri agar document induk tidak ikut tersentuh.
Kita terapkan alasan pertama dan ketiga: kredensial pindah ke collection sendiri, ditaut dengan penggunaId:
db.pengguna_kredensial.createIndex({ penggunaId: 1 }, { unique: true })
db.pengguna_kredensial.insertMany([
{ penggunaId: 1, email: "[email protected]", passwordHash: "$2b$12$YOUR_PASSWORD_HASH", tokenSesi: "tok-abc123", terakhirLogin: ISODate("2026-12-06T02:15:00Z") },
{ penggunaId: 2, email: "[email protected]", passwordHash: "$2b$12$YOUR_PASSWORD_HASH", tokenSesi: "tok-xyz789", terakhirLogin: ISODate("2026-12-05T19:40:00Z") }
])
db.pengguna_kredensial.findOne({ email: "[email protected]" })Output yang Dihasilkan:
penggunaId_1
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad398c40faf57d9e2a9ac02'),
'1': ObjectId('6ad398c40faf57d9e2a9ac03')
}
}
{
_id: ObjectId('6ad398c40faf57d9e2a9ac02'),
penggunaId: 1,
email: '[email protected]',
passwordHash: '$2b$12$YOUR_PASSWORD_HASH',
tokenSesi: 'tok-abc123',
terakhirLogin: ISODate('2026-12-06T02:15:00.000Z')
}Bedah blok di atas:
createIndexdenganunique: true— memaksa relasi tetap one-to-one di tingkat database: percobaan menyisipkan kredensial kedua untukpenggunaId: 1akan gagal dengan duplicate key. Relasi di MongoDB memang dijaga aplikasi, tetapi unique index adalah tempat menegakkannya.- Field
penggunaId— pengganti foreign key; nilainya sengaja sama dengan_iddipenggunaagar gabungan lewat$lookupmudah. findOnepada email — inilah keseluruhan query login: menyentuh collection kredensial saja, tanpa memuat profil, alamat, atau data pengguna lainnya.
Menggabungkan Saat Dibutuhkan #
Alur login memverifikasi hash, lalu halaman akun membutuhkan nama dan profil — dua document, satu tautan. $lookup menggabungkannya hanya dengan field yang aman:
db.pengguna.aggregate([
{ $match: { _id: 1 } },
{ $lookup: { from: "pengguna_kredensial", localField: "_id", foreignField: "penggunaId", as: "kredensial" } },
{ $project: { nama: 1, email: 1, kredensial: { terakhirLogin: 1 } } }
])Output yang Dihasilkan:
[
{
_id: 1,
nama: 'Budi',
email: '[email protected]',
kredensial: [ { terakhirLogin: ISODate('2026-12-06T02:15:00.000Z') } ]
}
]Perhatikan $project pada baris terakhir: dari seluruh document kredensial, hanya terakhirLogin yang diangkat — passwordHash dan tokenSesi tidak ikut keluar collection. Pemisahan document memberi kita kesempatan memilih field per pola akses, hal yang tidak tersedia ketika semua data menumpuk dalam satu document besar.
Sisi tulisnya ikut tercermin: pembaruan tokenSesi setiap login hanya menyentuh pengguna_kredensial, sementara pengguna yang mengedit alamat pengirimannya hanya menyentuh pengguna. Dua pola akses yang berbeda hidup di dua collection yang berbeda — tidak ada tulis yang saling mengganggu, dan document yang panas (kredensial, kecil) tidak pernah menyeret yang dingin (profil) ikut ditulis ulang.
Rangkuman #
- One-to-one yang kecil dan selalu dibaca bersama cukup di-embed sebagai subdocument — satu query untuk semuanya.
- Pisahkan document bila data sensitif, berukuran besar, atau punya pola akses yang berbeda dari induknya.
- Field referensi plus
uniqueindex adalah cara menegakkan keunikan relasi di tingkat collection. $lookupdengan$projectmenggabungkan pasangan document sambil memilih field yang aman untuk dibaca.
Relasi berikutnya jauh lebih sering muncul dan punya lebih banyak pilihan: one-to-many dengan tiga pola penyimpanan.