MongoDB watch() to observe change in Database with NodeJS and Mongoose

12,994

Solution 1

Change streams in MongoDB requires a replica set to function.

According to Mongoose docs:

To connect to a replica set you pass a comma delimited list of hosts to connect to rather than a single host.

mongoose.connect('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]' [, options]);

Full example

const { ReplSet } = require('mongodb-topology-manager');
const mongoose = require('mongoose');

run().catch(error => console.error(error));

async function run() {
  // Make sure you're using mongoose >= 5.0.0
  console.log(new Date(), `mongoose version: ${mongoose.version}`);

  await setupReplicaSet();

  // Connect to the replica set
  const uri = 'mongodb://localhost:31000,localhost:31001,localhost:31002/' +
    'test?replicaSet=rs0';
  await mongoose.connect(uri);
  // For this example, need to explicitly create a collection, otherwise
  // you get "MongoError: cannot open $changeStream for non-existent database: test"
  await mongoose.connection.createCollection('Person');

  // Create a new mongoose model
  const personSchema = new mongoose.Schema({
    name: String
  });
  const Person = mongoose.model('Person', personSchema, 'Person');

  // Create a change stream. The 'change' event gets emitted when there's a
  // change in the database
  Person.watch().
    on('change', data => console.log(new Date(), data));

  // Insert a doc, will trigger the change stream handler above
  console.log(new Date(), 'Inserting doc');
  await Person.create({ name: 'Axl Rose' });
  console.log(new Date(), 'Inserted doc');
}

// Boilerplate to start a new replica set. You can skip this if you already
// have a replica set running locally or in MongoDB Atlas.
async function setupReplicaSet() {
  const bind_ip = 'localhost';
  // Starts a 3-node replica set on ports 31000, 31001, 31002, replica set
  // name is "rs0".
  const replSet = new ReplSet('mongod', [
    { options: { port: 31000, dbpath: `${__dirname}/data/db/31000`, bind_ip } },
    { options: { port: 31001, dbpath: `${__dirname}/data/db/31001`, bind_ip } },
    { options: { port: 31002, dbpath: `${__dirname}/data/db/31002`, bind_ip } }
  ], { replSet: 'rs0' });

  // Initialize the replica set
  await replSet.purge();
  await replSet.start();
  console.log(new Date(), 'Replica set started...');
}

Full example excerpted from https://thecodebarbarian.com/stock-price-notifications-with-mongoose-and-mongodb-change-streams

Solution 2

You can’t, change stream cursor is not available on system collections, or any collections in the admin, local, and config databases. You could try configuring your database structure to not be an admin dB.

Mongodb changeStreams doc

Share:
12,994
TheTechGuy
Author by

TheTechGuy

Updated on June 17, 2022

Comments

  • TheTechGuy
    TheTechGuy almost 2 years

    I am trying to watch my mongodb. whenever a change occurs I want to apply an action. This is what I have tried

    var mongoose = require('mongoose');
    //mongoose.connect('mongodb://localhost/test');
    mongoose.Promise = global.Promise
    mongoose.connect('mongodb://localhost:27017')
    mongoose.connection.createCollection('people');
    const Person = mongoose.model('Person', new mongoose.Schema({ name: String }));
    Person.watch().
        on('change', data => console.log(new Date(), data));
    console.log(new Date(), 'Inserting doc');
    Person.create({ name: 'john doe' });
    
    console.log(new Date(), 'Inserted doc');
    

    But I am getting the following error

    node_modules/mongodb/lib/utils.js:132 throw err; ^

    MongoError: $changeStream may not be opened on the internal admin database

    How can I fix this ?