MongoDB time series collections store measurements whose meaning depends on time, such as sensor readings, application metrics, prices, or website traffic. MongoDB groups nearby measurements into internal buckets to reduce storage and improve time-based queries.
You work with a time series collection through normal MongoDB queries, but you define its time field and optional metadata when you create it.
Structure of Time Series Data
A useful measurement usually contains three parts:
- Time: the date and time when the measurement occurred.
- Metadata: stable information that identifies the source, such as a sensor ID or server name.
- Metrics: values that change over time, such as temperature, CPU usage, or order count.
Example document:
{
// Required date value used by the collection timeField
timestamp: ISODate("2026-09-03T10:00:00Z"),
// Stable information used to group and filter measurements
metadata: {
sensorId: "IND-101",
city: "Indore"
},
// Values measured at this point in time
temperature: 29.4,
humidity: 61
}
Create a Time Series Collection
Use db.createCollection() with a timeseries configuration. The timeField is required. The metaField and granularity settings are optional but important for efficient storage.
Example:
// Create a collection for readings recorded every few minutes
db.createCollection("weatherReadings", {
timeseries: {
timeField: "timestamp",
metaField: "metadata",
granularity: "minutes"
},
expireAfterSeconds: 2592000 // Keep data for approximately 30 days
})
Output:
{ ok: 1 }
Version note: Time series collections were introduced in MongoDB 5.0. Current releases provide additional indexing, update, sharding, and performance improvements.
Choose the Right Fields
The time field must contain a BSON date. MongoDB uses its values to place measurements into time-ordered buckets.
Choose metadata that changes rarely and commonly appears in filters. Do not place changing measurements or unique event values in the metadata object because high-cardinality metadata can create many small buckets.
| Setting | Good choice | Poor choice |
|---|---|---|
| timeField | Measurement timestamp | Formatted date string |
| metaField | Sensor ID and location | Temperature or random request ID |
| metric | Temperature and humidity | Permanent device identity |
Select Granularity
Granularity tells MongoDB how frequently measurements from one source normally arrive. Choose the closest practical interval.
| Granularity | Approximate maximum bucket span | Typical use |
|---|---|---|
| seconds | 1 hour | Frequent metrics or readings |
| minutes | 24 hours | Readings every few minutes |
| hours | 30 days | Infrequent daily or hourly data |
A setting that is too fine can create many lightly filled buckets. A setting that is too coarse can make a narrow query inspect a wider time range than necessary.
Insert Measurements
Insert measurements as normal documents. Use real BSON dates rather than storing timestamps as strings.
Example:
// Add readings from two sensors
db.weatherReadings.insertMany([
{
timestamp: ISODate("2026-09-03T10:00:00Z"),
metadata: { sensorId: "IND-101", city: "Indore" },
temperature: 29.4,
humidity: 61
},
{
timestamp: ISODate("2026-09-03T10:05:00Z"),
metadata: { sensorId: "IND-101", city: "Indore" },
temperature: 29.8,
humidity: 60
},
{
timestamp: ISODate("2026-09-03T10:05:00Z"),
metadata: { sensorId: "LON-204", city: "London" },
temperature: 18.2,
humidity: 72
}
])
Output:
{ acknowledged: true, insertedIds: { '0': ObjectId(...), '1': ObjectId(...), '2': ObjectId(...) } }
Query a Time Range
Filter by stable metadata and a bounded time range. This pattern lets MongoDB avoid unrelated sources and buckets.
Example:
// Read one sensor's measurements for a 15-minute window
db.weatherReadings.find({
"metadata.sensorId": "IND-101",
timestamp: {
$gte: ISODate("2026-09-03T10:00:00Z"),
$lt: ISODate("2026-09-03T10:15:00Z")
}
}).sort({ timestamp: 1 })
Output:
[
{ timestamp: ISODate("2026-09-03T10:00:00Z"), metadata: { sensorId: "IND-101", city: "Indore" }, temperature: 29.4, humidity: 61 },
{ timestamp: ISODate("2026-09-03T10:05:00Z"), metadata: { sensorId: "IND-101", city: "Indore" }, temperature: 29.8, humidity: 60 }
]
Aggregate Measurements by Time
Use an aggregation pipeline to calculate summaries for fixed intervals. $dateTrunc places timestamps into consistent time windows.
Example:
// Calculate the average temperature for each hour
db.weatherReadings.aggregate([
{
$match: {
"metadata.sensorId": "IND-101"
}
},
{
$group: {
_id: {
$dateTrunc: {
date: "$timestamp",
unit: "hour"
}
},
averageTemperature: { $avg: "$temperature" },
samples: { $sum: 1 }
}
},
{
// Return the earliest hour first
$sort: { _id: 1 }
}
])
Output:
[
{
_id: ISODate("2026-09-03T10:00:00Z"),
averageTemperature: 29.6,
samples: 2
}
]
Expire Old Measurements Automatically
The collection-level expireAfterSeconds option removes measurements after the configured age. Expiration uses the time-field value. Removal runs in the background and is not guaranteed to occur at the exact expiration second because MongoDB removes expired buckets during later cleanup passes.
Example:
// Change retention to seven days
db.runCommand({
collMod: "weatherReadings",
expireAfterSeconds: 604800
})
Indexes and Sharding
MongoDB 6.3 and later automatically create a compound index on the metadata and time fields for new time series collections. Add another index only after checking real query patterns with explain().
For sharded time series collections, use stable metadata as the shard-key basis. Starting in MongoDB 8.0, shard keys that contain the timeField are deprecated.
Important Limitations
- You cannot redefine the time field or metadata field after collection creation.
- Time series updates have restrictions; filters and changes are mainly designed around metadata rather than arbitrary measurement edits.
- Zone sharding is not supported for time series collections.
- TTL cleanup is eventual, so expired measurements may remain briefly.
- A time series collection is unsuitable for data that has no meaningful time order.
Best Practices
- Store timestamps as BSON dates in a consistent time zone, usually UTC.
- Keep metadata stable and use it in common filters.
- Select granularity from the normal interval between measurements from one source.
- Query bounded time ranges instead of scanning the complete collection.
- Set a retention period when old measurements have no business value.
- Test indexes and aggregation pipelines with production-like data volumes.
Conclusion
MongoDB time series collections organize measurements into compressed, time-aware buckets. Define a valid time field, choose stable metadata, match granularity to the arrival rate, and query with both metadata and time bounds. These choices improve storage, retention, and analysis without adding a separate data model.