1

I have a Node / Mongoose / MongoDB project which I use Selenium WebDriver for integration tests. Part of my testcase setup is to wipe the database before each test. I accomplish this via command line using the method that's heavily accepted here Delete everything in a MongoDB database

mongo [database] --eval "db.dropDatabase();"

The problem is however that after running this command, Mongo no longer enforces any unique fields, such as the following as defined by my Mongoose Schema:

  new Mongoose.Schema
    name :
      type : String
      required : true
      unique : true

If I restart mongod after the dropDatabase then the unique constraints are enforced again.

However since my mongod is run by a separate process, restarting it as part of my test case set up would be cumbersome, and I'm hoping unnecessary. Is this a bug or am I missing something?

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
DanH
  • 5,498
  • 4
  • 49
  • 72

2 Answers2

1

As you stated, your mongod (or multiple ones) is a separate process to your application, and as such your application has no knowledge of what has happened when you dropped the database. While the collections and indeed the database will be created when accessed by default, this is not true of all the indexes you defined.

Index creation actually happens on application start-up, as is noted in this passage from the documentation:

When your application starts up, Mongoose automatically calls ensureIndex for each defined index in your schema. ...

It does not automatically happen each time a model is accessed, mostly because that would be excessive overhead, even if the index is in place and the server does nothing, it's still additional calls that you don't want preceeding every "read", "update", "insert" or "delete".

So in order to have this "automatic" behavior "fire again", what you need to do is "restart" your application and this process will be run again on connection. In the same way, when your "application" cannot connect to the mongod process, it re-tries the connection and when reconnected the automatic code is run.

Alternately, you can code a function you could call to do this and expose the method from an API so you could link this function with any other external operation that does something like "drop database":

function reIndexAllModels(callback) {
    console.log("Re-Indexing all models");
    async.eachLimit(
      mongoose.connection.modelNames(),
      10,
      function(name,callback) {
        console.log("Indexing %s", name);
        mongoose.model(name).ensureIndexes(callback);
      },
      function(err) {
        if(err) throw err;
        console.log("Re-Indexing done");
        callback();
      }
    );
}

That will cycle through all registered models and re-create all indexes that are assigned to them. Depending on the size of registered models, you probably don't want to run all of these in parallel connections. So to help a little here, I use async.eachLimit in order to keep the concurrent operations to a manageable number.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • Thanks very much for the explanation and code. I'm trying to stick to a command-line solution however, so I don't need to provide an interface in my Node app for the webdriver to communicate with just for re-indexing. Instead I've switched from `dropDatabase()` to `remove()` on each collection. – DanH Jan 08 '15 at 13:16
0

Thanks to Neil Lunn for pointing out the pitfalls of dropDatabase and re-indexing. Ultimately, I went for the following command line solution, and removing the dropDatabase command.

mongo [database] --eval "db.getCollectionNames().forEach(function(n){db[n].remove()});"
DanH
  • 5,498
  • 4
  • 49
  • 72