Node.js的mongodb驱动Mongoose(二)

2017-10-26 20:05:56来源:CSDN作者:WuLex人点击

分享

Documents文档

Mongoose文档

Retrieving检索

Mongoose检索方法有很多种, 详情可以阅读Querying章节

#

Updating更新

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验证

文档在保存之前往往会先经过验证,详情可以阅读 Validating 章节

Sub Docs子文档

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);

Queries查询

Mongoose可以通过models的一些静态方法来检索文档
当执行一个模型函数时,通常有两种结果:

  • passed通过:这样回调函数会返回查询结果
  • not passed未通过:这样查询会被驳回

在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` fieldsPerson.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` fieldsquery.select('name occupation');// execute the query at a later timequery.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 docPerson.  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 builderPerson.  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 来事项类似联表的功能

Streaming

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验证

在开始学习Validation之前,请先记住以下规则:

  • Validation是在 SchamaType 中定义的
  • Validation是中间件。Mongoose默认将 Validation 作为 pre(‘save’) 注册。
  • 可以手动使用 doc.validate(callback) 或 doc.validateSync() 运行 Validation
  • Validator(验证器)不会运行在未定义的值上面。除非是用 require 引用 验证器
  • Validation是异步的;当调用 Model 的 save()时,子文档的 Validation 也会一起执行,同时,子文档的报错也会被父文档捕获到
  • 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.');    });

Buid-in Validators内置验证器

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?');

Custom Validators自定义验证器

如果内置验证器不能满足需求,你也可以定制个性化的验证器。
自定义验证使用函数式声明。详细和可参考官方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);

Async Custom Validators异步自定义验证器

自定义验证器也可以是异步的。如果验证器带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 Errors验证器错误信息

当验证器执行失败时就会产生错误信息。每个 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');    });

Required Validators On Nested Objects嵌套对象require啊验证器

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 schemavar 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更新验证器


Middleware中间件

Mongoose4有两种类型的中间件:document文档中间件 和 query查询中间件
Document文档中间件支持一下函数:

  • init
  • validate
  • save
  • remove

Query查询中间件支持一下函数:

  • count
  • find
  • findOne
  • findOneAndRemove
  • findOneAndUpdate
  • insertMany
  • update

Document中间件 和 query中间件都支持 pre 和 post 钩子。

微信扫一扫

第七城市微信公众平台