Mongoose est un Object Document Mapper (ODM) utilisé pour MongoDB, en opposition à un Object Relational Mapper (ORM) utilisé dans les bases de données relationnelles.
L'installation de Mongoose se fait avec npm
:
$ npm install mongoose
On peut utiliser les options -g
ou --save
si nécessaire. Voir la rubrique sur npm
pour plus de détails.
Créer un schema est plutôt simple. À la base, il faut spécifier les champs et leur type :
var simpleSchema = new Schema({ fieldName: SchemaType });
Un exemple plus complet ressemblerait à ceci :
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var customerSchema = new Schema({ name: String, address: String, city: String, state: String, country: String, zipCode: Number, createdOn: Date, isActive: Boolean });
created: { type: Date, default: Date.now }
Mongoose Data Type | Javascript Data Type |
---|---|
String | String |
Number | Number |
Date | Object |
Buffer | Object |
Boolean | Boolean |
Mixed | Object |
ObjectId | Object |
Array | Array (Object) |
On peut insérer des schemas à l'intérieur de schemas. Par exemple, un client a une adresse. Le client lui-même est un schema, mais l'adresse peut l'être aussi.
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var addressSchema = new Schema({ type: String, street: String, city: String, state: String, country: String, }); var customerSchema = new Schema({ name: { first: String, last: String }, address: [ addressSchema ], createdOn: { type: Date, default: Date.now }, isActive: { type: Boolean, default: true} });
Il faut remarquer que la propriété address
de customerSchema
est un tableau avec addressSchema
.
Par défaut, quand on spécifie pas de propriété identité, à l'instance de _id
, cette propriété est ajoutée automatiquement par MongoDB à l'ajout d'un document. La valeur par défaut de cette propriété est un ObjectId. On peut dire à Mongoose, dans le schéma, de ne pas ajouter cette propriété en lui assignant false
.
var noIdSchema = new Schema({ name: String }, { _id: false });
Si on prend l'exemple ci-dessous, on pourrait se demander pourquoi ne pas nommer questionType
tout simplement type
?
var subQuestions = { questionType: String, questionText: String, answers: [ subAnswers ] };
Les schemas de Mongoose ne peuvent pas contenir de propriété type
. Cette propriété a une signification. Celle-ci dit que le document doit être casté au type spécifié. Par exemple, si on change questionType: String
pour type: String
, l'objet sera casté en String et quand ce sera sauvegardé dans MongoDB, on verra type: [object Object]
.
Si on veut une propriété type
, on peut faire type: {type: String}
:
var subQuestions = { type: { type: String }, text: String, answers: [ subAnswers ] };
Avec add()
utilisé sur un schema, on peut ajouter des propriétés. Ceci est utile par exemple pour avoir des propriété à certaines conditions.
var exempleSchema = new Schema({ name: String }); exempleSchema.add({ address: String });
La création d'un modèle se fait avec mongoose.model()
. Le premier argument de model()
est le nom du modèle et le second est le schema. Normalement, le nom du modèle est singulier et sera plurialisé lors de la création de la collection sur MongoDB.
Dans le cas suivant, le nom du modèle est Person
, mais la collection devrait s'appeler People
.
var personSchema = new Schema({ firstName: String, lastName: String }); var Person = mongoose.model('Person', personSchema);
var bob = new Person({ firstName: 'Bob', lastName: 'Doe' });
bob.save(callback);
La signature de find()
:
Model.find(conditions, fields, options, callback);
Pour retrouver tous les documents d'un modèle, avec aucun callback et une exécution différée :
var allPeople = People.find(); allPeople.exec(function (err, results) { // Gérer l'erreur ou le résultat });
Avec un callback, exécute la requête immédiatement :
Standup.find(function (err, results) { // Gérer l'erreur ou le résultat });
Avec un callback et des conditions :
Standup.find({ memberName: 'David' }, function (err, results) { // Gérer l'erreur ou le résultat });
Limiter les propriétés retournés :
Standup.find({ memberName: 'Mary' }, 'memberName impediment', function (err, results) { });
Exemple d'options :
var query = Standup.findOne({ memberName: 'Bob'}, null, { limit: 10 });
Signature :
Model.findOne(conditions, [fields], [options], [callback])
Exemple sans callback et sans condition :
var Standup = require('../models/standup.server.model.js'); var query = Standup.findOne(); query.exec(function (err, results) { // gérer le résultat / l'erreur });
Exemple avec un callback :
var query = Standup.findOne({ memberName: 'Mark' });
Signature :
Model.findById(id, [fields], [options], [callback])
var id = '541c6fefefdf9c84172162a6'; var query = Standup.findById(id); query.exec(function (err, doc) { // gérer le résultat / l'erreur });
Même exemple que plus haut, mais avec la méthode chaînée :
Standup.findById(id).exec(function (err, results) { // gérer le résultat / l'erreur });
Exemple pour trouver un document par id
qui retourne tous les propriétés sauf impediment
. Le moins -
devant sert à soustraire des champs.
var query = Standup.findById(id, '-impediment');
Des opérateurs peuvent être utilisés dans les requêtes pour affiner les résultats. Ce sont les mêmes qu'utilisés dans MongoDB.
Les opérateurs de comparaison pour les requêtes | |
---|---|
$gt | greater than |
$gte | greater than or equal to $in exists in |
$lt | less than |
$lte | less than or equal to $ne not equal to |
$nin | does not exist |
Exemple :
Customer.find({discount: {$gte: 10}, function(err, results) { if (err) throw err; console.log(results); });
Au lieu d'utiliser une méthode find()
comme celle-ci :
Customer.find({discount: {$gte: 10, $lt: 20}, function(err, results) { // code ... });
On peut utiliser la méthode where()
qui est chaînable :
Customer.where('discount').gte(10).lt(20).exec(function(err, results) { // code ... });
Autre exemple avec plusieurs where()
chaînés :
Customer.where('discount').gte(10).lt(20) .where('zipCode', '12345') .exec(function(err, results) { // code ... });
Pour mettre à jour un document, on peut le récupérer et le sauvegarder dans le callback :
Standup.findById(id).exec(function (err, doc) { // Mise à jour ... doc.impediment('None'); doc.save(function (err) { // code du callback save. }); });
Le code ci-haut fait en sorte de faire deux requêtes. Il est préférable d'utiliser update()
:
Model.update(conditions, update, [options], [callback])
var condition = { memberName: 'Mary' }; var update = { impediment: 'None – Mary no longer works here!' }; Standup.update(condition, update, function(err, numberAffected, rawResponse) { // code du callback });
Option | Description | Default Value |
---|---|---|
safe | Safe mode – default to value set in schema | True |
upsert | Create the document if it does not match | False |
multi | Determines if multiple documents should be updated or not | False |
strict | Override the strict option for this update | |
overwrite | Disables the update-only mode to allow for overwrite of document | False |
Exemple qui montre la mise à jour de plusieurs documents qui matchent :
var condition = { firstName: 'Bob' }; var update = { firstName: 'Robert' }; Customer.update(condition, update, { multi: true }, function(err, numberAffected, raw) { // code du callback });
Option | Description | Default Value |
---|---|---|
new | Set to true to return the modified document rather than the original. | True |
upsert | Create the document if it does not match | False |
select | Specify the document fields to return |
Signature
Model.remove(conditions, [callback])
var condition = { memberName: 'Mary' }; Standup.remove(condition, function(err) { // code callback });
Supprimer tous les documents qui sont créés le ou après le 31 octobre 2014:
var gteDate = new Date(2014, 10, 31); Standup.remove({ createdOn: { $gte: gteDate }}, function (err) { // code callback });
Suppression sans callback :
var query = Standup.remove({ createdOn: { $gte: gteDate }}); query.exec();
Option | Description | Default Value |
---|---|---|
select | Specify the document fields to return |
var query = Standup.find(); query.sort({ createdOn: 'desc' }) .limit(10) .exec(function(err, results) { // code callback });
Schema Type | Built-in Validators | ||
---|---|---|---|
String | required | enum | match |
Number | required | min | max |
Date | required | ||
Buffer | required | ||
Boolean | required | ||
Mixed | required | ||
ObjectId | required | ||
Array | required |
Exemple de schema :
var customerSchema = new Schema({ name: { type: String, required: true }, address: String, city: String, state: String, country: String, zipCode: Number, createdOn: Date, isActive: Boolean });
Une fois le schema définit, on peut préciser une validation avec required()
. La signature est:
required(required, [message])
customerSchema.path('city').required(true, ‘Oops! Supply a city.’);
var reMatch = /[a-zA-Z]/; var customerSchema = new Schema({ name: { type: String, required: true, match: reMatch }, // etc... });
var impediments = ['none', 'minor', 'blocking', 'severe']; var standupSchema = new Schema({ // abbreviated... impediment: { type: String, required: true, enum: impediments } });
var customerSchema = new Schema({ name: String, discount: { type: Number, min: 5 } }); var customerSchema = new Schema({ name: String, discount: { type: Number, max: 60 } }); var customerSchema = new Schema({ name: String, discount: { type: Number, min: 5, max: 60 } });
Une validation personnalisée peut être spécifiée avec validate
. L'important, c'est que la fonction du validateur retourne true
ou false
, indiquant que la validation est correcte ou non.
var sizeValidator = [ function (val) { return (val.length > 0 && val.length <= 50) }, // Custom error text... 'String must be between 1 and 50 characters long' ]; var personSchema = new Schema({ firstName: { type: String, required: true, validate: sizeValidator }, lastName: { type: String, required: true, validate: sizeValidator }, status: { type: String, required: true, default: 'Alive' } }); var Person = new mongoose.model('Person', personSchema); var newPerson = new Person( { firstName: 'John', lastName: 'Doe' } ); newPerson.save(function (err) { if (err) return handleError(err); // saved the person document! });