Node.js的mongodb驱动Mongoose(二)

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` 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 来事项类似联表的功能

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


Middleware中间件

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

  • init
  • validate
  • save
  • remove

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值