Index pada time series collection bekerja dengan prinsip yang sudah kita kenal, tetapi dengan satu kejutan: index yang kita buat di field measurement tidak pernah benar-benar menempel pada data mentah. Server menerjemahkannya menjadi index di atas bucket internal. Bab ini memeriksa index otomatis yang dibuat server, menambahkan secondary index pada metaField dan pada data numerik, lalu membuktikan lewat explain() bagaimana index-index itu dipetakan ke struktur bucket.
Index Otomatis: metaField + timeField #
Saat collection dibuat, server langsung membangun satu compound index. Kita periksa dengan data cuaca yang sama:
use latihan_timeseries
db.dropDatabase()
db.createCollection("cuaca", { timeseries: { timeField: "waktu", metaField: "sensor", granularity: "minutes" } })
db.cuaca.insertMany([
{ waktu: ISODate("2026-12-10T02:00:00Z"), sensor: { sensor_id: "JKT-01", kota: "Jakarta" }, suhu: 27.4, kelembapan: 78 },
{ waktu: ISODate("2026-12-10T02:10:00Z"), sensor: { sensor_id: "JKT-01", kota: "Jakarta" }, suhu: 27.6, kelembapan: 77 },
{ waktu: ISODate("2026-12-10T02:20:00Z"), sensor: { sensor_id: "JKT-01", kota: "Jakarta" }, suhu: 27.9, kelembapan: 75 },
{ waktu: ISODate("2026-12-10T02:30:00Z"), sensor: { sensor_id: "BDG-07", kota: "Bandung" }, suhu: 22.1, kelembapan: 85 },
{ waktu: ISODate("2026-12-10T02:40:00Z"), sensor: { sensor_id: "BDG-07", kota: "Bandung" }, suhu: 22.3, kelembapan: 84 },
{ waktu: ISODate("2026-12-10T02:50:00Z"), sensor: { sensor_id: "SBY-12", kota: "Surabaya" }, suhu: 30.2, kelembapan: 68 }
])
db.cuaca.getIndexes()Output yang Dihasilkan:
switched to db latihan_timeseries
{ ok: 1, dropped: 'latihan_timeseries' }
{ ok: 1 }
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad3e0ae0fe9919406c01c33'),
'1': ObjectId('6ad3e0ae0fe9919406c01c34'),
'2': ObjectId('6ad3e0ae0fe9919406c01c35'),
'3': ObjectId('6ad3e0ae0fe9919406c01c36'),
'4': ObjectId('6ad3e0ae0fe9919406c01c37'),
'5': ObjectId('6ad3e0ae0fe9919406c01c38')
}
}
[
{
v: 2,
key: { sensor: 1, waktu: 1 },
name: 'sensor_1_waktu_1',
collation: { locale: 'simple' }
}
]Satu index tunggal: { sensor: 1, waktu: 1 } — persis compound index metaField + timeField yang kita pelajari di Section Indexing, tetapi diarahkan ke bucket dan memakai format storage yang dioptimalkan. Index ini melayani dua pola query terpenting: filter seri tertentu (sensor), dan filter seri plus rentang waktu. Sejak MongoDB 6.3 inilah satu-satunya index otomatis; versi lebih awal juga membuat index timeField terpisah, dan query rentang waktu murni kini ditangani pemindaian terkluster di atas bucket yang tersusun waktu.
Yang juga penting untuk dicatat: tidak ada index _id. Tidak ada _id_ di daftar di atas — pencarian berbasis ObjectId acak bukan pola akses time series, jadi server tidak membebani collection dengan index yang tidak terpakai.
Secondary Index pada metaField #
Query aplikasi kita sering menyaring berdasarkan komponen metaField, misalnya semua sensor di satu kota. createIndex() bekerja seperti biasa, cukup arahkan ke subfield-nya:
db.cuaca.createIndex({ "sensor.sensor_id": 1 })
db.cuaca.find({ "sensor.sensor_id": "JKT-01" }).explain().stages[0].$cursor.queryPlanner.winningPlan.inputStageOutput yang Dihasilkan:
sensor.sensor_id_1
{
stage: 'IXSCAN',
nss: 'latihan_timeseries.cuaca',
keyPattern: { 'meta.sensor_id': 1 },
indexName: 'sensor.sensor_id_1',
isMultiKey: false,
multiKeyPaths: { 'meta.sensor_id': [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: { 'meta.sensor_id': [ '["JKT-01", "JKT-01"]' ] }
}Bukti pemetaan internalnya terlihat jelas di keyPattern: kita membuat index pada sensor.sensor_id, server menyimpannya sebagai meta.sensor_id — nama field meta bucket. IXSCAN dengan indexBounds tepat menunjukkan index dipakai, bukan COLLSCAN. Aturan praktis dari Section Indexing tetap berlaku di sini: index field yang benar-benar muncul di filter aplikasi, dan karena equality pada metaField memimpin pola query, letakkan komponen meta paling selektif di depan.
Index pada Data Pengukuran #
Filter “semua pembacaan dengan suhu di atas 27 derajat” menyentuh field measurement. Index pada field seperti suhu juga didukung, dengan trik internal yang menarik:
db.cuaca.createIndex({ suhu: 1 })
db.cuaca.find({ suhu: { $gt: 27 } }).hint({ suhu: 1 }).explain().stages[0].$cursor.queryPlanner.winningPlan.inputStageOutput yang Dihasilkan:
suhu_1
{
stage: 'IXSCAN',
nss: 'latihan_timeseries.cuaca',
keyPattern: { 'control.min.suhu': 1, 'control.max.suhu': 1 },
indexName: 'suhu_1',
isMultiKey: false,
multiKeyPaths: { 'control.min.suhu': [], 'control.max.suhu': [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: {
'control.min.suhu': [ '[MinKey, MaxKey]' ],
'control.max.suhu': [ '[MinKey, MaxKey]' ]
}
}Perhatikan keyPattern-nya: satu index suhu_1 menjadi index gabungan control.min.suhu + control.max.suhu — ringkasan nilai terkecil dan terbesar per bucket dari field control yang kita lihat di Chapter Time Series Collections. Server mencari bucket yang rentang min–max-nya mungkin berisi nilai di atas 27; bucket yang seluruh isinya di bawah 27 terpangkas tanpa dibongkar, lalu pengukuran di dalam bucket kandidat difilter satu per satu. Karena itu index measurement paling efektif saat rentang nilai query lebar atau data tersebar merata antar seri.
Rangkuman #
- MongoDB otomatis membuat satu compound index
metaField + timeField(sejak 6.3); tidak ada index_idpada time series collection. - Secondary index pada subfield
metaFielddipetakan kemeta.*di bucket — pola equality meta tetap menjadi pintu masuk tercepat. - Index pada field measurement menjadi index
control.min/control.max— memangkas bucket berdasarkan rentang nilai sebelum isi bucket dibaca. - Gunakan
explain()dan.hint()untuk memverifikasi pemetaan index; kerangka keputusan index dari Section Indexing tetap berlaku.