Katalog buku kita kini menjual tiga bentuk sekaligus: buku fisik punya field bahan dan halaman, ebook punya format dan ukuranMB, audiobook punya durasiJam. Field atribut tumbuh bebas mengikuti jenis produk — dan membuat index satu per satu untuk setiap field yang mungkin muncul jelas tidak praktis. Wildcard index menjawabnya: satu perintah yang meng-index seluruh field di bawah sebuah path, atau bahkan seluruh collection, tanpa menyebutkan nama field secara eksplisit.
Peta materi Chapter ini:
- Wildcard per path:
"atribut.$**"untuk semua field di bawahatribut. - Wildcard seluruh collection:
"$**". - Menyaring field yang masuk dengan
wildcardProjection. - Batasan yang berlaku — diuji langsung di MongoDB 9.0.
Wildcard pada Path Tertentu #
String khusus "$**" pada posisi nilai menggantikan arah 1/-1. Diletakkan setelah nama path ("atribut.$**"), index meng-cover semua field di bawah atribut apa pun namanya:
use latihan_index
db.buku.drop()
db.buku.insertMany([
{ judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, atribut: { penerbit: "Gramedia", tahun: 2017 } },
{ judul: "Bumi", penulis: "Tere Liye", harga: 89000, atribut: { format: "ebook", berkas: "PDF", ukuranMB: 12 } },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, atribut: { bahan: "softcover", halaman: 380 } },
{ judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, atribut: { format: "audiobook", durasiJam: 11 } }
])
db.buku.createIndex({ "atribut.$**": 1 })
const rencana = db.buku.find({ "atribut.format": "ebook" }).explain()
print(rencana.queryPlanner.winningPlan.inputStage.stage + " lewat " + rencana.queryPlanner.winningPlan.inputStage.indexName)Output yang Dihasilkan:
switched to db latihan_index
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3bdf9e8060a3eadba68d4'),
'1': ObjectId('6ad3bdf9e8060a3eadba68d5'),
'2': ObjectId('6ad3bdf9e8060a3eadba68d6'),
'3': ObjectId('6ad3bdf9e8060a3eadba68d7')
}
}
atribut.$**_1
IXSCAN lewat atribut.$**_1Index atribut.$**_1 langsung melayani query atas atribut.format — field yang tidak pernah kita deklarasikan di createIndex(). Index ini menampung kunci untuk penerbit, tahun, format, berkas, ukuranMB, bahan, halaman, dan durasiJam sekaligus, dan field baru yang muncul bulan depan pun otomatis masuk tanpa migrasi.
Wildcard Seluruh Collection #
Bentuk kedua meletakkan "$**" langsung sebagai key — meng-index semua field di seluruh document (kecuali _id, yang selalu dikecualikan):
db.buku.createIndex({ "$**": 1 })
const rencana = db.buku.find({ judul: "Bumi" }).explain()
print(rencana.queryPlanner.winningPlan.inputStage.stage + " lewat " + rencana.queryPlanner.winningPlan.inputStage.indexName)Output yang Dihasilkan:
$**_1
IXSCAN lewat $**_1Query atas judul — field di luar atribut — kini terlayani oleh $**_1. Bentuk ini cocok untuk schema yang benar-benar liar, misalnya penyimpanan atribut produk dari banyak pemasok dengan format yang tidak bisa diprediksi. Konsekuensinya jelas: indexnya besar karena menampung hampir semua field, dan perancangan yang lebih disiplin (path-specific atau index biasa) hampir selalu lebih efisien.
Menyaring Field dengan wildcardProjection #
Bila "$**" terasa terlalu rakus, wildcardProjection memilih field mana saja yang ikut ter-index. Opsi ini hanya sah untuk key "$**":
db.buku.createIndex(
{ "$**": 1 },
{ wildcardProjection: { judul: 1, harga: 1 }, name: "wildcard_terpilih" }
)
db.buku.getIndexes().filter(i => i.name === "wildcard_terpilih")Output yang Dihasilkan:
wildcard_terpilih
[
{
v: 2,
key: { '$**': 1 },
name: 'wildcard_terpilih',
collation: { locale: 'simple' },
wildcardProjection: { judul: 1, harga: 1 }
}
]Spesifikasi index menampilkan wildcardProjection: { judul: 1, harga: 1 } — dari seluruh field collection, hanya dua field itu yang diberi kunci. Sama seperti projection pada find(), nilai 1 menyertakan dan 0 mengecualikan; _id secara bawaan ikut kecuali dinyatakan _id: 0.
Batasan Wildcard Index #
Tiga batasan berikut penting dan semuanya kita uji langsung:
try {
db.buku.createIndex({ "atribut.$**": 1 }, { unique: true })
} catch (e) {
print("Unique ditolak: " + e.message)
}
try {
db.buku.createIndex({ "atribut.$**": 1, "ulasan.$**": 1 })
} catch (e) {
print("Dua wildcard ditolak: " + e.message)
}
print("Compound satu wildcard: " + db.buku.createIndex({ penulis: 1, "atribut.$**": 1 }))Output yang Dihasilkan:
Unique ditolak: Error in specification { unique: true, name: "atribut.$**_1", key: { atribut.$**: 1 }, v: 2 } :: caused by :: Index type 'wildcard' does not support the 'unique' option
Dua wildcard ditolak: Error in specification { name: "atribut.$**_1_ulasan.$**_1", key: { atribut.$**: 1, ulasan.$**: 1 } } :: caused by :: Index Key can contain only one wildcard index field
Compound satu wildcard: penulis_1_atribut.$**_1Hasil pengujian di MongoDB 9.0:
- Tidak mendukung
unique— server menolak dengan pesan Index type ‘wildcard’ does not support the ‘unique’ option. Penegakan keunikan tetap harus lewat index biasa pada field yang benar-benar dibatasi nilainya. - Maksimal satu wildcard per index — menggabungkan dua path wildcard ditolak: Index Key can contain only one wildcard index field.
- Compound wildcard diperbolehkan — sejak MongoDB 7.0, satu wildcard boleh digabung dengan field biasa seperti
{ penulis: 1, "atribut.$**": 1 }di atas, mengikuti aturan prefix seperti compound index pada umumnya.
Rangkuman #
createIndex({ "atribut.$**": 1 })meng-index semua field di bawahatributtanpa menyebut nama; field baru otomatis ter-cover.createIndex({ "$**": 1 })meng-index seluruh collection kecuali_id; gunakan untuk schema yang tidak bisa diprediksi, dengan kesadaran ukurannya besar.wildcardProjectionmembatasi field yang masuk, tetapi hanya untuk key"$**".- Wildcard index tidak mendukung
unique, hanya boleh satu wildcard per index, dan sejak 7.0 boleh digabung dengan field biasa dalam compound index.