Mongoose文档
Mongoose检索方法有很多种, 详情可以阅读Querying章节
Mongoose更新文档的方法有很多种, findById是一个比较传统的方法
Tank.findById(id, function (err, tank) {
if (err) return handleError(err);
tank.size = 'large';
tank.save(function (err, updatedTank) {
if (err) return handleError(err);
res.send(updatedTank);
});
});
findById先从MongoDB检索数据,再用save方法保存修改后的数据。不过,有时候我们不需要将数据返回到应用,仅仅是想在数据库中更新,这时候就可以用
Tank.update({ _id: id }, { $set: { size: 'large' }}, callback);
如果想把文档返回到应用中,还有一个更好的方法
Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true }, function (err, tank) {
if (err) return handleError(err);
res.send(tank);
});
文档在保存之前往往会先经过验证,详情可以阅读 Validating 章节
Sub-documents嵌套在父文档里,而且有自己的Schema模式
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
children: [childSchema]
})
Sub-documents继承普通文档的所有特性,唯一不同的是,Sub-documents的保存要依赖于父文档。
var Parent = mongoose.model('Parent', parentSchema);
var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] })
parent.children[0].name = 'Matthew';
parent.save(callback);
如果子文档的中间件出错,报错会发生在父文档的 save() 函数中
childSchema.pre('save', function (next) {
if ('invalid' == this.name) return next(new Error('#sadpanda'));
next();
});
var parent = new Parent({ children: [{ name: 'invalid' }] });
parent.save(function (err) {
console.log(err.message) // #sadpanda
})
每一个文档都有一个唯一 _id,通过唯一的 _id 可以找到子文档
var doc = parent.children.id(_id);
Mongoose可以通过models的一些静态方法来检索文档
当执行一个模型函数时,通常有两种结果:
在Mongoose 4版本中,为查询提供了 .then() 函数,这样就可以像 promise 一样执行函数
当执行一个带回调函数的查询时, 你需要提供JSON格式的查询条
var Person = mongoose.model('Person', yourSchema);
// find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) {
if (err) return handleError(err);
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
})
在上面的例子中,查询结果 person 通过回调函数返回。在Mongoose中,所有的回调函数使用固定的格式: callback(error, result)。当在执行查询时发生了错误,将只返回错误参数不返回查询结果,,同样的,如果查询成功执行,就不会有错误信息。
当查询成功时,回调函数按固定格式:callback(error, result)返回结果。 具体返回什么样的数据,取决于查询的方式。
+ findOne(): 返回单个文档数据
+ find(): 返回一个文档数组
+ count(): 返回文档数量
+ update(): 返回更新的文档
现在,来看看没有回调函数的情况:
// find each person with a last name matching 'Ghost'
var query = Person.findOne({ 'name.last': 'Ghost' });
// selecting the `name` and `occupation` fields
query.select('name occupation');
// execute the query at a later time
query.exec(function (err, person) {
if (err) return handleError(err);
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host.
})
在上面的在代码中,使用一个查询变量query。Mongoose中,除了用JSON格式的条件语句,还可以使用链式函数来查询文档,比如下面2个例子:
// 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);
// 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);
详情可以阅读详细的API文档
MongoDB本身没有联表,但是有时会需要引用其他的文档。这时候就可以用 population 来事项类似联表的功能
You can stream query results from MongoDB. You need to call the Query#cursor() function instead of Query#exec to return an instance of QueryCursor.
var cursor = Person.find({ occupation: /host/ }).cursor();
cursor.on('data', function(doc) {
// Called once for every document
});
cursor.on('close', function() {
// Called when done
});
在开始学习Validation之前,请先记住以下规则:
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.');
});
Mongoose有几个内置验证器
+ 所有的 SchemaType 都内置 require 验证器。require 验证器使用 checkRequired() 函数判断参数的值是否正确
+ Number数值 有 min 和 max 验证器
+ String字符串 有 enum,match,maxlength 和 minlength 验证器
Each of the validator links above provide more information about how to enable them and customize their error messages.
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']
}
});
var Breakfast = db.model('Breakfast', breakfastSchema);
var badBreakfast = new Breakfast({
eggs: 2,
bacon: 0,
drink: 'Milk'
});
var error = badBreakfast.validateSync();
assert.equal(error.errors['eggs'].message,
'Too few eggs');
assert.ok(!error.errors['bacon']);
assert.equal(error.errors['drink'].message,
'`Milk` is not a valid enum value for path `drink`.');
badBreakfast.bacon = null;
error = badBreakfast.validateSync();
assert.equal(error.errors['bacon'].message, 'Why no bacon?');
如果内置验证器不能满足需求,你也可以定制个性化的验证器。
自定义验证使用函数式声明。详细和可参考官方API文档
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);
自定义验证器也可以是异步的。如果验证器带2个参数,Mongoose会假定第二个参数是回调函数。即使你不想用异步回调函数,也要谨慎, 因为Mongoose 4会假定所有带2个参数的的回调函数都是异步的。
var userSchema = new Schema({
phone: {
type: String,
validate: {
validator: function(v, cb) {
setTimeout(function() {
cb(/\d{3}-\d{3}-\d{4}/.test(v));
}, 5);
},
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';
user.validate(function(error) {
assert.ok(error);
assert.equal(error.errors['phone'].message,
'555.0123 is not a valid phone number!');
});
当验证器执行失败时就会产生错误信息。每个 Validation Error有 kind, path, value 和 message 属性
var toySchema = new Schema({
color: String,
name: String
});
var Toy = db.model('Toy', toySchema);
var validator = function (value) {
return /blue|green|white|red|orange|periwinkle/i.test(value);
};
Toy.schema.path('color').validate(validator,
'Color `{VALUE}` not valid', 'Invalid color');
var toy = new Toy({ color: 'grease'});
toy.save(function (err) {
// err is our ValidationError object
// err.errors.color is a ValidatorError object
assert.equal(err.errors.color.message, 'Color `grease` not valid');
assert.equal(err.errors.color.kind, 'Invalid color');
assert.equal(err.errors.color.path, 'color');
assert.equal(err.errors.color.value, 'grease');
assert.equal(err.name, 'ValidationError');
});
Mongoose嵌套对象定义 验证器 有点棘手,因为嵌套对象没有完整的路径
“`
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
assert.throws(function() {
// This throws an error, because 'name' isn't a full fledged path
personSchema.path('name').required(true);
}, /Cannot.*'required'/);
// To make a nested object required, use a single nested schema
var nameSchema = new Schema({
first: String,
last: String
});
personSchema = new Schema({
name: {
type: nameSchema,
required: true
}
});
var Person = db.model('Person', personSchema);
var person = new Person();
var error = person.validateSync();
assert.ok(error.errors['name']);
“`
###Update Validators更新验证器
Mongoose4有两种类型的中间件:document文档中间件 和 query查询中间件
Document文档中间件支持一下函数:
Query查询中间件支持一下函数:
Document中间件 和 query中间件都支持 pre 和 post 钩子。