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
- 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 validatordefault
: Any value or function that sets the path default. If it is a function, the function returns the default valueselect
: Boolean specifies the default value for queryprojectionsvalidate
: function adds avalidator function for this propertyget
: function usageObject.defineProperty()
Define custom gettersset
: function usageObject.defineProperty()
Define custom settersalias
: 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 indexunique
: Specifies whether a Boolean value is created for this attributeThe only indexsparse
: 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 expressionenum
Array 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 valuemax
: Value creationThe validatorCheck whether the property is less than or equal to the value
Date
min
: Datemax
: 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 use
doc.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:
-
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
-
Model.findone ({age:5},function(err,doc){//doc is a single document});
-
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}).
-
Model.count(conditions,callback)
-
Model.remove Deletes the qualified document model.remove (conditions,callback)
-
Model. Distinct queries qualified documents and returns results grouped by key
Model.distinct(field,conditions,callback)
-
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)
-
Model.$where
Find ({where: JavaScript}) is a shortcut to query MongoDB using JavaScript expressions. $WHERE (‘ this.firstName ===this.lastname’).exec(callback)
-
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)
-
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);