Navigation

Watch for Document Changes

Overview

MongoDB Realm allows you to watch a collection and receive client-side database events whenever documents in the collection are inserted or modified. This enables you to develop real-time and collaborative applications without the overhead and complexity of alternatives such as long polling.

Realm uses MongoDB change streams on watched collections to listen for changes and broadcast them to any client applications that are watching documents in the collection at the time of the event. Realm supports watching any change on a collection and watching changes that match a certain filter.

Change Stream Limitations

Realm opens a single MongoDB change stream for each watched collection and limits the total number of open change streams on each linked cluster across all Realm apps based on the cluster’s size. See change stream limitations for more information.

Setup

To use the following code snippets in an iOS project, you must first do the following:

1

Set Up Your Project

Follow the steps in the Install Realm for iOS, macOS, tvOS, and watchOS guide.

2

Import Realm Dependencies

In scope (e.g. UIViewController)
import RealmCore
import RealmCoreRemoteMongoDBService
import RealmRemoteMongoDBService
3

Initialize the MongoDB Realm iOS SDK

Application Startup (e.g. AppDelegate.didFinishLaunchingWithOptions())
do {
    let _ = try Realm.initializeDefaultAppClient(
        withClientAppID: "YOUR-APP-ID"
    )
} catch {
    print("Failed to initialize MongoDB Realm iOS SDK: \(error)")
}
4

Instantiate a MongoDB Collection Handle

In scope (e.g. UIViewController)
// Variables in scope:
private lazy var realmClient = Realm.defaultAppClient!
private var mongoClient: RemoteMongoClient?
private var itemsCollection: RemoteMongoCollection<Document>?

// Set the realm variables declared above in viewDidLoad()
mongoClient = realmClient.serviceClient(
    fromFactory: remoteMongoClientFactory,
    withName: "mongodb-atlas"
)
itemsCollection = mongoClient?.db("store").collection("items")
purchasesCollection = mongoClient?.db("store").collection("purchases")

To use the following code snippets in an Android project, you must first do the following:

1

Set Up Your Project

Follow the steps in the Install Realm for Android guide.

2

Import Realm Dependencies

For CRUD operations on a remote MongoDB collection, you will use one or more of the following import statements:

// Base Realm Packages
import io.realm.Realm
import io.realm.mongodb.App
import io.realm.mongodb.AppConfiguration
// Realm Authentication Packages
import io.realm.mongodb.User
import io.realm.mongodb.Credentials;

// MongoDB Service Packages
import io.realm.mongodb.mongo.MongoClient
import io.realm.mongodb.mongo.MongoDatabase
import io.realm.mongodb.mongo.MongoCollection
// Utility Packages
import org.bson.Document
3

Instantiate a MongoDB Collection Handle

Top of Activity File
private App realmApp;
private MongoClient mongoClient;
private MongoCollection itemsCollection;
private User user;
In Activity.onCreate()
String appID = "<your app ID>"; // replace this with your App ID
Realm.init(this); // initialize Realm, required before interacting with SDK
App app = new App(new AppConfiguration.Builder(appID)
           .build());

// an authenticated user is required to access a MongoDB instance
Credentials credentials = Credentials.anonymous();
app.loginAsync(credentials, it -> {
    if (it.isSuccess()) {
        user = app.currentUser();
        MongoClient mongoClient = user.getMongoClient("<atlas service name>");
        if (mongoClient != null) {
           itemsCollection = mongoClient.getDatabase("<database name>").getCollection("<collection name>");
        } else {
           Log.e(TAG, "Error connecting to the MongoDB instance.");
        }
    else {
       Log.e(TAG, "Error logging into the Realm app. Is anonymous authentication enabled?");
    }
});

To use the following code snippets in a JavaScript project, you must first do the following:

1

Import Realm Dependencies

Import from CDN
<!-- Import the Realm JS SDK at the top of the file -->
<script src="https://s3.amazonaws.com/realm-sdks/js/bundles/4/realm.js"></script>
<script>
  // Destructure Realm JS SDK Components
  const { Realm, RemoteMongoClient, BSON } = realm;
</script>

– or –

Import from Module
// Import components of the Realm JS SDK at the top of the file
import {
  Realm,
  RemoteMongoClient,
  BSON
} from "mongodb-realm-browser-sdk";
2

Instantiate a MongoDB Remote Collection Handle

const realmApp = Realm.initializeDefaultAppClient("<Your App ID>");
const mongodb = realmApp.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");

const itemsCollection = mongodb.db("store").collection("items");
const purchasesCollection = mongodb.db("store").collection("purchases");

Watch Snippets

Watch a Collection

You can open a stream of changes made to a collection by calling RemoteMongoCollection.watch(delegate:). Whenever the watched collection changes, the passed ChangeStreamDelegate will receive the associated ChangeEvent.

class MyCustomDelegate<T>: ChangeStreamDelegate
  where T: Encodable, T: Decodable
{
    typealias DocumentT = T

    func didReceive(event: ChangeEvent<T>) {
        // react to events
    }
    // ...other method implementations
}

class Watcher {
   var changeStreamSession: ChangeStreamSession<Document>?

   func watch(collection: RemoteMongoCollection<Document>) throws {
      // Watch the collection for any changes. As long as the changeStreamSession
      // is alive, it will continue to send events to the delegate.
      changeStreamSession = try collection.watch(delegate: MyCustomDelegate<Document>.init())
   }
}

You can open a stream of changes made to a collection by calling RemoteMongoCollection.watch(). Whenever the watched collection changes, the AsyncChangeStream will emit an event.

itemsCollection.watch()
        .addOnCompleteListener(task -> {
            AsyncChangeStream<Document, ChangeEvent<Document>> changeStream = task.getResult();
            changeStream.addChangeEventListener((BsonValue documentId, ChangeEvent<Document> event) -> {
                // handle change event
            });
        });

You can open a stream of changes made to a collection by calling collection.watch() with no argument. Whenever the watched collection changes, the stream’s event handler fires with a ChangeEvent object as its only argument.

async function watcher() {
   // Create a change stream that watches the collection
   const stream = await myCollection.watch();
   // Set up a change event handler function for the stream
   stream.onNext((event) => {
     // Handle the change events for all specified documents here
     console.log(event.fullDocument);
   });
}

Watch a Collection with a Filter

You can open a stream of changes that match a filter by calling RemoteMongoCollection.watch(matchFilter:delegate:) with a $match expression as the matchFilter argument. Whenever the watched collection changes and the change event matches the provided $match expression, the passed ChangeStreamDelegate will receive the ChangeEvent.

The $match expression is similar to the $match expression passed when configuring a database trigger.

class MyCustomDelegate<T>: ChangeStreamDelegate
  where T: Encodable, T: Decodable
{
   typealias DocumentT = T

   func didReceive(event: ChangeEvent<T>) {
      // react to events
   }
   // ...other method implementations
}

class Watcher {
   var changeStreamSession: ChangeStreamSession<Document>?

   func watchWithFilter(collection: RemoteMongoCollection<Document>) throws {
      // Watch the collection for any changes that match the given filter.
      // As long as the changeStreamSession is alive, it will continue
      // to send events to the delegate.
      changeStreamSession = try collection.watch(
          matchFilter: ["fullDocument.checked": true] as Document,
          delegate: MyCustomDelegate<Document>.init()
      )
   }
}

You can open a stream of changes that match a filter by calling RemoteMongoCollection.watchWithFilter() with a $match expression as the matchFilter argument. Whenever the watched collection changes and the change event matches the provided $match expression, the AsyncChangeStream will emit an event.

The $match expression is similar to the $match expression passed when configuring a database trigger.

// Watch for events that match the given certain filter
itemsCollection.watchWithFilter(new BsonDocument("fullDocument.checked", new BsonBoolean(true)))
        .addOnCompleteListener(task -> {
            AsyncChangeStream<Document, ChangeEvent<Document>> changeStream = task.getResult();
            changeStream.addChangeEventListener((BsonValue documentId, ChangeEvent<Document> event) -> {
                // handle change event
            });
        });

You can open a stream of changes that match a filter by calling collection.watch(delegate:) with a $match expression as the argument. Whenever the watched collection changes and the ChangeEvent matches the provided $match expression, the stream’s event handler fires with the ChangeEvent object as its only argument.

The $match expression is similar to the $match expression passed when configuring a database trigger.

async function watcher() {
   // Create a change stream that watches the collection
   // for when any document's 'status' field is changed
   // to the string 'blocked'.
   const stream = await myCollection.watch({
      "updateDescription.updatedFields": {
         "status": "blocked",
      }
   });
   // Set up a change event handler function for the stream
   stream.onNext((event) => {
     // Handle the change events for all specified documents here
     console.log(event.fullDocument);
   });

   // Insert a document with status set to 'blocked'
   // to trigger the stream onNext handler
   await myCollection.insertOne({
      "name": "test",
      "status": "blocked",
   });
}