Mongoose ODM

NodeJS #5

Mongoose

  • Schema-based solution to model,
  • Validation,
  • Query building,
  • Hooks, ...
npm install --save mongoose

Installation locale

Un ODM fait le lien entre une base de données orientée Documents et des Object models

Mongoose

// index.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

Initialiser la connexion

var db = mongoose.connection;
db.on('error', console.log);
db.once('open', function() {
  // we're connected!
});

Let's go !

Mongoose

connect()
  .on('error', console.log)
  .on('disconnected', connect)
  .once('open', listen);

function listen () {
  app.listen(port);
  console.log('Express app started on port ' + port);
}

function connect () {
  var options = { server: { socketOptions: { keepAlive: 1 } } };
  return mongoose.connect(config.db, options).connection;
}

Start Express when Mongoose is ready ?!

Models

Schemas

const UserSchema = new mongoose.Schema({
  name: { type: String, default: '' },
  email: { type: String, default: '' },
  username: { type: String, default: '' },
  hashed_password: { type: String, default: '' },
  salt: { type: String, default: '' },
  authToken: { type: String, default: '' },
  createdAt: { type: Date, default: Date.now }
});

mongoose.model('User', UserSchema);

Types : String, Number, Date, Buffer, Boolean, Mixed, ObjectId, Array

Models

Schemas (w/ subdocuments)

const getTags = tags => tags.join(',');
const setTags = tags => tags.split(',');

const ArticleSchema = new Schema({
  title: { type : String, default : '', trim : true },
  body: { type : String, default : '', trim : true },
  user: { type : Schema.ObjectId, ref : 'User' },
  comments: [{
    body: { type : String, default : '' },
    user: { type : Schema.ObjectId, ref : 'User' },
    createdAt: { type : Date, default : Date.now }
  }],
  tags: { type: [], get: getTags, set: setTags },
  image: {
    cdnUri: String,
    files: []
  },
  createdAt  : { type : Date, default : Date.now }
});

Models

Validation built-in

 var breakfastSchema = new Schema({
  eggs: {
    type: Number,
    min: [6, 'Too few eggs'],
    max: 12
  },
  bacon: {
    type: Number,
    required: [true, 'Why no bacon?']
  },
  drink: {
    type: String,
    enum: ['Coffee', 'Tea']
  }
});

Models

Schemas (validation simple)

UserSchema.path('name').validate(function (name) {
  return name.length;
}, 'Name cannot be blank');

Mongoose

Schemas (validation async)

UserSchema.path('email').validate(function (email, fn) {
  const User = mongoose.model('User');

  // Check only when it is a new user or when email field is modified
  if (this.isNew || this.isModified('email')) {
    User.find({ email: email }).exec(function (err, users) {
      fn(!err && users.length === 0);
    });
  } else fn(true);
}, 'Email already exists');

Mongoose

Custom instance methods

// define a schema
var animalSchema = new Schema({ name: String, type: String });

// define a model
var Animal = mongoose.model('Animal', animalSchema);

// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
  return this.model('Animal').find({ type: this.type }, cb);
}

var dog = new Animal({ type: 'dog' });

dog.findSimilarTypes(function (err, dogs) {
  console.log(dogs); // woof
});

Mongoose

Custom static methods

// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name, cb) {
  return this.find({ name: new RegExp(name, 'i') }, cb);
}

var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
  console.log(animals);
});

Mongoose

Hooks (middlewares)

var schema = new Schema(..);

// Before saving

schema.pre('save', function(next) {
  // do stuff
  next();
});

// After saving

schema.post('save', function(doc) {
  console.log('%s has been saved', doc._id);
});

Models

Sauvegarde

var Tank = mongoose.model('Tank', yourSchema);

var small = new Tank({ size: 'small' });
small.save(function (err) {
  if (err) return handleError(err);
  // saved!
})

Models

Requêtes

var Person = mongoose.model('Person', yourSchema);

// find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
Person.findOne({ 'name': 'Ghost' }, 'name occupation', function (err, person) {
  if (err) return handleError(err);
  console.log('%s is a %s.', person.name, person.occupation)
})

findfindByIdfindOne, or where static methods.

In mongoose 4, a Query has a .then() function, and thus can be used as a promise.

Models

Requêtes (JSON docs)

// With a JSON doc
Person.
  find({
    occupation: /host/,
    'name.last': 'Ghost',
    age: { $gt: 17, $lt: 66 },
    likes: { $in: ['vaporizing', 'talking'] }
  }).
  limit(10).
  sort({ occupation: -1 }).
  select({ name: 1, occupation: 1 }).
  exec(callback);

Models

Requêtes (Query builder)

// Using query builder
Person.
  find({ occupation: /host/ }).
  where('name.last').equals('Ghost').
  where('age').gt(17).lt(66).
  where('likes').in(['vaporizing', 'talking']).
  limit(10).
  sort('-occupation').
  select('name occupation').
  exec(callback);

Models

Suppression

Tank.remove({ size: 'large' }, function (err) {
  if (err) return handleError(err);
  // removed!
});

Reprendre l'exercice de la userslist et faire en sorte d'utiliser Mongo plutôt que la session pour le stockage des utilisateurs.

exercice

Made with Slides.com