← Back to DevBytes

Designing a Real-Time Analytics with MongoDB

Designing a Real-Time Analytics with MongoDB

What Is Real-Time Analytics with MongoDB?

Real-time analytics involves processing and visualizing data as it arrives, with minimal latency. In MongoDB, this means leveraging its native capabilities—such as change streams, aggregation pipelines, and in-memory processing—to continuously ingest, transform, and query data without batch delays. Unlike traditional ETL-based systems that refresh dashboards every few minutes or hours, a real-time analytics system built on MongoDB can update metrics, aggregations, and alerts within seconds of an event occurring.

Why It Matters

How to Use It: A Practical Tutorial

1. Core Components for Real-Time Analytics

To design a real-time analytics pipeline, you need three building blocks:

2. Setting Up a Sample Collection

Let’s model a system that tracks page views on a website. Each event is a document inserted into an events collection:

{
  "_id": ObjectId("..."),
  "event": "page_view",
  "userId": "u123",
  "page": "/product/abc",
  "timestamp": ISODate("2025-03-20T10:00:00Z"),
  "duration": 45
}

Create the collection and index on timestamp for efficient aggregation:

db.createCollection("events")
db.events.createIndex({ timestamp: -1 })

3. Listening to Changes with Change Streams

We need a background process (e.g., a Node.js or Python worker) that listens to new inserts and updates an analytics collection. Below is a Node.js example using the MongoDB driver:

const { MongoClient } = require('mongodb');

async function startChangeStream() {
  const client = await MongoClient.connect('mongodb://localhost:27017');
  const db = client.db('analytics');
  const eventsCollection = db.collection('events');
  const changeStream = eventsCollection.watch([
    { $match: { 'operationType': 'insert' } }
  ]);

  changeStream.on('change', async (change) => {
    const newEvent = change.fullDocument;
    // Immediately update an hourly aggregation
    await updateHourlyStats(db, newEvent);
  });
}

async function updateHourlyStats(db, event) {
  const hourBucket = new Date(event.timestamp);
  hourBucket.setMinutes(0, 0, 0); // round down to hour

  const collection = db.collection('page_views_hourly');
  const update = {
    $inc: { count: 1 },
    $setOnInsert: { hour: hourBucket }
  };
  await collection.updateOne(
    { hour: hourBucket },
    update,
    { upsert: true }
  );
}

startChangeStream();

This worker updates a page_views_hourly collection in real time. For higher throughput, use bulk operations or batch changes.

4. Aggregating Data on Demand

Sometimes you need complex aggregations that are not precomputed. Use MongoDB’s aggregation pipeline with $match on the latest timestamp range. Example: get top 10 pages in the last hour:

db.events.aggregate([
  { $match: { timestamp: { $gte: new Date(Date.now() - 60*60*1000) } } },
  { $group: { _id: "$page", views: { $sum: 1 } } },
  { $sort: { views: -1 } },
  { $limit: 10 }
])

For sub-second response times on high-frequency data, consider materialized views (see section 5).

5. Building Materialized Views with $merge

A materialized view stores precomputed aggregates and updates them periodically or via change streams. Use the $merge stage to write pipeline results into a collection:

db.events.aggregate([
  { $match: { timestamp: { $gte: new Date(Date.now() - 3600*1000) } } },
  { $group: {
      _id: { page: "$page", hour: { $dateTrunc: { date: "$timestamp", unit: "hour" } } },
      views: { $sum: 1 },
      avgDuration: { $avg: "$duration" }
  } },
  { $merge: {
      into: "page_views_materialized",
      on: ["_id"],
      whenMatched: "replace",
      whenNotMatched: "insert"
  } }
])

Run this aggregation on a schedule (e.g., every minute) or trigger it from the change stream for near-real-time updates.

6. Using TTL Indexes for Data Expiry

Real-time analytics often deals with transient data. Use a TTL index to automatically delete raw events after a retention period:

db.events.createIndex(
  { timestamp: 1 },
  { expireAfterSeconds: 86400 } // delete after 24 hours
)

This keeps storage manageable without manual cleanup.

Best Practices for Real-Time Analytics with MongoDB

Complete Example: Real-Time Dashboard Backend

Here is a compact Node.js snippet that ties everything together—listening to change streams, maintaining a materialized view of hourly stats, and exposing an endpoint for the dashboard:

const express = require('express');
const { MongoClient } = require('mongodb');
const app = express();

let db;
let materializedCollection;

MongoClient.connect('mongodb://localhost:27017')
  .then(client => {
    db = client.db('analytics');
    materializedCollection = db.collection('page_views_hourly');
    startChangeStream();
  });

async function startChangeStream() {
  const events = db.collection('events');
  const pipeline = [
    { $match: { 'operationType': 'insert' } }
  ];
  const stream = events.watch(pipeline);

  stream.on('change', async (change) => {
    const doc = change.fullDocument;
    const hour = new Date(doc.timestamp);
    hour.setMinutes(0, 0, 0);
    await materializedCollection.updateOne(
      { hour },
      { $inc: { count: 1, totalDuration: doc.duration || 0 } },
      { upsert: true }
    );
  });
}

app.get('/api/stats/latest', async (req, res) => {
  const stats = await materializedCollection
    .find()
    .sort({ hour: -1 })
    .limit(24)
    .toArray();
  res.json(stats);
});

app.listen(3000, () => console.log('Dashboard API running'));

This example updates the materialized collection on every insert and serves the last 24 hourly buckets via a REST endpoint.

Conclusion

Designing real-time analytics with MongoDB is achievable using its built-in features: change streams for event-driven updates, aggregation pipelines for flexible transformations, and materialized views for precomputed metrics. By following best practices such as pre-aggregating at insert time, using TTL indexes for data retention, and routing read queries to secondaries, you can build a system that provides sub-second insights without adding heavy infrastructure. Start small with a single worker and scale horizontally by sharding your collections and distributing change stream listeners. With these patterns, MongoDB becomes a powerful platform for operational and analytical workloads alike.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles