Manage High-Frequency Sensor Data with MongoDB Time Series Collections
High-frequency sensor data can bloat regular MongoDB collections. Time series collections automate bucketing, compression, and expiration—if you declare them at creation time.
02 Sept 2025, 23:50 UTC

The problem: regular collections are expensive for sensor streams
Imagine a fleet of sensors posting a JSON document every second. Each document holds a timestamp, a sensor identifier, and a reading. In a regular MongoDB collection, every document is stored and indexed individually. After 30 days you have millions of documents, large indexes, and queries that scan more data than necessary. You can add a TTL index to delete old documents, but you still pay for storage and index maintenance on the hot data, and you must manually bucket time ranges for dashboards.
What a time series collection does differently
A time series collection is a native MongoDB collection type, available since MongoDB 4.2, optimized for sequences of time-stamped events. Instead of treating each document as an isolated row, MongoDB groups incoming points into internal buckets based on the time field you declare. It applies compression suited to repetitive time-series values and can expire old buckets automatically using TTL (time-to-live) through the expireAfterSeconds option.
Three options matter at creation:
timeField: the field holding the event time, such astimestamp.metaField: an optional field with metadata that rarely changes, such as a sensor ID or location.granularity: the expected ingest frequency—seconds,minutes, orhours—which helps MongoDB choose bucket sizing.
Regular collection vs. time series collection
| Concern | Regular collection | Time series collection |
|---|---|---|
| Declaration | Created implicitly on first insert | Must be declared explicitly with timeseries options |
| Old data removal | Manual TTL index or application deletion | Automatic via expireAfterSeconds on the time field |
| Storage layout | Document-oriented | Time-bucketed and compressed internally |
| Conversion | N/A | Cannot convert an existing regular collection |
Worked example: sensor readings with 30-day retention
Run these commands in mongosh against the target database. You need createCollection privileges and, for inserts, insert privileges.
db.createCollection("sensor_readings", {
timeseries: {
timeField: "timestamp",
metaField: "metadata",
granularity: "seconds"
},
expireAfterSeconds: 2592000
});
2592000 seconds is 30 days. MongoDB removes buckets older than that window automatically. Insert a sample reading:
db.sensor_readings.insertOne({
timestamp: new Date("2026-06-01T12:00:00Z"),
metadata: { sensorId: "temp-01", unit: "celsius" },
value: 22.5
});
Query a range. Because timestamp is the declared time field, range filters use the time-series storage layout:
db.sensor_readings.find({
timestamp: {
$gte: new Date("2026-06-01T00:00:00Z"),
$lt: new Date("2026-06-02T00:00:00Z")
}
});
Aggregate five-minute averages. Replace the collection name and field names with your own schema:
db.sensor_readings.aggregate([
{
$match: {
timestamp: { $gte: new Date("2026-06-01T00:00:00Z") }
}
},
{
$group: {
_id: {
$dateToString: { format: "%Y-%m-%dT%H:%M", date: "$timestamp" }
},
averageValue: { $avg: "$value" },
readingCount: { $sum: 1 }
}
},
{ $sort: { _id: 1 } }
]);
Verify the collection type and retention behavior
Confirm the server supports time series collections by checking the version in mongosh:
db.version()
Expect a version string of 4.2 or later. Then inspect the collection:
db.runCommand({ listCollections: 1, filter: { name: "sensor_readings" } })
Look for a timeseries block in the output containing your timeField, metaField, and granularity settings. To test expiration without waiting 30 days, create a throwaway collection with a short expireAfterSeconds value, insert a document with a past timestamp, and confirm it disappears after the TTL background task runs. Test this on a non-production database first, because TTL deletion is irreversible.
Trade-offs and limits
- No conversion. You cannot change a regular collection into a time series collection. Decide at design time, or plan to export data, drop the collection, and recreate it.
- TTL is tied to the time field. Expiration applies only to the declared
timeField. If you need different retention for different sensors, handle that in the application or with separate collections. - Ingest ceiling. Very high write rates can reach shard-level limits in clustered deployments. Load-test your expected writes per second before committing to the architecture.
- Immutable options. After creation, you cannot alter
timeseriessettings orexpireAfterSecondswithout dropping and recreating the collection. - Monitor growth. Watch collection size and bucket growth after deployment, especially if
expireAfterSecondsis misconfigured.
Closing checklist
- Confirm your MongoDB version is 4.2 or newer.
- Choose the
timeFieldand optionalmetaFieldbased on your sensor schema. - Set
granularityto match your average ingest frequency. - Set
expireAfterSecondsto your retention window and test TTL on a scratch database. - Run the
listCollectionsinspection command to verify the collection type before loading production data.
Time series collections remove much of the manual storage and indexing work for high-frequency sensor data, but they reward upfront planning. If your workload is append-heavy, time-centered, and has a clear retention window, they are worth the design constraint.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.