Nodejs学习笔记web前端基础Nodejs

mongoose学习笔记2:Schame

2018-11-12  本文已影响6人  8b9a7eb5887b

当阅读这篇文章时,如果你对 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 可以是以下的值:

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

静态方法

为 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' }

下篇再续...

上一篇下一篇

猜你喜欢

热点阅读