mongoose学习笔记2:Schame

作者: 8b9a7eb5887b | 来源:发表于2018-11-12 20:44 被阅读6次

    当阅读这篇文章时,如果你对 Mongoose 还不了解,那么建议你花上一点时间阅读一下 Quick Start 或者阅读我写的上一篇文章:mongoose学习笔记1:起步 ,以了解 Mongoose 的基本运作机制。如果你正从 4.x 版本过渡到 5.x 版本,那么也建议你花上一点时间阅读 migration guide

    定义 Schema

    在 Mongoose 中,一切事物都起源于一个 Schema 。每一个 Schema 对应 MongoDB 中的一个集合,并定义了这个集合由哪些形式的字段组成。

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

    在 Schema 实例创建后,你可以通过使用 Schema 实例的 add 方法,为 Schema 添加一个字段信息。

    blogSchema.add({ abstract: String });
    

    blogSchema 中的每一个键都定义了我们文档中的一个属性,该属性将被转换为与之对应的 SchemaType 。在这个例子中,我们定义了一个 String 类型的 title ,一个 Date 类型的 date 以及含有键值对的一个 Object 类型的 meta 。

    SchemaType 可以是以下的值:

    • String
    • Number
    • Date
    • Buffer
    • Boolean
    • Mixed
    • ObjectId
    • Array
    • Decimal128
    • Map

    Schema 不只是定义了 Model 的结构和属性,同时还定义了 Model 的实例方法、静态方法、复合索引以及 Model 的生命周期钩子(也称作 中间件 )。

    创建 Model

    要使用我们定义好的 blogSchema ,我们需要将其编译为我们可以使用的 Model 。你只需要将 blogSchema 传递给 mongoose.model 就可以:

    var Blog = mongoose.model('Blog', blogSchema);
    

    实例方法

    每一个 Model 实例都是一个文档,这些文档有着他们原生的实例方法,当然我们也可以为这些文档定制我们的想要的实例方法:

    // 定义一个 Schema
    var animalSchema = new Schema({ name: String, type: String });
    
    // 为 animalSchema 的 methods 对象添加一个方法
    animalSchema.methods.findSimilarTypes = function(cb) {
      return this.model('Animal').find({ type: this.type }, cb);
    };
    

    现在,每一个 animal 实例都将带有一个可以直接调用的 findSimilarTypes 方法。

    var Animal = mongoose.model('Animal', animalSchema);
    var dog = new Animal({ type: 'dog' });
    
    dog.findSimilarTypes(function(err, dogs) {
      console.log(dogs); // woof
    });
    
    • 覆盖一个默认的 mongoose 实例方法可能会导致出现不可预知的后果,有关详细信息,可以点击这里查看。
    • 上面的实例直接将实例方法定义在 Schema 的 methods 属性上,你也可以通过调用 Schema 的 method 方法实现同样的效果。
    • 不用使用ES6的箭头函数,否则你定义的方法将无法访问 Model 实例,上面的实例也就不起作用了。

    静态方法

    为 Model 添加一个静态方法也是很简单的,我们还是以 animalSchema 为例。

    // 将静态方法定义在 Schema 的 statics 对象上
    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);
    });
    

    同样的,不要使用ES6的箭头函数来定义静态方法。

    查询助手

    你也可以为 Model 实例添加查询助手,他们看起来有点像实例方法,但不同的是他们用于 Model 查询。查询助手可以让你扩展 mongoose 的链式查询 API 。

    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);
    });
    
    Animal.findOne().byName('fido').exec(function(err, animal) {
      console.log(animal);
    });
    

    索引

    MongoDB支持辅助索引。在 mongoose 中,可以在 路径级别模式级别 中定义这些索引。创建复合索引时,推荐在模式级别中定义。

    var animalSchema = new Schema({
      name: String,
      type: String,
      tags: { type: [String], index: true }  // 路径级别
    });
    
    animalSchema.index({ name: 1, type: -1 });  // 模式级别
    

    当你的程序启动时,Mongoose 将会自动调用 Schema 中定义的每一个索引的 createIndex 方法。mongoose 会依次调用每个索引的 createIndex ,并在所有的 createIndex 调用完成后,在 Model 上触发一个 "index" 事件。虽然创建索引对于开发来说很方便,但建议在生产环境中禁用这种行为,因为索引的创建可能会对性能产生重大影响。通过将 Schema 的 autoIndex 设置为 false 来禁用该行为,或者通过将 autoIndex 选项 设置为 false 来全局禁用索引。

    mongoose.connect('mongodb://user:pass@localhost:port/database', { autoIndex: false });
    // or
    mongoose.createConnection('mongodb://user:pass@localhost:port/database', { autoIndex: false });
    // or
    animalSchema.set('autoIndex', false);
    // or
    new Schema({..}, { autoIndex: false });
    

    当所有的 createIndex 都调用成功 或 出现报错时,mongoose 将会在 Model 上触发一个 "index" 事件。

    // 以下代码将导致报错,原因是 MongoDB 的 _id 索引默认
    // sparse 为 false
    animalSchema.index({ _id: 1 }, { sparse: true });
    var Animal = mongoose.model('Animal', animalSchema);
    
    Animal.on('index', function(error) {
      // "_id index cannot be sparse"
      console.log(error.message);
    });
    

    虚拟属性Virtuals

    虚拟属性是可读写的 Model 的属性,但它不会被保存到 MongoDB 中。你可以通过 虚拟属性的 getter 格式化或组合多个字段,通过 setter 将单个值分解为多个值来进行存储。

    // 定义一个 Schema
    var personSchema = new Schema({
      name: {
        first: String,
        last: String
      }
    });
    
    // 编译成 model
    var Person = mongoose.model('Person', personSchema);
    
    // 创建一个 model 实例
    var axl = new Person({
      name: { first: 'Axl', last: 'Rose' }
    });
    

    当你需要打印 axl 的全名是,你需要这样:

    console.log(axl.name.first + ' ' + axl.name.last); // Axl Rose
    

    但如果每次都需要连接名字和姓氏则会显得很麻烦。如果你想对名字进行一些额外的处理,比如全名需要会是怎么样呢?虚拟属性的 getter 可以让你定义不会被保存到 MongoDB 中的全名属性。

    personSchema.virtual('fullName').get(function () {
      return this.name.first + ' ' + this.name.last;
    });
    

    现在,每次当你获取 fullName 的值时,mongoose 将会调用你定义的 getter 方法。

    console.log(axl.fullName); // Axl Rose
    

    默认情况下,当你调用 toJSON()toObject() 时,mongoose 将不会带上虚拟属性(除非你向这两个方法传入参数:{ virtuals: true }),这也包括当你调用 JSON.stringify() 方法时,也不会带有虚拟属性。

    你还可以设置一个自定义的 setter ,它将允许你通过设置全名,一次性设置姓名和姓氏的值。

    personSchema.virtual('fullName').
      get(function() { return this.name.first + ' ' + this.name.last; }).
      set(function(v) {
        this.name.first = v.substr(0, v.indexOf(' '));
        this.name.last = v.substr(v.indexOf(' ') + 1);
      });
    
    axl.fullName = 'William Rose';  // 现在 axl.name.first 的值为 William
    

    由于虚拟属性并没有被存储在 MongoDB 中,因此你不能使用虚拟属性来进行查询操作。

    别名 Aliases

    别名是一种特殊的虚拟属性,可以无缝地获取或者设置另一个属性的值。你可以将存储在 MongoDB 中的短属性名转换为更长的名称,这对于节省网络宽带是非常方便的。

    var personSchema = new Schema({
      n: {
        type: String,
        // 定义一个别名
        alias: 'name'
      }
    });
    
    var person = new Person({ name: 'Val' });
    
    console.log(person); // { n: 'Val' }
    console.log(person.name); // 'Val'
    console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }
    

    下篇再续...

    相关文章

      网友评论

        本文标题:mongoose学习笔记2:Schame

        本文链接:https://www.haomeiwen.com/subject/zpicfqtx.html