The three most important concepts in Mongoose are Schemas, Models, and Documents, as well as verification and middleware. The first three are very important. I won’t introduce you to the knowledge about Populate and Discriminators. For details, please refer to official documents.

All Mongoose started with a Schema, which defines the composition of documents in a collection. Models are constructors compiled from a Schema, and their instances represent documents that can be saved and read from a database. Creating and reading documents from the database is done through the Model, while saving is done on the Model instance.

var schema = new mongoose.Schema({ name: 'string'.size: 'string' });
var Tank = mongoose.model('Tank', schema);

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

// or

Tank.create({ size: 'small' }, function (err, small) {
  if (err) return handleError(err);
  // saved!
})
Copy the code

Model Schemas

  1. How to define our own methods for Models Instances (Documents) Although Douments has many native instance methods, we can also customize our own methods, which need to be defined on the Schema.
  var animalSchema = new Schema({ name: String.type: String });

  animalSchema.methods.findSimilarTypes = function(cb) {
    return this.model('Animal').find({ type: this.type }, cb);
  };
  
  var Animal = mongoose.model('Animal', animalSchema);
  var dog = new Animal({ type: 'dog' });

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

Static methods can also be added

 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);
  });
Copy the code

There are many configuration options, which can be set at construction time or directly

newSchema({.. }, options);// or

var schema = newSchema({.. }); schema.set(option, value);Copy the code

Model Models

The query

Documents can be queried using the Model static methods find, findById,findOne, and WHERE

delete

The remove method

update

The Update method of Model modifies documents in the database, but does not return them

The findOneAndUpdate method is used to update a single document and return it to the application layer

Document the Documents

retrieve

There are many retrieval methods, which will not be elaborated temporarily

update

There are also many newer methods

  • FindById + change is worth doing

    Tank.findById(id, function (err, tank) {
      if (err) return handleError(err);
    
      tank.size = 'large';// Or use tank.set({size: 'large'});
      tank.save(function (err, updatedTank) {
        if (err) return handleError(err);
        res.send(updatedTank);
      });
    });
    Copy the code
  • update

    Tank.update({ _id: id }, { $set: { size: 'large' }}, callback);
    Copy the code
  • The findByIdAndUpdate method returns the document

    Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true }, function (err, tank) {
      if (err) return handleError(err);
      res.send(tank);
    });
    Copy the code
  • Other methods such as findAndUpdate/Remove find and return at most one document

validation

The Document will be validated before it is saved

cover

You can override the entire document with.set()

Tank.findById(id, function (err, tank) {
  if (err) return handleError(err);
  // Now `otherTank` is a copy of `tank`
  otherTank.set(tank);
});
Copy the code

Query the queries

Find findById count Update (find findById count update) can be executed in either of the following ways:

  • If a callback argument is passed, the operation is executed immediately and the query result is passed to the callback function.

  • Without passing the callback argument, an instance of Query (a Query object) is returned, which provides a special interface to build the Query. Query instances have a.then() function, which is used similarly to Promise.

    Callback ();

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

// Query each person whose last name is 'Ghost', select 'name' and 'occupation' fields
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation'.function (err, person) {
  if (err) return handleError(err);
  // Prints "Space Ghost is a talk show host".
  console.log('%s %s is a %s.', person.name.first, person.name.last,
    person.occupation);
});
Copy the code

If no callback argument is passed:

// Query each person whose last name is 'Ghost'
var query = Person.findOne({ 'name.last': 'Ghost' });

// Select 'name' and 'occupation' fields
query.select('name occupation');

// Then execute the query
query.exec(function (err, person) {
  if (err) return handleError(err);
  // Prints "Space Ghost is a talk show host."
  console.log('%s %s is a %s.', person.name.first, person.name.last,
    person.occupation);
});
Copy the code

Schema type SchemaTypes

There are SchemaTypes:

  • String

  • Number

  • Date

  • Buffer

  • Boolean

  • Mixed

  • ObjectId

  • Array

  • Decimal128 allows you to declare schema Type as a type or assign an object with a Type attribute

    var schema1 = new Schema({
      test: String // `test` is a path of type String
    });
    
    var schema2 = new Schema({
      test: { type: String } // `test` is a path of type string
    });
    Copy the code

    In addition to the Type attribute, there are some options available for all types and some options available for limited parts of types

    All available
    • required: Boolean value or function added to this property if the value is trueThe required validator
    • default: Any value or function that sets the path default. If it is a function, the function returns the default value
    • select: Boolean specifies the default value for queryprojections
    • validate: function adds avalidator function for this property
    • get: function usageObject.defineProperty()Define custom getters
    • set: function usageObject.defineProperty()Define custom setters
    • alias: String only mongoose >= 4.10.0. Defines the field pathThe virtual value gets/sets
    var numberSchema = new Schema({
      integerOnly: {
        type: Number.get: v= > Math.round(v),
        set: v= > Math.round(v),
        alias: 'i'}});var Number = mongoose.model('Number', numberSchema);
    
    var doc = new Number(a); doc.integerOnly =2.001;
    doc.integerOnly; / / 2
    doc.i; / / 2
    doc.i = 3.001;
    doc.integerOnly; / / 3
    doc.i; / / 3
    Copy the code
    Indexes related to
    • index: Specifies whether a Boolean value is created for this attributeThe index
    • unique: Specifies whether a Boolean value is created for this attributeThe only index
    • sparse: Specifies whether a Boolean value is created for this attributeSparse index
var schema2 = new Schema({
  test: {
    type: String.index: true.unique: true // Unique index. If you specify `unique: true`
    // specifying `index: true` is optional if you do `unique: true`}});Copy the code
String
  • lowercase: Whether a Boolean value is called before saving.toLowerCase()
  • uppercase: Whether a Boolean value is called before saving.toUpperCase()
  • trim: Whether a Boolean value is called before saving.trim()
  • match: Creates a regular expressionThe validatorCheck that the value matches the given regular expression
  • enumArray creationThe validatorCheck whether the value is contained in the given array
Number
  • min: Value creationThe validatorCheck whether the property is greater than or equal to the value
  • max: Value creationThe validatorCheck whether the property is less than or equal to the value
Date
  • min: Date
  • max: Date

To verify the validation

  • Validation is defined in SchemaType
  • Validation is a middleware. It is registered with the schema by default as the Pre (‘save’) hook
  • You can usedoc.validate(callback)doc.validateSync()Manual verification
  • The validator does not validate undefined values, with the only exception being the Required validator
  • Validation is asynchronous and recursive. When you call Model#save, the subdocument validation is also performed, and the Model#save callback receives an error if something goes wrong
  • Validation is customizable
    var schema = new Schema({
      name: {
        type: String.required: true}});var Cat = db.model('Cat', schema);

    // This cat has no name :(
    var cat = new Cat();
    cat.save(function(error) {
      assert.equal(error.errors['name'].message,
        'Path `name` is required.');

      error = cat.validateSync();
      assert.equal(error.errors['name'].message,
        'Path `name` is required.');
    });
Copy the code

Built-in validators

Mongoose has some built-in validators

  • All SchemaTypes have a built-in Required validator. The Required validator uses the checkRequired() function to determine whether this value satisfies the Required validator
  • Numbers have min and Max validators.
  • Strings has enum, match, MaxLength, and MinLength validators

Custom validators

Custom validators are defined by passing in a validation function

 var userSchema = new Schema({
      phone: {
        type: String.validate: {
          validator: function(v) {
            return /\d{3}-\d{3}-\d{4}/.test(v);
          },
          message: '{VALUE} is not a valid phone number! '
        },
        required: [true.'User phone number required']}});var User = db.model('user', userSchema);
    var user = new User();
    var error;

    user.phone = '555.0123';
    error = user.validateSync();
    assert.equal(error.errors['phone'].message,
      '555.0123 is not a valid phone number! ');

    user.phone = ' ';
    error = user.validateSync();
    assert.equal(error.errors['phone'].message,
      'User phone number required');

    user.phone = '201-555-0123';
    // Validation succeeds! Phone number is defined
    // and fits `DDD-DDD-DDDD`
    error = user.validateSync();
    assert.equal(error, null);
Copy the code

Asynchronous custom validators

Custom validators can be asynchronous. If the validation function returns a promise (like async), Mongoose will wait for the promise to complete. If you prefer to use the callback function, set the isAsync option and Mongoose will use the callback function as the second argument to the validation function.

var userSchema = new Schema({
      name: {
        type: String.// You can also make a validator async by returning a promise. If you
        // return a promise, do **not** specify the `isAsync` option.
        validate: function(v) {
          return new Promise(function(resolve, reject) {
            setTimeout(function() {
              resolve(false);
            }, 5); }); }},phone: {
        type: String.validate: {
          isAsync: true.validator: function(v, cb) {
            setTimeout(function() {
              var phoneRegex = /\d{3}-\d{3}-\d{4}/;
              var msg = v + ' is not a valid phone number! ';
              // The first argument is a Boolean value that represents the validation result
              The second argument is an error message
              cb(phoneRegex.test(v), msg);
            }, 5);
          },
          // The default error message is overwritten by the second argument of 'cb()'
          message: 'Default error message'
        },
        required: [true.'User phone number required']}});var User = db.model('User', userSchema);
    var user = new User();
    var error;

    user.phone = '555.0123';
    user.name = 'test';
    user.validate(function(error) {
      assert.ok(error);
      assert.equal(error.errors['phone'].message,
        '555.0123 is not a valid phone number! ');
      assert.equal(error.errors['name'].message,
        'Validator failed for path `name` with value `test`');
    });
  
Copy the code

Middleware middleware

Middleware (Pre and POST hooks) are the control functions passed in by the function when the asynchronous function executes. All middleware in Mongoose supports pre and POST hooks.

The pre hooks

Pre hooks can be divided into serial and parallel hooks. Serial means that the middleware executes one after another, that is, when the last middleware calls the next function, the next one executes.

var schema = newSchema(..) ; schema.pre('save'.function(next) {
  // do stuff
  next();
});
Copy the code

Parallel middleware provides fine-grained flow control

var schema = newSchema(..) ;// Parallel execution is performed only if the second argument is' true'
schema.pre('save'.true.function(next, done) {
  // calling next kicks off the next middleware in parallel
  next();
  setTimeout(done, 100);
});
Copy the code

Post middleware

The POST middleware is called after the method executes

schema.post('init'.function(doc) {
  console.log('%s has been initialized from the db', doc._id);
});
schema.post('validate'.function(doc) {
  console.log('%s has been validated (but not saved yet)', doc._id);
});
schema.post('save'.function(doc) {
  console.log('%s has been saved', doc._id);
});
schema.post('remove'.function(doc) {
  console.log('%s has been removed', doc._id);
});
Copy the code

If we pass two arguments to the callback function, Mongoose will assume that the second argument is the next function, which we can use to trigger the next middleware

// Takes 2 parameters: this is an asynchronous post hook
schema.post('save'.function(doc, next) {
  setTimeout(function() {
    console.log('post1');
    // Kick off the second post hook
    next();
  }, 10);
});

// Will not execute until the first middleware calls `next()`
schema.post('save'.function(doc, next) {
  console.log('post2');
  next();
});
Copy the code

Connect the Connections

We can use mongoose. The connect (), the simplest as mongoose. Connect (‘ mongo: / / localhost/myapp ‘);

And, of course, we can also specify multiple parameters in the uri mongoose. Connect (‘ mongo: / / username: password @ host: port/database? options… ‘);

We can also use mongoose. The createConnection () to connect, it returns a new connection, behind the connection object is used to create and retrieve models.

const conn = mongoose.createConnection('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', options);
Copy the code

Operating the cache

This means that we don’t have to wait for the connection to be established to use models. Mongoose will cache the Model operation first

var MyModel = mongoose.model('Test'.new Schema({ name: String }));
// The operation will be suspended until the connection succeeds
MyModel.findOne(function(error, result) { / *... * / });

setTimeout(function() {
  mongoose.connect('mongodb://localhost/myapp');
}, 60000);
Copy the code

If you want to disable caching, you can modify the bufferCommands configuration or disable bufferCommands globally

mongoose.set('bufferCommands'.false);
Copy the code

options

The connect method can accept the options parameter. Please refer to the official documentation for details

mongoose.connect(uri,options)
Copy the code
const options = {
  useMongoClient: true.autoIndex: false.// Don't build indexes
  reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
  reconnectInterval: 500.// Reconnect every 500ms
  poolSize: 10.// Maintain up to 10 socket connections
  // If not connected, return errors immediately rather than waiting for reconnect
  bufferMaxEntries: 0
};
mongoose.connect(uri, options);
Copy the code

The callback

Connect can accept a callback or return a promise

mongoose.connect(uri,options,function(error){
})

mongoose.connect(uri,options).then(
 () => { /** ready to use. The `mongoose.connect()` promise resolves to undefined. */ },
  err => { /** handle initial connection error */ }
)
Copy the code

Pay attention to

There is a connection pool concept, whether using mongoose. Connect or mongoose. CreateConnection create connections, have been included in the default maximum 5 connection pool, can through poolSize options to adjust:

// With object options
mongoose.createConnection(uri, { poolSize: 4 });

const uri = 'mongodb://localhost/test? poolSize=4';
mongoose.createConnection(uri);
Copy the code

Subdocument Subdocument

A subdocument is a document nested within another document. Mongoose documents have two different concepts: an array of subdocuments and a single nested subdocument

var childSchema = new Schema({ name: 'string' });

var parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
});
Copy the code

Subdocuments are similar to regular documents, except that they cannot be saved separately; they are saved when their top-level documents are saved

var Parent = mongoose.model('Parent', parentSchema);
var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
parent.children[0].name = 'Matthew';

// 'parent.children[0].save()' has no operation, although it triggers middleware
// But ** does not ** save the document. You need to save his parent document
parent.save(callback);
Copy the code

Common operations:

  1. Model. The find Model. The find (query, fields, the options, the callback) / / fields and options are simple query optional parameters Model.find({‘csser.com’:5},function(err,docs){docs is the result array}); Model.find({},[‘first’,’last’],function(err,docs){//docs

  2. Model.findone ({age:5},function(err,doc){//doc is a single document});

  3. Model.findbyid is the same as findOne, but it takes the _id of the document as an argument and returns a single document. _ID can be a string or an ObjectID object model.findById (obj._id,function(err,doc){//doc is a single document}).

  4. Model.count(conditions,callback)

  5. Model.remove Deletes the qualified document model.remove (conditions,callback)

  6. Model. Distinct queries qualified documents and returns results grouped by key

    Model.distinct(field,conditions,callback)

  7. Where Model. Where (‘age’).gte(25) where Model.

    where(‘tags’).in([‘movie’,’music’,’art’]) .select(‘name’,’age’,’tags’) .skip(20) .limit(10) .asc(‘age’) .slaveOk() .hint({age:1,name:1}) .run(callback)

  8. Model.$where

    Find ({where: JavaScript}) is a shortcut to query MongoDB using JavaScript expressions. $WHERE (‘ this.firstName ===this.lastname’).exec(callback)

  9. Model.update

    Use the UPDATE clause to update the document with the specified condition, Var conditions={name:’borne’},update={$inc:{visits:1}} ,options={multi:true} Model.update(conditions,update,options,callback)

    Note: For backward compatibility, all top-level update keys that are not named by an atomic operation are treated as $set. For example:

    var queryb={name:’borne’};

    Model.update(query,{name:’jason borne’},options,callback)

    It will be sent to the database server like this

    Model.update(query,{$set:{name:’jason borne’}},options,callback)

  10. If the Query API does not provide a callback function, all of these methods return Query objects, which can be modified again until the exec method var Query = model.find ({}) is called;

    query.where(‘field’,5);

    query.limit(5);

    query.skip(100);