Fork me on GitHub

Documents in ES6

Asynchronous document functions return promises, and so are compatible with the ES6 yield keyword and libraries like co.

Note that the yield keyword is currently only supported in NodeJS 0.11.x with the --harmony flag.

validate() integrates with co and the yield keyword


    co(function*() {
      var schema = null;
      var called = false;
      var shouldSucceed = true;
      var error;

      var validate = {
        validator: function() {
          called = true;
          return shouldSucceed;
        },
        message: 'BAM'
      };

      schema = new Schema({
        eggs: {type: String, required: true, validate: validate},
        bacon: {type: Boolean, required: true}
      });

      var M = db.model('validateSchema', schema, getCollectionName());
      var m = new M({eggs: 'Sunny side up', bacon: false});

      try {
        yield m.validate();
      } catch (e) {
        error = e;
      }

      assert.ok(!error);
      assert.equal(called, true);
      called = false;

      // The validator function above should now fail
      shouldSucceed = false;
      try {
        yield m.validate();
      } catch (e) {
        error = e;
      }

      assert.ok(error);
      assert.ok(error instanceof ValidationError);

      done();
    })();
  

save() integrates with co and the yield keyword


    co(function*() {
      var error;
      var schema = new Schema({
        description: {type: String, required: true}
      });

      var Breakfast = db.model('breakfast', schema, getCollectionName());

      var goodBreakfast = new Breakfast({description: 'eggs & bacon'});

      try {
        yield goodBreakfast.save();
      } catch (e) {
        error = e;
      }

      assert.ifError(error);
      var result;
      try {
        result = yield Breakfast.findOne().exec();
      } catch (e) {
        error = e;
      }
      assert.ifError(error);
      assert.equal(result.description, 'eggs & bacon');

      // Should cause a validation error because `description` is required
      var badBreakfast = new Breakfast({});
      try {
        yield badBreakfast.save();
      } catch (e) {
        error = e;
      }

      assert.ok(error);
      assert.ok(error instanceof ValidationError);

      done();
    })();
  

populate() requires execPopulate() to work with the yield keyword


    /**
     *  Because the `populate()` function supports chaining, it's difficult
     *  to determine when the chain is 'done'. Therefore, you need to call
     *  `execPopulate()` to use `populate()` with `yield`.
     */
    co(function*() {
      var error;
      var breakfastCollectionName = getCollectionName();
      var foodCollectionName = getCollectionName();
      var breakfastSchema = new Schema({
        foods: [{type: mongoose.Schema.ObjectId, ref: foodCollectionName}]
      });

      var foodSchema = new Schema({
        name: String
      });

      var Food = db.model(foodCollectionName, foodSchema, foodCollectionName);
      var Breakfast = db.model(breakfastCollectionName, breakfastSchema, breakfastCollectionName);

      var bacon = new Food({name: 'bacon'});
      var eggs = new Food({name: 'eggs'});
      var goodBreakfast = new Breakfast({foods: [bacon, eggs]});

      try {
        yield [bacon.save(), eggs.save(), goodBreakfast.save()];
      } catch (e) {
        error = e;
      }

      var result;
      try {
        result = yield Breakfast.findOne().exec();
      } catch (e) {
        error = e;
      }
      assert.ifError(error);
      assert.equal(result.foods.length, 2);

      try {
        result = yield result.populate('foods').execPopulate();
      } catch (e) {
        error = e;
      }
      assert.ifError(error);
      assert.equal(result.foods.length, 2);
      assert.equal(result.foods[0].name, 'bacon');
      assert.equal(result.foods[1].name, 'eggs');

      done();
    })();
  

update() works with co and yield


    co(function*() {
      var schema = new Schema({
        steak: String,
        eggs: String
      });

      var Breakfast = db.model('breakfast', schema, getCollectionName());

      var breakfast = new Breakfast({});
      var error;

      try {
        yield breakfast.update({steak: 'Ribeye', eggs: 'Scrambled'}, {upsert: true}).exec();
      } catch (e) {
        error = e;
      }

      assert.ifError(error);
      var result;
      try {
        result = yield Breakfast.findOne().exec();
      } catch (e) {
        error = e;
      }
      assert.ifError(error);
      assert.equal(breakfast._id.toString(), result._id.toString());
      assert.equal(result.steak, 'Ribeye');
      assert.equal(result.eggs, 'Scrambled');
      done();
    })();
  

Queries in ES6

Mongoose queries' .exec() function returns a promise, and so its compatible with the ES6 yield keyword and libraries like co.

Note that the yield keyword is currently only supported in NodeJS 0.11.x with the --harmony flag.

exec() integrates with co and the yield keyword


    co(function*() {
      var schema = new Schema({
        eggs: {type: Number, required: true},
        bacon: {type: Number, required: true}
      });

      var Breakfast = db.model('BreakfastHarmony', schema, getCollectionName());

      try {
        yield Breakfast.create(
          {eggs: 4, bacon: 2},
          {eggs: 3, bacon: 3},
          {eggs: 2, bacon: 4});
      } catch (e) {
        return done(e);
      }

      var result;
      try {
        result = yield Breakfast.findOne({eggs: 4}).exec();
      } catch (e) {
        return done(e);
      }

      assert.equal(result.bacon, 2);

      var results;
      try {
        results = yield Breakfast.find({eggs: {$gt: 2}}).sort({bacon: 1}).exec();
      } catch (e) {
        return done(e);
      }

      assert.equal(results.length, 2);
      assert.equal(results[0].bacon, 2);
      assert.equal(results[1].bacon, 3);

      var count;
      try {
        count = yield Breakfast.count({eggs: {$gt: 2}}).exec();
      } catch (e) {
        return done(e);
      }

      assert.equal(count, 2);

      done();
    })();
  

can call populate() with exec()


    co(function*() {
      var bookSchema = new Schema({
        author: {type: mongoose.Schema.ObjectId, ref: 'AuthorHarmony'},
        title: String
      });

      var authorSchema = new Schema({
        name: String
      });

      var Book = db.model('BookHarmony', bookSchema, getCollectionName());
      var Author = db.model('AuthorHarmony', authorSchema, getCollectionName());

      try {
        var hugo = yield Author.create({name: 'Victor Hugo'});
        yield Book.create({author: hugo._id, title: 'Les Miserables'});
      } catch (e) {
        return done(e);
      }

      var result;
      try {
        result = yield Book.findOne({title: 'Les Miserables'}).populate('author').exec();
      } catch (e) {
        return done(e);
      }

      assert.equal(result.author.name, 'Victor Hugo');

      done();
    })();
  

Models in ES6

Asynchronous functions on Model return promises, and so are compatible with the ES6 yield keyword and libraries like co.

Note that the functions find(), findOne(), findById(), count(), findOneAndUpdate(), remove(), distinct(), findByIdAndUpdate(), findOneAndRemove(), update(), and findByIdAndRemove() return Query objects, and so you need to use .exec() to use these functions with yield as described above.

create() integrates with co and the yield keyword


    co(function * () {
      var schema = new Schema({
        eggs: {type: String, required: true},
        bacon: {type: Boolean, required: true}
      });

      var M = db.model('harmonyCreate', schema, getCollectionName());

      var results;
      try {
        results = yield M.create([
          {eggs: 'sunny-side up', bacon: false},
          {eggs: 'scrambled', bacon: true}]);
      } catch (e) {
        return done(e);
      }

      assert.equal(results.length, 2);
      assert.equal(results[0].eggs, 'sunny-side up');
      assert.equal(results[1].eggs, 'scrambled');

      done();
    })();
  

aggregate() integrates with co and the yield keyword


    co(function*() {
      var schema = new Schema({
        eggs: {type: String, required: true},
        bacon: {type: Boolean, required: true}
      });

      var M = db.model('harmonyAggregate', schema, getCollectionName());

      try {
        yield M.create([
          {eggs: 'sunny-side up', bacon: false},
          {eggs: 'scrambled', bacon: true}]);
      } catch (e) {
        return done(e);
      }

      var results;
      try {
        results = yield M.aggregate([
          {$group: {_id: '$bacon', eggs: {$first: '$eggs'}}},
          {$sort: {_id: 1}}
        ]).exec();
      } catch (e) {
        return done(e);
      }

      assert.equal(results.length, 2);
      assert.equal(results[0]._id, false);
      assert.equal(results[0].eggs, 'sunny-side up');
      assert.equal(results[1]._id, true);
      assert.equal(results[1].eggs, 'scrambled');

      done();
    })();
  

mapReduce() can also be used with co and yield


    co(function*() {
      var schema = new Schema({
        eggs: {type: String, required: true},
        bacon: {type: Boolean, required: true}
      });

      var M = db.model('harmonyMapreduce', schema, getCollectionName());

      try {
        yield M.create([
          {eggs: 'sunny-side up', bacon: false},
          {eggs: 'sunny-side up', bacon: true},
          {eggs: 'scrambled', bacon: true}]);
      } catch (e) {
        return done(e);
      }

      var results;
      try {
        results = yield M.mapReduce({
          map: function() { emit(this.eggs, 1); },
          reduce: function(k, vals) { return vals.length; }
        });
      } catch (e) {
        return done(e);
      }

      assert.equal(results.length, 2);
      assert.ok(results[0]._id === 'sunny-side up' || results[1]._id === 'sunny-side up');
      assert.ok(results[0]._id === 'scrambled' || results[1]._id === 'scrambled');

      done();
    })();