mongoose学习教程
why mongoose?
在官网上是这样说的,我们开发Mongoose是因为(开发者)写MongoDB的验证机制、类型转换与业务逻辑模板很麻烦。 针对为应用数据建模的问题,Mongoose 提供了一套直白的,基于模式的解决方案。包括了内建的类型转换、验证器、查询构造器、业务逻辑钩子等。
简介
mongoose是mongoDB的一个对象模型工具,是基于node-mongodb-native
开发的mongoDB的nodejs驱动,可以在异步的环境下执行。同时它也是针对mongoDB操作的一个对象模型库,封装了mongoDB对文档的一些增删改查等常用方法,让nodejs操作mongoDB数据库变得更加容易。
安装
在安装mongoose之前我们需要安装mongodb
和nodejs
,然后
npm install mongoose
然后开始建立连接并使用即可:
var mongoose = require('mongoose');
mongoose.connect('mongodb://user:pass@localhost:port/database',options);
var conn = mongoose.connection;
conn.on('connected', callback); //连接成功
conn.on('disconnected', callback); //断开连接
conn.on('connecting', callback); //连接中
conn.on('disconnecting', callback); //断开连接中
conn.on('error', callback); //连接异常
Schemas
mongoose中的schema对应于mongodb中的collection,并且定义了这个文档里面的数据模型。
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var blogSchema = new Schema({
title: String,
author: String,
body: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});
//如果想在后期加入别的数据模型
blogSchema.add({ name: 'string', color: 'string', price: 'number' });
在定义数据模型的时候,您需要指定每个字段对应的数据类型,mongoose支持以下这些类型:
String
Number
Date
Buffer
Boolean
Mixed
ObjectId
Array
Decimal128
Map
您可以直接指定数据的类型,还可以在指定类型的同时,设置一些选项:
var schema1 = new Schema({
test: String
});
var schema2 = new Schema({
test: {
type: String,
lowercase: true //该选项只支持string类型
}
});
公共选项列表如下:
required
是否是必须填写的default
默认值,可以是一个值或者一个带返回值的函数select
布尔值,是否启用Mongodb的projection
功能validate
函数,添加验证器get
添加getter函数set
添加setter函数alias
字符类型,定义一个virtual
序列选项列表如下:index
布尔值,是否为该键建立序列unique
布尔值,是否建议唯一序列sparse
布尔值,是否建立稀疏序列
字符串选项列表如下:lowercase
布尔值,转换为小写uppercase
布尔值,转换为大写trim
布尔值,是否使用trim()
方法消除空白match
正则,检查是否匹配enum
数组,检测是否在数组中minlength
最小长度maxlength
最大长度
注意的点:
- 内置的js的Date方法改变mongoose的date类型数据时,保存时无效,必须先调用
markModified
方法
var Assignment = mongoose.model('Assignment', { dueDate: Date });
Assignment.findOne(function (err, doc) {
doc.dueDate.setMonth(3);
doc.save(callback); // 无效
doc.markModified('dueDate');
doc.save(callback); // 有效
})
- mixed类型的数据类型如果值改变,也需要先调用
markModified
方法
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save();
- 具化ObjectId的时候需要使用
Schema.Types.ObjectId
var ObjectId = mongoose.Schema.Types.ObjectId;
var Car = new Schema({ driver: ObjectId });
- map类型的数据需要
get()
和set()
来取值和设置值
const user = new User({
socialMediaHandles: {}
});
user.socialMediaHandles.set('github', 'vkarpov15');
user.set('socialMediaHandles.twitter', '@code_barbarian');
console.log(user.socialMediaHandles.get('github'));
console.log(user.get('socialMediaHandles.twitter'));
schema.path()
可以获得实例化的schema的类型
var sampleSchema = new Schema({ name: { type: String, required: true } });
console.log(sampleSchema.path('name'));
// 输出如下:
/**
* SchemaString {
* enumValues: [],
* regExp: null,
* path: 'name',
* instance: 'String',
* validators: ...
*/
我们可以在schema上定义一些methods
,该方法可以在document上使用(注意不要使用箭头函数,否则无法获取this)
var animalSchema = new Schema({ name: String, type: String });
animalSchema.methods.findSimilarTypes = function(cb) {
return this.model('Animal').find({ type: this.type }, cb);
};
//或者这样写
animalSchema.method('findSimilarTypes ', function () {})
animalSchema.method({findSimilarTypes : function () {}});
//在Model上使用
var Animal = mongoose.model('Animal', animalSchema);
var dog = new Animal({ type: 'dog' });
dog.findSimilarTypes(function(err, dogs) {
console.log(dogs); // woof
});
我们还可以在schema上定义一些statics
,该方法可以在model上使用:
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);
});
我们还可以为查找器增加一些链式帮助函数
animalSchema.query.byName = function(name) {
return this.where({ name: new RegExp(name, 'i') });
};
var Animal = mongoose.model('Animal', animalSchema);
Animal.find().byName('fido').exec(function(err, animals) {
console.log(animals);
});
mongoose默认会为每个schema建立一个自动索引,不过在生产环境建议关掉,有如下这些方法:
mongoose.connect('mongodb://user:pass@localhost:port/database', { autoIndex: false });
mongoose.createConnection('mongodb://user:pass@localhost:port/database', { autoIndex: false });
animalSchema.set('autoIndex', false);
new Schema({..}, { autoIndex: false });
您还可以为schema设置虚拟的属性,该属性不会保存在数据库中,但是在你取值设置值的时候,可以分解或者合并属性值:
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
var Person = mongoose.model('Person', personSchema);
var axl = new Person({
name: { first: 'Axl', last: 'Rose' }
});
personSchema.virtual('fullName').get(function () {
return this.name.first + ' ' + this.name.last;
});
console.log(axl.fullName); // Axl Rose
注意要对虚拟属性进行toJSON()
ortoObject()
转换时需要传递{ virtuals: true }
参数进来才行。
你还可以使用alias
为属性名设置别名
var personSchema = new Schema({
n: {
type: String,
alias: 'name'
}
})
var person = new Person({ name: 'Val' });
console.log(person); // { n: 'Val' }
console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }
注意如果是嵌套属性,需要设置嵌套路径
const parentSchema = new Schema({
name: {
f: {
type: String,
alias: 'name.first'
}
}
});
您还可以为schema设置选项,语法如下:
new Schema({..}, options);
//或者
var schema = new Schema({..});
schema.set(option, value);
它有如下这些选项:
autoIndex
布尔值,是否开启自动索引bufferCommands
布尔值,当连接挂掉重连之前是否缓冲命令capped
数值,使用固定大小collectioncollection
字符串,为collection设置不同的名字id
布尔值,关闭默认的虚拟属性id
值(返回的其实是_id)_id
布尔值,关闭_id
值(将会导致无法保存)minimize
布尔值,设为false会保存空对象read
设置query的read的优先级writeConcern
在schema级别设置write concernsafe
遗留api,与writeConcern相同shardKey
设置分片key值strict
布尔值,规定在schema中没有定义的key不会被保存strictQuery
为filter设置querytoJSON
转换为json对象与toObject相同,但是需要显示调用toObject
转换为Json对象
var schema = new Schema({ name: String });
schema.path('name').get(function (v) {
return v + ' is my name';
});
schema.set('toJSON', { getters: true, virtuals: false });
var M = mongoose.model('Person', schema);
var m = new M({ name: 'Max Headroom' });
schema.set('toObject', { getters: true });
console.log(m.toJSON());// { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }
console.log(m); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }
typeKey
默认的,mongoose会将type
字符串当做Mongoose的type解释,如果需要当做属性进行解释,需要设置typeKey
var schema = new Schema({
// Mongoose interpets this as 'loc is an object with 2 keys, type and coordinates'
loc: { type: String, coordinates: [Number] },
// Mongoose interprets this as 'name is a String'
name: { $type: String }
}, { typeKey: '$type' }); // A '$type' key means this object is a type declaration
- validateBeforeSave手动控制是否在保存之前进行验证
var schema = new Schema({ name: String });
schema.set('validateBeforeSave', false);
versionKey
自定义版本控制collation
设置校验skipVersioning
忽略版本管理timestamps
设置时间戳,默认为createdAt 和updatedAtuseNestedStrict
设置嵌套schema的更新规则
连接
您可以使用下面的语法来连接mongodb:
mongoose.connect('mongodb://username:password@host:port/database?options...',options);
options有一下选项:
bufferCommands
user/pass
autoIndex
dbName
autoReconnect
reconnectTries
reconnectInterval
promiseLibrary
poolSize
bufferMaxEntries
connectTimeoutMS
socketTimeoutMS
Models
Models是用来创建和读写数据库的,它的一个实例就是document。
var Tank = mongoose.model('Tank', yourSchema);
//第一种方法
var small = new Tank({ size: 'small' });
small.save(function (err) {
if (err) return handleError(err);
// saved!
});
// 第二种方法
Tank.create({ size: 'small' }, function (err, small) {
if (err) return handleError(err);
// saved!
});
// 第三种方法
Tank.insertMany([{ size: 'small' }], function(err) {
});
注意每一个model都有对应的连接,如果使用的是mongoose.model()
,则对应mongoose.connect('localhost', 'gettingstarted');
如果使用的是自定义的连接,则使用
var connection = mongoose.createConnection('mongodb://localhost:27017/test');
var Tank = connection.model('Tank', yourSchema);
常用查询操作:
deleteMany()
deleteOne()
find()
findById()
findByIdAndDelete()
findByIdAndRemove()
findByIdAndUpdate()
findOne()
findOneAndDelete()
findOneAndRemove()
findOneAndUpdate()
replaceOne()
updateMany()
updateOne()
这些方法都有两种方法,一种是直接使用回调函数,另一种是使用.then()
链式调用(不同于真正的promise,mongoose返回的查询不能使用多次then,否则会执行多次),当数据改变的时候,您还可以使用model.watch
来进行观察:
async function run() {
// Create a new mongoose model
const personSchema = new mongoose.Schema({
name: String
});
const Person = mongoose.model('Person', personSchema, 'Person');
// Create a change stream. The 'change' event gets emitted when there's a
// change in the database
Person.watch().
on('change', data => console.log(new Date(), data));
// Insert a doc, will trigger the change stream handler above
console.log(new Date(), 'Inserting doc');
await Person.create({ name: 'Axl Rose' });
}
2018-05-11T15:05:35.467Z 'Inserting doc'
2018-05-11T15:05:35.487Z 'Inserted doc'
2018-05-11T15:05:35.491Z { _id: { _data: ... },
operationType: 'insert',
fullDocument: { _id: 5af5b13fe526027666c6bf83, name: 'Axl Rose', __v: 0 },
ns: { db: 'test', coll: 'Person' },
documentKey: { _id: 5af5b13fe526027666c6bf83 } }
您不仅可以把查询的条件一次性全部写在查询json中,还可以使用链式方式书写,下面的写法都是相等的:
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);
// 相等的写法
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);
您还可以为查询方法设置一些钩子函数,使用cursor()
即可,有两种方法,第一种使用stream调用,第二种使用next
手动调用:
// There are 2 ways to use a cursor. First, as a stream:
Thing.
find({ name: /^hello/ }).
cursor().
on('data', function(doc) { console.log(doc); }).
on('end', function() { console.log('Done!'); });
// Or you can use `.next()` to manually get the next doc in the stream.
// `.next()` returns a promise, so you can use promises or callbacks.
var cursor = Thing.find({ name: /^hello/ }).cursor();
cursor.next(function(error, doc) {
console.log(doc);
});
subdocuments
documents代表存储在mongodb中的实际数据,每一个document都是model的一个实例。subdocuments就是嵌套在其他documents中的子文档。mongoose中有两种子文档类型,一种是数组,一种是单个文档:
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
});
subdocuments和documents之间的主要不同就是,subdocuments不单独保存,只要父级documents保存了,它就会被保存。子文档的pre('save')
和pre('validate')
会在父文档的pre('save')
之前,pre('validate')
之后执行。您可以使用_id
查找子文档:
var doc = parent.children.id(_id);
您可以使用push,unshift,addToSet来对子文档数组操作,或者直接使用create方法:
var Parent = mongoose.model('Parent');
var parent = new Parent;
// create a comment
parent.children.push({ name: 'Liesl' });
var subdoc = parent.children[0];
console.log(subdoc) // { _id: '501d86090d371bab2c0341c5', name: 'Liesl' }
subdoc.isNew; // true
parent.save(function (err) {
if (err) return handleError(err)
console.log('Success!');
});
//或者
var newdoc = parent.children.create({ name: 'Aaron' });
当您想要删除子文档时,可以使用remove
方法:
// Equivalent to `parent.children.pull(_id)`
parent.children.id(_id).remove();
// Equivalent to `parent.child = null`
parent.child.remove();
验证
在mongoose中,数据的每次操作都需要进行验证,这样就可以防止一些恶意注入等危害。验证是在schema中定义的,且是middleware。
Mongoose中有一些内置的验证器:
required
Numbers
有min
和max
验证器Strings
有enum
,match
,maxlength
andminlength
验证器
如果想要自定义你自己的验证器,可以加入validate
属性:
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!');
您还有可以设置异步的验证器:
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!';
// First argument is a boolean, whether validator succeeded
// 2nd argument is an optional error message override
cb(phoneRegex.test(v), msg);
}, 5);
},
// Default error message, overridden by 2nd argument to `cb()` above
message: 'Default error message'
},
required: [true, 'User phone number required']
}
});
如果想要在update()
或者findOneAndUpdate()
中开启验证的话,需要设置runValidators
:
var toySchema = new Schema({
color: String,
name: String
});
var Toy = db.model('Toys', toySchema);
Toy.schema.path('color').validate(function (value) {
return /blue|green|white|red|orange|periwinkle/i.test(value);
}, 'Invalid color');
var opts = { runValidators: true };
Toy.update({}, { color: 'bacon' }, opts, function (err) {
assert.equal(err.errors.color.message,
'Invalid color');
});
update
验证器和document
验证器有几点区别:
- update验证器this未定义,document验证器this指向该document,不过你可以在update验证器上通过设置context设置this指向该查询
- update验证器只验证对应的路径
- update验证器只在一些操作符时候运行,
$set
,$unset
,$push (>= 4.8.0)
,$addToSet (>= 4.8.0)
,$pull (>= 4.12.0)
,$pullAll (>= 4.12.0)
Middleware
Mongoose有4种中间件:
- document 中间件:
init
validate
save
remove
- model 中间件:
count
find
findOne
findOneAndRemove
findOneAndUpdate
update
updateOne
updateMany
aggregate
中间件:aggregate
query
中间件:insertMany
有两种钩子函数,一种是串行的,一种是并行的,在串行中,你可以使用next
来执行下一步,或者返回一个promise函数,pre表示之前,post表示之后:
var schema = new Schema(..);
schema.pre('save', function(next) {
// do stuff
next();
});
schema.pre('save', function() {
return doStuff().
then(() => doMoreStuff());
});
// Or, in Node.js >= 7.6.0:
schema.pre('save', async function() {
await doStuff();
await doMoreStuff();
});
并行的提供了更加细粒度的流控制:
var schema = new Schema(..);
// 第二个参数设置为true表示并行
schema.pre('save', true, function(next, done) {
// calling next kicks off the next middleware in parallel
next();
setTimeout(done, 100);
});
注意post和pre不会在update(), findOneAndUpdate()上执行。
Populate
population是一个自动把document中的路径转换为对应的document的过程,使用ref
指向即可:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
注意ObjectId, Number, String, and Buffer都是合法的ref类型,但是对于新手最好只使用ObjectId。
然后在查询的时候,我们就可以populate
将对应的数据整合进来:
Story.
findOne({ title: 'Casino Royale' }).
populate('author').
exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Ian Fleming"
});
如果只想整合一些域的话,可以在populate的第二个参数中写入要选择的字段,如果是多个的话可以用空格分开:
Story.
findOne({ title: /casino royale/i }).
populate('author', 'name'). // only return the Persons name
exec(function (err, story) {})
如果想要整合多个ref的话,可以连续调用(如果对同一个ref设置多个,则只有最后一个生效)
Story.
find(...).
populate('fans').
populate('author').
exec();
如果想要更精细化的控制,还可以给populate传入一个对象:
Story.
find(...).
populate({
path: 'fans',
match: { age: { $gte: 21 }},
// Explicitly exclude `_id`, see http://bit.ly/2aEfTdB
select: 'name -_id',
options: { limit: 5 }
}).
exec();
如果你想要populate文档下面的populate对象,则:
User.
findOne({ name: 'Val' }).
populate({
path: 'friends',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'friends' }
});
跨数据库populate:
var eventSchema = new Schema({
name: String,
// The id of the corresponding conversation
// Notice there's no ref here!
conversation: ObjectId
});
var conversationSchema = new Schema({
numMessages: Number
});
var db1 = mongoose.createConnection('localhost:27000/db1');
var db2 = mongoose.createConnection('localhost:27001/db2');
var Event = db1.model('Event', eventSchema);
var Conversation = db2.model('Conversation', conversationSchema);
Event.
find().
populate({ path: 'conversation', model: Conversation }).
exec(function(error, docs) { /* ... */ });
您还可以通过设置refPath
来设置动态的ref:
var userSchema = new Schema({
name: String,
connections: [{
kind: String,
item: { type: ObjectId, refPath: 'connections.kind' }
}]
});
只在_id中设置ref不是最好的选择,您还可以在virtuals中进行设置:
var PersonSchema = new Schema({
name: String,
band: String
});
var BandSchema = new Schema({
name: String
});
BandSchema.virtual('members', {
ref: 'Person', // The model to use
localField: 'name', // 找到本地字段 `localField`与对应model字段`foreignField`值相等的document
foreignField: 'band',
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false
});
注意toJSON()
不包含在virtuals 中,所以需要手动设置:
var BandSchema = new Schema({
name: String
}, { toJSON: { virtuals: true } });
然后使用,注意foreignField
必须包含在populate中:
Band.
find({}).
populate({ path: 'members', select: 'name band' }).
exec(function(error, bands) {
// Works, foreign field `band` is selected
});
您还可以在中间件中使用populate:
MySchema.pre('find', function() {
this.populate('user');
});
MySchema.post('find', async function(docs) {
for (let doc of docs) {
if (doc.isPublic) {
await doc.populate('user').execPopulate();
}
}
});
MySchema.post('save', function(doc, next) {
doc.populate('user').execPopulate(function() {
next();
});
});
Discriminators
Discriminators 是一个schema的继承机制,可以使得你拥有多个models使用了重叠的schema在同一个collection下面。例如你可以使用model.discriminator()
来整合一个基础的schema和一个Discriminator schema:
var options = {discriminatorKey: 'kind'};
var eventSchema = new mongoose.Schema({time: Date}, options);
var Event = mongoose.model('Event', eventSchema);
// ClickedLinkEvent is a special type of Event that has
// a URL.
var ClickedLinkEvent = Event.discriminator('ClickedLink',
new mongoose.Schema({url: String}, options));
// When you create a generic event, it can't have a URL field...
var genericEvent = new Event({time: Date.now(), url: 'google.com'});
assert.ok(!genericEvent.url);
// But a ClickedLinkEvent can
var clickedEvent =
new ClickedLinkEvent({time: Date.now(), url: 'google.com'});
mongoose分辨不同的Discriminators 是通过__t
来区分的,而且Discriminators 都可以很智能地通过find()
, count()
, aggregate()
来找到数量:
var event1 = new Event({time: Date.now()});
var event2 = new ClickedLinkEvent({time: Date.now(), url: 'google.com'});
var event3 = new SignedUpEvent({time: Date.now(), user: 'testuser'});
var save = function (doc, callback) {
doc.save(function (error, doc) {
callback(error, doc);
});
};
async.map([event1, event2, event3], save, function (error) {
ClickedLinkEvent.find({}, function (error, docs) {
assert.equal(docs.length, 1);
assert.equal(docs[0]._id.toString(), event2._id.toString());
assert.equal(docs[0].url, 'google.com');
});
});
Discriminators 可以使用基础的schema的中间件,但是你也可以为它添加自己的中间件而不影响基础的schema:
var options = {discriminatorKey: 'kind'};
var eventSchema = new mongoose.Schema({time: Date}, options);
var eventSchemaCalls = 0;
eventSchema.pre('validate', function (next) {
++eventSchemaCalls;
next();
});
var Event = mongoose.model('GenericEvent', eventSchema);
var clickedLinkSchema = new mongoose.Schema({url: String}, options);
var clickedSchemaCalls = 0;
clickedLinkSchema.pre('validate', function (next) {
++clickedSchemaCalls;
next();
});
var ClickedLinkEvent = Event.discriminator('ClickedLinkEvent',
clickedLinkSchema);
var event1 = new ClickedLinkEvent();
event1.validate(function() {
assert.equal(eventSchemaCalls, 1);
assert.equal(clickedSchemaCalls, 1);
var generic = new Event();
generic.validate(function() {
assert.equal(eventSchemaCalls, 2);
assert.equal(clickedSchemaCalls, 1);
});
});
当你使用Model.create()
自动创建时,mongoose会自动为您选择适合的Discriminator进行创建:
var Schema = mongoose.Schema;
var shapeSchema = new Schema({
name: String
}, { discriminatorKey: 'kind' });
var Shape = db.model('Shape', shapeSchema);
var Circle = Shape.discriminator('Circle',
new Schema({ radius: Number }));
var Square = Shape.discriminator('Square',
new Schema({ side: Number }));
var shapes = [
{ name: 'Test' },
{ kind: 'Circle', radius: 5 },
{ kind: 'Square', side: 10 }
];
Shape.create(shapes, function(error, shapes) {
assert.ifError(error);
assert.ok(shapes[0] instanceof Shape);
assert.ok(shapes[1] instanceof Circle);
assert.equal(shapes[1].radius, 5);
assert.ok(shapes[2] instanceof Square);
assert.equal(shapes[2].side, 10);
});
常用API
-
Model
Model.count()
找到符合条件的文档数量,已经废弃Model.countDocuments()
找到符合条件的文档数量Model.create()
创建一个或多个文档,MyModel.create(docs)
等同于多个new MyModel(doc).save()
Model.deleteMany()
删除多个Model.deleteOne()
删除一个Model.distinct()
查找所有文档中某个字段的不重复值,返回去重后的字段所有值Model.find()
Model.findById()
Model.findByIdAndDelete()
Model.findByIdAndRemove()
Model.findByIdAndUpdate()
Model.findOne()
Model.findOneAndDelete()
Model.findOneAndRemove()
Model.findOneAndUpdate()
Model.insertMany()
Model.populate()
Model.prototype.$where()
创建一个查询
Blog.$where('this.username.indexOf("val") !== -1').exec();
Model.prototype.remove()
Model.prototype.save()
Model.remove()
Model.replaceOne()
Model.update()
更新文档但是不返回该数据Model.updateMany()
Model.updateOne()
Model.where()
查询
User.where('age').gte(21).lte(65).exec(callback);
-
Document
Document.prototype.$ignore()
不允许验证器,不保留改动某个字段Document.prototype.$isDefault()
判断某个字段是否设置默认值Document.prototype.$isDeleted()
判断是否移除Document.prototype.depopulate()
进行populate的逆运算Document.prototype.get()
取值Document.prototype.invalidate()
将字段标记为不合格,触发验证失败Document.prototype.populate()
整合refDocument.prototype.populated()
返回整合的ref的_id
Document.prototype.save()
保存Document.prototype.set()
设置值Document.prototype.toJSON()
转换为jsonDocument.prototype.toObject()
转换为plainObjectDocument.prototype.update()
更新文档Document.prototype.validate()
执行验证器Document.prototype.validateSync()
同步执行验证器
-
Query
Query.prototype.$where()
执行一个函数或表达式查询
query.$where('this.comments.length === 10 || this.name.length === 5')
// or
query.$where(function () {
return this.comments.length === 10 || this.name.length === 5;
})
Query.prototype.countDocuments()
查询数量Query.prototype.deleteMany()
Query.prototype.deleteOne()
Query.prototype.distinct()
Query.prototype.elemMatch()
嵌套查询使用,替代find查询嵌套字段写path.path
Query.prototype.equals()
查询时候值等于Query.prototype.exec()
执行查询Query.prototype.exists()
判断是否存在某个字段Query.prototype.find()
Query.prototype.findOne()
Query.prototype.findOneAndDelete()
Query.prototype.findOneAndRemove()
Query.prototype.findOneAndUpdate()
Query.prototype.getQuery()
返回查询条件,使用json格式表示出来Query.prototype.getUpdate()
返回更新条件,使用json格式表示Query.prototype.gt()
Query.prototype.gte()
Query.prototype.lean()
查询后的结果如果使用了lean将不再有save等doc的方法Query.prototype.limit()
Query.prototype.lt()
Query.prototype.lte()
Query.prototype.nor()
都不是
query.nor([{ color: 'green' }, { status: 'ok' }])
Query.prototype.or()
两个中一个
query.or([{ color: 'red' }, { status: 'emergency' }])
Query.prototype.populate()
Query.prototype.regex()
正则查询Query.prototype.remove()
Query.prototype.select()
选择需要展示或者排除的字段,如果是字符可以用+
表示包含,-
表示排除,对象可以用1
表示包含,0
表示排除
query.select('a b');
query.select('-c -d');
query.select({ a: 1, b: 1 });
query.select({ c: 0, d: 0 });
Query.prototype.set()
Query.prototype.setOptions()
Query.prototype.skip()
Query.prototype.slice()
Query.prototype.sort()
排序,当你使用object时,可以指定asc
,desc
,ascending
,descending
,1
, and-1
,如果是字符串,默认是升序,使用-
来降序:
// sort by "field" ascending and "test" descending
query.sort({ field: 'asc', test: -1 });
// equivalent
query.sort('field -test');
Query.prototype.then()
Query.prototype.update()
Query.prototype.updateMany()
Query.prototype.updateOne()
Query.prototype.where()
实例
分页:
model.find(queryCondition).limit(limit).skip(limit*page).exec(function(err, results){
...
});