Navigation

Watch for Document Changes

Overview

Stitch 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.

Stitch 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. Stitch supports watching any change on a collection and watching changes that match a certain filter.

Change Stream Limitations

Stitch 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 Stitch apps based on the cluster’s size. See change stream limitations for more information.

Setup

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

1

Import Stitch Dependencies

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

– or –

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

Instantiate a MongoDB Remote Collection Handle

const stitchApp = Stitch.initializeDefaultAppClient("<Your App ID>");
const mongodb = stitchApp.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
const itemsCollection = mongodb.db("store").collection("items");
const purchasesCollection = mongodb.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 Set up a MongoDB Mobile Project guide.

Note

For more details on setting up your Android app to use Stitch, refer to Build a Mobile App with Sync or Build a Local-Only Mobile App.

2

Import Stitch Dependencies

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

// Base Stitch Packages
import com.mongodb.stitch.android.core.Stitch;
import com.mongodb.stitch.android.core.StitchAppClient;
// Stitch Authentication Packages
import com.mongodb.stitch.android.core.auth.StitchUser;
import com.mongodb.stitch.core.auth.providers.anonymous.AnonymousCredential;
// MongoDB Service Packages
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
// Utility Packages
import com.mongodb.stitch.core.internal.common.BsonUtils;

To Sync documents between a remote and a local MongoDB instance, you will also need to import the following packages:

// Stitch Sync Packages
import com.mongodb.stitch.core.services.mongodb.remote.sync.ChangeEventListener;
import com.mongodb.stitch.core.services.mongodb.remote.sync.DefaultSyncConflictResolvers;
import com.mongodb.stitch.core.services.mongodb.remote.sync.ErrorListener;
import com.mongodb.stitch.core.services.mongodb.remote.sync.internal.ChangeEvent;

Important

If you use Sync, be sure to add .sync() before every remote database call in the examples below, as in the following:

itemsCollections.sync().find()

To perform CRUD operations only on the local (on-device) database, import the following packages:

// MongoDB Mobile Local Database Packages
import com.mongodb.stitch.android.services.mongodb.local.LocalMongoDbService;
3

Instantiate a MongoDB Collection Handle

Top of Activity File
private StitchAppClient stitchClient;
private RemoteMongoClient mongoClient;
private RemoteMongoCollection itemsCollection;
In Activity.onCreate()
stitchClient = Stitch.getDefaultAppClient();
mongoClient = stitchClient.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
itemsCollection = mongoClient.getDatabase("store").getCollection("items");
purchasesCollection = mongoClient.getDatabase("store").getCollection("purchases");

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 Set up a MongoDB Mobile Project guide.

2

Import Stitch Dependencies

In scope (e.g. UIViewController)
import StitchCore
import StitchCoreRemoteMongoDBService
import StitchRemoteMongoDBService
3

Initialize the MongoDB Stitch iOS SDK

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

Instantiate a MongoDB Collection Handle

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

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

Watch Snippets

Watch a Collection

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);
   });
}

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 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())
   }
}

Watch a Collection with a Filter

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",
   });
}

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 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()
      )
   }
}