Schemas

作者: itvwork | 来源:发表于2019-04-30 18:07 被阅读0次

    定义一个 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方法
    代码中定义的每个字段属性都会转换成SchemaType.例如我们定了title字段,它将被转换成String 的SchemaType 并且字段date将换成Date SchemaType
    字段的设置可以嵌套,像JOSN格式一样,比如meta字段

    允许定的SchemaTypes 有:

    查看更多SchemaTypes 点击这里.
    Schemas不仅可以定义表的结构和属性,还能定义了文档实例方法静态模型方法复合索引中间件.。

    创建Model

    创建一个Model,我们必须要用到Schema,我们使用上面的blogSchema 创建一个Model,使用mongoose.model(modelName,blogSchema)

    var Blog = mongoose.model('Blog', blogSchema);
      // 继续……
    

    实例方法

    Model是一个文档结构,有许多内置的方法。我们也可以自定义实例方法

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

    现在所有animalSchema的实例都有一个findSimilarTypes的方法

     var Animal = mongoose.model('Animal', animalSchema);
     var dog = new Animal({ type: 'dog' });
    
      dog.findSimilarTypes(function(err, dogs) {
        console.log(dogs); //查找 type是dog的表
      });
    
    • 如果重写Mongoose的文档内置方法可能会造成不可预料的错,点击这里查看更多详情

    • 上面的实例使用了Schema.methods对创建保存一个实例方法,点击这里查看Schema.method()的详情细介绍

    • 不要使用es6的箭头方法,关于es6简头方法的使用请自行查找

    静态方法

    向model添加一个静态方法也非常简单,我们继使用animalSchema:

      // 添加静态方法
      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);
      });
    

    查询助手

    你可以定义查询助手方法,类拟实例方法,但是主要是用于mongoose的查询。查询助手方法允许可以链式使用,例如:

     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在Schema 定义这些索引,可以使用以下两种方法:

    //在Schema直接定义
    var animalSchema = new Schema({
        name: String,
        type: String,
        tags: { type: [String], index: true }
      });
    
    //在Schema对象方法中添加  
    animalSchema.index({ name: 1, type: -1 });
    

    当你的应用开始运行时,Mongoose会为Schema中定义的每一个索引自动创建索引,这个创建是依次的进行的,不是同时的时的。当所有索引创建完成时或出错误会在model上发送一个Index事件。虽然对开发者来说创建索引有利于开发,但是生产建不要使用索引,因为会对性能造成影响。可以禁用些功能。使用如下代码

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

    开发时使用索引,可以使用以下方式监听Index事件

    
      animalSchema.index({ _id: 1 }, { sparse: true });
      var Animal = mongoose.model('Animal', animalSchema);
    
      Animal.on('index', function(error) {
        console.log(error.message);
      });
    

    详细请查看 Model#ensureIndexes 方法。

    虚拟(Virtuals)

    Virtuals能获取和设置的文档属性,但不能持久化到MongoDB。getter用于格式化或组合字段,而setter用于将单个值分解为多个值进行存储。

      var personSchema = new Schema({
        name: {
          first: String,
          last: String
        }
      });
    
      var Person = mongoose.model('Person', personSchema);
    
      var axl = new Person({
        name: { first: 'Axl', last: 'Rose' }
      });
    

    你可以这样使用属性

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

    每次使用上面将name.firstname.last链接起来非常麻烦,如果你想对name做一些特殊的处理,你可以使用Virtuals的get来处理。虚拟属性的getter允许您定义一个不会持久化到MongoDB的fullName属性。

    personSchema.virtual('fullName').get(function () {
      return this.name.first + ' ' + this.name.last;
    });
    
    //使用时,跟es6的get set有点相似
    console.log(axl.fullName); 
    

    如果你使用toJSON()toObject()时,mongoose默认没有包含虚拟属性的;这包括在Mongoose文档上调用JSON.stringify()的输出,因为JSON.stringify()调用toJSON()。将{virtuals: true}传递给toObject()toJSON()

    你还可以为fullName设置一个set方法

    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"
    

    在其他验证之前应用虚拟属性设置器。因此,即使需要姓和名字段,上面的示例仍然有效。虚拟属性不能作为字段查询条件,因为数据库不存在该字段的

    别名(Aliases)

    别名是一种特殊的虚拟类型,getter和setter在其中无缝地获取和设置另一个属性。这对于节省网络带宽非常方便,因此可以将存储在数据库中的短属性名称转换为更长的名称,以提高代码的可读性。

    var personSchema = new Schema({
      n: {
        type: String,
        // Now accessing `name` will get you the value of `n`, and setting `n` will set the value of `name`
        alias: 'name'
      }
    });
    
    // Setting `name` will propagate to `n`
    var person = new Person({ name: 'Val' });
    console.log(person); // { n: 'Val' }
    console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }
    console.log(person.name); // "Val"
    
    person.name = 'Not Val';
    console.log(person); // { n: 'Not Val' }
    

    还可以在嵌套路径上声明别名。使用嵌套模式和子文档更容易,但是也可以内联声明嵌套路径别名,只要使用完整的嵌套路径嵌套即可

    const childSchema = new Schema({
      n: {
        type: String,
        alias: 'name'
      }
    }, { _id: false });
    
    const parentSchema = new Schema({
      // If in a child schema, alias doesn't need to include the full nested path
      c: childSchema,
      name: {
        f: {
          type: String,
          // Alias needs to include the full nested path if declared inline
          alias: 'name.first'
        }
      }
    });
    

    选项

    Schemas中有些一些配置选项,可直传给构造方法,也可以用set设置,如下:

    new Schema({..}, options);
    
    // 或
    
    var schema = new Schema({..});
    schema.set(option, value);
    

    可设置的选项目有:

    option: autoIndex

    这个是否创建索引,默认情况下会开启这一选项,在开发和测试的时候还是很有用的。但在生产会产生比较大的负载,生产上建议关闭此选项,关闭方式如下:

    const schema = new Schema({..}, { autoIndex: false });
    const Clock = mongoose.model('Clock', schema);
    Clock.ensureIndexes(callback);
    

    这个选项默认是开启的,你可以关闭,使用mongoose.use('autoIndex', false);

    option: autoCreate

    在Mongoose构建索引之前,如果autoCreate设置为true,它调用Model.createCollection()在MongoDB中创建底层集合。调用createCollection()根据collation选项设置集合的默认排序规则,如果设置了capped模式选项,则将表建立为一个有上限的表。与autoIndex一样,将autoCreate设置为true有助于开发和测试环境。
    不幸的是,createCollection()不能更改现有集合。例如,如果您将capped: 1024添加到您的模式中,而现有的集合没有被覆盖,createCollection()将抛出一个错误。通常,对于生产环境,autoCreate应该为false。

    option: bufferCommands

    这个选项主是当mongoose 中断了链接会自动重新链接,直到驱动程序重新连接为止,禁用请设置为false

    var schema = new Schema({..}, { bufferCommands: false });
    //全局设置
    mongoose.set('bufferCommands', true);
    

    option: capped

    设置Mongodb表大小,单字为字节

    new Schema({..}, { capped: 1024 });
    

    如查你要对该属性,设置maxautoIndexId属性,那么你可以传一个对象,但该对象一定要包含size属性

    new Schema({..}, { capped: { size: 1024, max: 1000, autoIndexId: true } });
    

    option: collection

    Mongodb表名,默认会生一个表名,但是最后此项不要用默认,最好给表设个名字

    var dataSchema = new Schema({..}, { collection: 'data' });
    

    option: id

    是否给Schema添加一个虚拟id

    // 默认情况下
    var schema = new Schema({ name: String });
    var Page = mongoose.model('Page', schema);
    var p = new Page({ name: 'mongodb.org' });
    console.log(p); // { _id: '50341373e894ad16347efe01', name: 'mongodb.org' }
    
    // 禁用_id
    var childSchema = new Schema({ name: String }, { _id: false });
    var parentSchema = new Schema({ children: [childSchema] });
    
    var Model = mongoose.model('Model', parentSchema);
    
    Model.create({ children: [{ name: 'Luke' }] }, function(error, doc) {
      // doc.children[0]._id will be undefined
    });
    

    可存数据的时候没有_id是没无保存数据的,最好不要禁用此功能

    option: minimize

    是否删除空对象,以此来减小表所占的大小

    const schema = new Schema({ name: String, inventory: {} });
    const Character = mongoose.model('Character', schema);
    
    // 如果“inventory”字段不是空的,将会存储它
    const frodo = new Character({ name: 'Frodo', inventory: { ringOfPower: 1 }});
    await frodo.save();
    let doc = await Character.findOne({ name: 'Frodo' }).lean();
    doc.inventory; // { ringOfPower: 1 }
    
    // 如果“inventory”字段是空的,将不会存储它
    const sam = new Character({ name: 'Sam', inventory: {}});
    await sam.save();
    doc = await Character.findOne({ name: 'Sam' }).lean();
    doc.inventory; // undefined
    

    可以通过以下代码设置minimize属

    const schema = new Schema({ name: String, inventory: {} }, { minimize: false });
    const Character = mongoose.model('Character', schema);
    
    // 如果nventory是空对象时,会存个空对象进去
    const sam = new Character({ name: 'Sam', inventory: {} });
    await sam.save();
    doc = await Character.findOne({ name: 'Sam' }).lean();
    doc.inventory; // {}
    

    判断对象是否是空对象可以使用$isEmpty()

    const sam = new Character({ name: 'Sam', inventory: {} });
    sam.$isEmpty('inventory'); // true
    
    sam.inventory.barrowBlade = 1;
    sam.$isEmpty('inventory'); // false
    

    option: read(暂时还不是很明白此项目的作用,后期弄懂再补)

    这个选项是给Schema起一个别名,配合### readConcern()使用

    var schema = new Schema({..}, { read: 'primary' });            // 可以用别名 'p'
    var schema = new Schema({..}, { read: 'primaryPreferred' });   // 可以用别名 'pp'
    var schema = new Schema({..}, { read: 'secondary' });          // 可以用别名 's'
    var schema = new Schema({..}, { read: 'secondaryPreferred' }); // 可以用别名 'sp'
    var schema = new Schema({..}, { read: 'nearest' });            // 可以用别名 'n'
    

    option: writeConcern

    设置mongodb写入策略(WriteConcern),具体说明请自行查找mongodb

    const schema = new Schema({ name: String }, {
      writeConcern: {
        w: 'majority',
        j: true,
        wtimeout: 1000
      }
    });
    

    option: safe

    safe是一个与writeConcern类似的遗留选项。特别是,safe: false是未确认的写入writeConcern: {w: 0}的缩写。而是使用writeConcern选项。

    option: shardKey

    设置分片的时候用到;分片必须自行配置,详情请自行查看mongodb文档

    new Schema({ .. }, { shardKey: { tag: 1, name: 1 }})
    

    option: strict(严格模式)

    这个选是默认开启的,开启这个选项,如果你的Schema没有这个指定的字段,那么mongoose将不保存到数据库中(建议开启这个选项)

    var thingSchema = new Schema({..})
    var Thing = mongoose.model('Thing', thingSchema);
    var thing = new Thing({ iAmNotInTheSchema: true });
    thing.save(); // 如查开启将不会保存到数据库
    
    // set to false..
    var thingSchema = new Schema({..}, { strict: false });
    var thing = new Thing({ iAmNotInTheSchema: true });
    thing.save(); // 关闭会保存数据到数据库
    

    也可以通过以下方式开启/关闭strict

    var Thing = mongoose.model('Thing');
    var thing = new Thing(doc, true);  // 第二个参数就strict的值,strict设为true
    var thing = new Thing(doc, false); // 第二个参数就strict的值,strict设为false
    

    可以将“strict”设置为“throw”,抛出错误会,而不是过滤没有的数据
    *注意,如查实例不存在的话,你怎么样设置这个参数都是不起作用的

    var thingSchema = new Schema({..})
    var Thing = mongoose.model('Thing', thingSchema);
    var thing = new Thing;
    thing.iAmNotInTheSchema = true;
    thing.save(); // iAmNotInTheSchema  数据不会被保存
    

    字段不向后兼容,strict选项不适用查询时过滤在Schema不存在的数据

    const mySchema = new Schema({ field: Number }, { strict: true });
    const MyModel = mongoose.model('Test', mySchema);
    
    // mongoose不会过滤notInSchema,因为strict对于查询是不起作用的
    MyModel.find({ notInSchema: 1 });
    

    strict选项支持更新数据

    MyModel.updateMany({}, { $set: { notInSchema: 1 } });
    

    option: strictQuery

    该字段与上文的strict相对应,这个字段是在查询的时候过滤Schema没有的字段查询数据

    const mySchema = new Schema({ field: Number }, {
      strict: true,
      strictQuery: true // 打开查询过滤模式(严格模式)
    });
    const MyModel = mongoose.model('Test', mySchema);
    
    // Mongoose会过滤掉'{ notInSchema: 1 }'因已经打开查询过滤模式,此条件无效
    MyModel.find({ notInSchema: 1 });
    

    option: toJSON

    与toObject选项完全相同,但仅在调用documents 对象的toJSON方法时才有用(实话说,没什么大卵用)

    var schema = new Schema({ name: String });
    schema.path('name').get(function (v) {
      return v + ' is my name';
    });
    schema.set('toJSON', { getters: true, virtuals: false });
    var M = mongoose.model('Person', schema);
    var m = new M({ name: 'Max Headroom' });
    console.log(m.toObject()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom' }
    console.log(m.toJSON()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }
    // 只要js对象被转为字符串,就会调用toJSON
    console.log(JSON.stringify(m)); // { "_id": "504e0cd7dd992d9be2f20b6f", "name": "Max Headroom is my name" }
    

    option: toObject

    documents 有一个toObject方法,它将mongoose文档转换为一个普通的javascript对象。此方法接受几个选项。我们可以在这里声明这些选项,并在缺省情况下将其应用于所有这些模式文档,而不是根据每个文档应用这些选项
    要让所有virtuals对象都显示在console.log输出中,请将toObject选项设置为{getters: true}:

    var schema = new Schema({ name: String });
    schema.path('name').get(function (v) {
      return v + ' is my name';
    });
    schema.set('toObject', { getters: true });
    var M = mongoose.model('Person', schema);
    var m = new M({ name: 'Max Headroom' });
    console.log(m); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }
    

    option: typeKey

    在默认的情况下,如果你在Schema设置了一个字段,mongoose将认为你这是类型声明

    // Mongoose 将认为loc是一个字符串
    var schema = new Schema({ loc: { type: String, coordinates: [Number] } });
    

    如果你需要type是一个字段,而不是作为字段声明的类型,那么你可以设置这个参数

    var schema = new Schema({
      // Mongoose将认为loc是一个对象,而不是一个字符串
      loc: { type: String, coordinates: [Number] },
      // Mongoose将认为name是一个字符串,而不是一个对象
      name: { $type: String }
    }, { typeKey: '$type' }); // 用$type替换type
    

    option: validateBeforeSave

    默认情况下,文档在保存到数据库之前会自动验证。这是为了防止保存无效的数据。如果希望手动处理验证,并能够保存没有通过验证的数据,可以将validatebeforeave设置为false。

    var schema = new Schema({ name: String });
    schema.set('validateBeforeSave', false);
    schema.path('name').validate(function (value) {
        return v != null;
    });
    var M = mongoose.model('Person', schema);
    var m = new M({ name: null });
    m.validate(function(err) {
        console.log(err); // 不允许为空
    });
    m.save(); // 成功保存
    

    option: versionKey

    versionKey,在你最开始创建文档插入数据时已经创建了,一个记更改,添加的版本号,默认的字段是__v,如果字段跟你的业务逻辑有冲突,你可以像下面一样写

    const schema = new Schema({ name: 'string' });
    const Thing = mongoose.model('Thing', schema);
    const thing = new Thing({ name: 'mongoose v3' });
    await thing.save(); // { __v: 0, name: 'mongoose v3' }
    
    // customized versionKey
    new Schema({..}, { versionKey: '_somethingElse' })
    const Thing = mongoose.model('Thing', schema);
    const thing = new Thing({ name: 'mongoose v3' });
    thing.save(); // { _somethingElse: 0, name: 'mongoose v3' }
    

    版本号控制不是一个很可靠的解决并发的方案.
    Note that Mongoose versioning is not a full optimistic concurrency solution. Use mongoose-update-if-currentfor OCC support. Mongoose versioning only operates on arrays:

    还没有写完,待更新……

    相关文章

      网友评论

          本文标题:Schemas

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