Dua anggota toko buku tidak boleh memakai email yang sama, dan dua judul tidak boleh berbagi ISBN. Aturan seperti ini sering ditempatkan di aplikasi — padahal dua aplikasi yang menulis ke database yang sama bisa sama-sama lalai. Unique index memindahkan penegakan aturan ke level database: server menolak setiap insert atau update yang melanggar keunikan, dari koneksi mana pun. Chapter ini menguji penolakan tersebut secara nyata, termasuk pada compound index dan pada data lama yang ternyata sudah berisi duplikat.
Peta materi Chapter ini:
- Membuat unique index dan menerima galat
E11000yang sebenarnya. - Index
_idsebagai unique index otomatis bawaan. - Compound unique index atas gabungan dua field.
- Kegagalan pembangunan index pada data existing yang mengandung duplikat.
Membuat Unique Index #
Opsi unique: true pada createIndex() menjadikan index sebagai penjaga keunikan. Siapkan katalog lima buku yang masing-masing memiliki ISBN:
use latihan_index
db.buku.drop()
db.buku.insertMany([
{ judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, isbn: "9786024246945" },
{ judul: "Bumi", penulis: "Tere Liye", harga: 89000, isbn: "9786022914024" },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, isbn: "9786020627436" },
{ judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, isbn: "9786020486824" },
{ judul: "Hujan Bulan Juni", penulis: "Sapardi Djoko Damono", harga: 65000, isbn: "9789794338212" }
])
db.buku.createIndex({ isbn: 1 }, { unique: true })Output yang Dihasilkan:
switched to db latihan_index
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bd139cb9d6ea21c17234'),
'1': ObjectId('6ad3bd139cb9d6ea21c17235'),
'2': ObjectId('6ad3bd139cb9d6ea21c17236'),
'3': ObjectId('6ad3bd139cb9d6ea21c17237'),
'4': ObjectId('6ad3bd139cb9d6ea21c17238')
}
}
isbn_1Sekarang coba sisipkan buku keenam dengan ISBN yang sudah dipakai “Bumi”:
try {
db.buku.insertOne({ judul: "Bumi Edisi Khusus", penulis: "Tere Liye", harga: 120000, isbn: "9786022914024" })
} catch (e) {
print("Kode galat: " + e.code)
print("Pesan: " + e.message)
}
db.buku.countDocuments({})Output yang Dihasilkan:
Kode galat: 11000
Pesan: E11000 duplicate key error collection: latihan_index.buku index: isbn_1 dup key: { isbn: "9786022914024" }
5Galat E11000 (duplicate key error) memuat tiga informasi berharga: nama collection, nama index yang dilanggar (isbn_1), dan nilai kunci duplikatnya ({ isbn: "9786022914024" }). Insert ditolak seketika — jumlah document tetap lima. Penegakan berlaku juga untuk update: operasi $set yang mengubah ISBN menjadi nilai milik buku lain akan ditolak dengan galat yang sama.
Index _id adalah Unique Index Otomatis #
Kita sebenarnya sudah memakai unique index sejak chapter pertama — tanpa membuatnya. Setiap collection lahir dengan index unik pada _id, dan itulah alasan dua document tidak pernah berbagi _id:
db.buku.insertOne({ _id: 1, judul: "Catatan Uji", penulis: "Tim", harga: 50000 })
try {
db.buku.insertOne({ _id: 1, judul: "Catatan Uji Kedua", penulis: "Tim", harga: 60000 })
} catch (e) {
print("Kode galat: " + e.code)
print("Pesan: " + e.message)
}Output yang Dihasilkan:
{ acknowledged: true, insertedId: 1 }
Kode galat: 11000
Pesan: E11000 duplicate key error collection: latihan_index.buku index: _id_ dup key: { _id: 1 }Pesan galatnya identik bentuknya — hanya nama indexnya _id_. Unique index bukan fitur terpisah melainkan properti yang bisa menempel pada index apa pun, termasuk index _id bawaan yang tidak bisa dihapus tersebut.
Compound Unique #
Keunikan sering kali berlaku untuk gabungan field, bukan masing-masing field. Penulis boleh menulis banyak buku, dan judul yang sama boleh dipakai penulis berbeda — yang tidak boleh adalah kombinasi yang sama persis:
db.buku.createIndex({ penulis: 1, judul: 1 }, { unique: true })
try {
db.buku.insertOne({ judul: "Bumi", penulis: "Tere Liye", harga: 95000, isbn: "9786022919999" })
} catch (e) {
print("Kode galat: " + e.code + " — index: " + e.message.match(/index: (\S+)/)[1])
}
db.buku.insertOne({ judul: "Bumi", penulis: "Fajar Nugraha", harga: 75000, isbn: "9786022917777" })
db.buku.countDocuments({})Output yang Dihasilkan:
penulis_1_judul_1
Kode galat: 11000 — index: penulis_1_judul_1
{
acknowledged: true,
insertedId: ObjectId('6ad3bd149cb9d6ea21c1723b')
}
7Insert pertama ditolak karena pasangan Tere Liye + Bumi sudah ada di index penulis_1_judul_1, sedangkan insert kedua diterima — penulis berbeda membuat kombinasi baru sah meskipun judulnya sama. Field penulis dan judul masing-masing bebas berduplikat; keunikannya melekat pada pasangannya.
Pembangunan Gagal pada Data Existing Duplikat #
Unique index dibangun di atas data yang ada — dan bila data lama ternyata mengandung duplikat, pembangunan gagal. Ini skenario nyata saat menerapkan aturan baru pada database yang sudah lama hidup:
db.stokLama.insertMany([
{ judul: "Bumi", isbn: "9786022914024" },
{ judul: "Bumi Cetakan Lama", isbn: "9786022914024" }
])
try {
db.stokLama.createIndex({ isbn: 1 }, { unique: true })
} catch (e) {
print("Kode galat: " + e.code)
print("Pesan: " + e.message)
}
db.stokLama.countDocuments({})Output yang Dihasilkan:
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bd149cb9d6ea21c1723c'),
'1': ObjectId('6ad3bd149cb9d6ea21c1723d')
}
}
Kode galat: 11000
Pesan: Index build failed: 3c5d50ae-745a-478d-8f6a-e8e9deb305cb: Collection latihan_index.stokLama ( 56792620-b0ed-4ce8-bdd0-76568b2f9f63 ) :: caused by :: E11000 duplicate key error collection: latihan_index.stokLama index: isbn_1 dup key: { isbn: "9786022914024" }
2Index isbn_1 tidak jadi dibuat, kedua document lama tetap utuh, dan aplikasi berjalan seperti sebelumnya — kegagalan build tidak merusak apa pun. Satu-satunya jalan adalah membersihkan duplikat terlebih dahulu (misalnya lewat aggregation $group yang kita bahas di Section Aggregation), lalu mengulang createIndex().
Rangkuman #
createIndex({ field: 1 }, { unique: true })menolak insert/update yang melanggar keunikan dengan galatE11000, lengkap dengan nama index dan nilai kunci duplikatnya.- Setiap collection otomatis memiliki unique index
_id_yang tidak bisa dihapus. - Compound unique index menjaga keunikan kombinasi field — masing-masing field tetap boleh berduplikat.
- Pembangunan unique index pada data existing yang mengandung duplikat gagal tanpa merusak data; bersihkan duplikat dulu, lalu ulangi pembangunan.
- Opsi
dropDupstidak lagi berfungsi dan diabaikan server — jangan mengandalkannya.