美文网首页mongoDBmongoose
mongoose学习笔记2之Schemas

mongoose学习笔记2之Schemas

作者: 打铁大师 | 来源:发表于2017-01-14 19:42 被阅读913次

    指南

    假设以下代码都运行在

    let mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost:27017/test');
    let db = mongoose.connection;
    db.on('error', () => {
      console.error('链接失败');
    });
    db.once('open', function () {
      //下面讲到的所有代码都在这里运行
    });
    

    Schema

    定义 schema

    在Mongoose中,任何事情都是从Schema(模式)开始的。每一个模式映射到MongoDB集合,并且定义了这个集合中文档的模型。

    let Schema = mongoose.Schema;
    let blogSchema = new Schema({
        title: String,
        author: String,
        body: String,
        comments: [{
            body: String,
            data: Date
        }],
        date: {
            type: Date, default: Date.now
        },
        hidden: Boolean,
        meta: {
            votes: Number,
            favs: Number
        }
    });
    

    在blogSchema中,我们定义的每一个key都被关联了一个SchemaType。比如title这个key,对应一个String SchemaType。date对应一个Date SchemaType。当然,这些key也可以对应一个对象,进行更深度的嵌套。

    SchemaTypes 有以下几种:

    • String
    • Number
    • Date
    • Buffer
    • Mixed
    • ObjectId
    • Array

    Schema 不仅定义了你的文档结构和属性,而且也定义了文档的实例方法,静态的模型方法、复合索引和被称为中间件的文档生命周期钩子。


    创建模型

    要使用我们的模式定义,我们需要将我们的 blogSchema转换为我们可以使用的模型。为此,我们要将blogSchema传入 mongoose.model(modelName,schema):

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

    实例方法

    模型的实例叫做文档。文档有许多自己的内置实例方法。我们也可以定义我们自己的自己的文档实例方法。

     let Schema = mongoose.Schema;
    //定义一个schema
    let animalSchema = new Schema({
        name: String,
        type: String
    });
    //为我们的animalSchema的methods对象分配一个函数。
    animalSchema.methods.findSimilarTypes = function (cb) {
        console.log(this.model);
        return this.model('Animal').find({ type: this.type }, cb);
    }
    //现在,我们的所有animal实例都有一个findSimilarTypes方法可以使用。
    let Animal = mongoose.model('Animal', animalSchema);
    let dog = new Animal({
        type: 'dog'
    });
    dog.save(function (err, dog) {
        dog.findSimilarTypes(function (err, dogs) {
            console.log(dogs);
        });
    });
    

    静态方法

    添加静态方法给一个model也同样简单。继续我们的animalSchema

    let Schema = mongoose.Schema;
    let animalSchema = new Schema({
        name: String,
        type: String
    });
    //给我们animalsChema上的statics对象添加一个方法。
    animalSchema.statics.findByName = function(name,cb){
        this.find({name:new RegExp(name,'i')},cb);
    }
    let Animal = mongoose.model('Animal',animalSchema);
    Animal.findByName('fido',function(err,target){
        console.log(target)
    });
    

    索引

    MongoDB支持二级索引。 使用mongoose,我们在路径级别或模式级别的模式中定义这些索引。 在创建复合索引时,必须在模式级别定义索引。
    建议在生产中禁用此行为,因为索引创建可能会导致显着的性能影响。 通过将模式的autoIndex选项设置为false来禁用该行为。

    animalSchema.set('autoIndex', false);
    // or
    new Schema({..}, { autoIndex: false });
    

    虚拟

    虚拟是指可以获取和设置但不会持久保存到MongoDB的文档属性。 getters对于格式化或组合字段很有用,而settings对于将单个值拆分为多个值以用于存储是有用的。

    let Schema = mongoose.Schema;
    //定义一个Schema
    let personSchema = new Schema({
        name:{
            first:String,
            last:String
        }
    });
     //编译成model
    let Person = mongoose.model('Person',personSchema);
    //  创建一个文档
    let bad = new Person({
        name:{
           first :'Walter',
            last:'White'
          }
    });
    

    假设我们想要记录bad的全名,那么我们可以手动这样处理。

      console.log(bad.name.first+' '+bad.name.last)
    

    或者,我们可以在personSchema上定义一个虚拟属性getter,那么我们不需要每次都写出这个字符串连接代码。

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

    现在,当我们访问我们的虚拟属性“name.full"时,我们的getter函数就会被调用,并且会将结果返回。

    console.log('%s is insane', bad.name.full); // Walter White is insane
    

    请注意,如果结果记录被转换为对象或JSON,默认情况下不会包括虚拟。将virtuals:true传递给toOject()或传递给toJSON()以使它们返回。
    我们同样可以通过设置this.name.full来设置this.name.first和this.name.last。例如,如果我们想把bad的name.first和name.last分别改为"Breaking"和“bad",那么只需这样:

    bad.name.full = 'Breaking Bad';
    

    Mongoose允许你通过它的虚拟属性setters来做到这一点。

    let personSchema = new Schema({
        name:{
            first:String,
            last:String
        }
    });
    
    let Person = mongoose.model('Person',personSchema);
    //虚拟属性getters
    personSchema.virtual('name.full').get(function(){
            return this.name.first+' '+this.name.last;
    });
    //虚拟属性setters
    personSchema.virtual('name.full').set(function(name){
        let split = name.split(' ');
        this.name.first = split[0];
        this.name.last = split[1];
    });
    let bad = new Person({
        name:{
           first :'Walter',
        last:'White'
        }
    });
    console.log(bad.name.full);  //Walter White
    bad.name.full = 'Breaking Bad';
    console.log(bad.name.full);  // Breaking Bad
    

    Options


    Schema 有一些可配置的选项,可以直接传递给构造函数或者直接设置:

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

    有效的选项(options):

    • autoIndex
    • capped
    • collection
    • id
    • _id
    • read
    • safe
    • shardKey
    • strict
    • toJSON
    • toObject
    • versionKey

    option:autoIndex

    在应用程序启动时,Mongoose为您的Schema中声明的每个索引发送一个ensureIndex命令。 从Mongoose v3开始,默认情况下在后台创建索引。 如果要禁用自动创建功能并在创建索引时手动处理,请将模式autoIndex选项设置为false,并在模型上使用ensureIndexes方法。

     let Schema = mongoose.Schema;
     let schema = mongoose.Schema({
        name: String
     }, {
            autoIndex: false
        });
     let Person = mongoose.model('Person', schema);
     Person.ensureIndexes(function (err) {
        if (err) {
            console.error(err);
        }
     });
    

    注意:不建议在生产环境中运行。 索引创建可能会影响数据库性能,具体取决于负载。 小心使用。
    ensureIndex命令不是并行发送的。 这是为了避免MongoError:无法添加索引与后台操作进行中错误。


    option: capped

    Mongoose支持MongoDB限制集合大小。 要指定底层MongoDB集合的上限,请将capped选项设置为集合的最大大小(以字节为单位)。

    let Schema = mongoose.Schema;
    let schema = mongoose.Schema({
        name: String
    }, {
            capped:1 //设置大小1字节
        });
    let Person = mongoose.model('Person', schema);
    let man = new Person({
        name:'noshower' 
    });
    man.save(function(err,target){
        if(err){
            console.error(err);
        }else{
            console.log(target);
        }
    });
    

    此时man能正常保存在数据库。但是,当name的值设置为很多字时,保存就会报错。

    MongoError: object to insert exceeds cappedMaxSize
    

    如果要传递其他选项(如max或auto IndexId),则capped选项也可以设置为对象。在这种情况下,你必须明确地传递其所需的大小选项。

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

    option: collection

    Mongoose默认情况下通过将模型名称传递给utils.toCollectionName方法来生成集合名称。 如果您需要为集合使用不同的名称,请设置此选项。

    上面我们model的名称是Person,Mongoose自动给我们生成了people结合。
    现在我们修改代码如下:

    let schema = new Schema({name:String},{collection:'data'});
    

    那么,生成的集合名字就叫"data";


    option:id

    Mongoose默认为每个schema分配一个id虚拟getter,它返回的文档_id字段会被强制转换为字符串,或者在ObjectIds及其hexString的情况下。 如果不想将id getter添加到模式中,可以在schema构建时禁用它传递此选项。

    //默认行为
    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    });
    let Person = mongoose.model('Page',schema);
    let man = new Person({
        name:'noshower'
    });
    console.log(man.id);  //5877026e2c52b034810535e2
    // 禁用id
    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    },{
        id:false
    });
    let Person = mongoose.model('Page',schema);
    let man = new Person({
        name:'noshower'
    });
    console.log(man.id); //undefined
    

    option: _id

    Mongoose默认为每个模式分配一个_id字段,如果没有传递给Schema构造函数。 类型被设置为一个ObjectId,与MongoDB的默认行为一致。 如果您不想将_id添加到schema中,您可以使用此选项禁用它。

    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    });
    //默认行为
    let Person = mongoose.model('Person',schema);
    let man = new Person({
        name:'noshower'
    });
    console.log(man); //name: 'noshower', _id: 58771d4c27cb0d3869a88885 }
    
    //禁止_id
    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    },{
        _id:false
    });
    let Person = mongoose.model('Person',schema);
    let man = new Person({
        name:'noshower'
    });
    console.log(man);//{ name: 'noshower' }
    man.save(function(err,target){
        if(err){
            console.error(err);
        }else{
            console.log(target);
        }
    });
    //此时不能保存信息
    

    Error: document must have an _id before saving
    我们刚刚禁止了_id,但是在保存前,必须要有_id属性。那么,我们就需要在定义schema时,就定义好_id

    let schema = new Schema({
      name:String,
      _id:Number
    },{
        _id:false
    });
    let Person = mongoose.model('Person',schema);
    let man = new Person({
        name:'noshower',
        _id:1
    });
    //此时能够正常保存了。
    man.save(function(err,target){
        if(err){
            console.error(err);
        }else{
            console.log(target);//{ __v: 0, name: 'noshower', _id: 1 }
        }
    });
    

    option:safe

    这个选项被传递给MongoDB和所有操作,并指定是否应该将错误返回给我们的回调,以及调整写入行为。

    new Schema({..},{safe:true});
    

    默认情况下,safe设置的是true。 通过设置safe,比如{j:1,w:2,wtimeout:10000}这样的东西,我们可以保证,写入被提交到MongoDB日志(j:1),至少有2个副本(w:2) 如果超过10秒(wtimeout:10000),写入将超时。 错误仍然会传递给我们的回调。

    new Schema({..},{j:1,w:2,wtimeout:10000})
    

    option:shardKey

    当我们有一个分片的MongoDB架构时,使用shardKey选项。 每个分片集合都有一个分片键,它必须出现在所有插入/更新操作中。 我们只需要将此模式选项设置为相同的shard键,我们就会设置。
    请注意,Mongoose不会为您发送shardcollection命令。 您必须自己配置您的碎片。

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

    option:strict

    strict选项(默认情况下已启用)确保传递给模型构造函数的,未在模式中指定的值不会保存到db。

     let Schema = mongoose.Schema;
     // 只定义了nane
    let schema = new Schema({
        name:String 
    });
    let Person = mongoose.model('Person',schema);
    // 多传了age
    let man = new Person({
        name:'noshower',
        age:22
    });
    man.save(function(err,target){
        if(err){
            console.error(err+'xx');
        }else{
            console.log(target);//age没有被存进去
    //{ __v: 0, name: 'noshower', _id: 58772602945ef339db63dc3d }
        }
    });
    //设置strict为false
    let schema = new Schema({
        name:String 
    },{
        strict:false
    });
    //此时age也存进去了。
    //{ __v: 0,name: 'noshower',age: 22,_id: 5877285c8913073ae8e0146f }
    

    option:toObject

    与toObject选项完全相同,但仅在调用文档toJSON方法时才适用。

    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    });
    schema.path('name').get(function(v){
        return v+' is my name'
    });
    schema.set('toJSON',{getters:true,virtuals:false});
    let Person = mongoose.model('Person',schema);
    let man = new Person({
        name:'noshwoer'
    });
    console.log(man);//{ name: 'noshwoer', _id: 5877349b8c8cf73cafa89ada }
    console.log(man.toObject());//{ name: 'noshwoer', _id: 5877349b8c8cf73cafa89ada }
    console.log(man.toJSON());//{ name: 'noshwoer is my name', _id: 5877349b8c8cf73cafa89ada }
    //当一个js对象被字符串化时,就会调用JSON。
    console.log(JSON.stringify(man));//{"name":"noshwoer is my name","_id":"5877349b8c8cf73cafa89ada"}
    

    option: toObject

    文档有一个toObject方法,将mongoose文档转换为一个纯javascript对象。 此方法接受几个选项。 默认情况下,不是基于每个文档应用这些选项,我们可以在此声明选项,并将其应用于所有这些模式文档。要使所有虚拟显示在您的console.log输出中,请将toObject选项设置为{getters:true}:

    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    });
    schema.path('name').get(function(v){
        return v+' is my name'
    });
    schema.set('toObject',{getters:true});
    let Person = mongoose.model('Person',schema);
    let man = new Person({
        name:'noshwoer'
    });
    console.log(man);//{ name: 'noshwoer is my name', _id: 587736eafce76a3cddadd692, id: '587736eafce76a3cddadd692' }
    

    option:versionKey

    当文档第一次被Mongoose创建时,都会被设置versionKey属性。 此键值包含文档的内部版本。 此文档属性的名称是可配置的。 默认值为__v。 如果这与您的应用程序冲突,您可以这样配置:

    let Schema = mongoose.Schema;
    let schema = new Schema({
        name:String
    });
    let Person = mongoose.model('Person',schema);
    let man = new Person({
        name:'noshwoer'
    });
    man.save(function(err,target){
        if(err){
            console.error(err);
        }else{
            console.log(target); //{ __v: 0, name: 'noshwoer', _id: 587739f5a090c53d34444e45 }
        }
    });
    // 下面自定义 versionkey
    let schema = new Schema({
        name:String
    },{
        versionKey:"_somethingElse"
    });
     // _v 变成了_somethingElse
     // { _somethingElse: 0, name: 'noshwoer', _id: 58773a67f2ec543d46cdac5b }
    

    还可以通过将versionKey设置为false来禁用文档版本控制。 除非你知道你在做什么,否则不要禁用版本控制。

    new Schema({..}, { versionKey: false });
    

    现在我们已经介绍了Schemas,让我们来看看SchemaTypes。

    SchemaTypes

    以下是所有有效的SchemaTypes

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

    Dates

    如果必须使用内置方法修改日期类型,请在保存之前使用doc.markModified('pathToYourDate')告诉Mongoose关于更改。

     let Schema = mongoose.Schema;
    let schema = new Schema({
        dueDate: Date
    });
    let Assignment = mongoose.model('Assignment', schema);
    let time = new Assignment({
        dueDate:new Date()
    });
    Assignment.findOne(function(err,doc){
        doc.dueDate.setMonth(3);
        doc.save(function(err,doc){}); //没有保存
    });
    Assignment.findOne(function (err, doc) {
        doc.dueDate.setMonth(2);
        doc.markModified('dueDate');
        doc.save(function (err, doc) {
      //保存了
        });
    });
    

    Mixed

    “任何事情”SchemaType,它的灵活性是在它的难以维护的权衡。 混合可以通过Schema.Types.Mixed或通过传递一个空对象字面量。 以下是等效的:

    let Any = new Schema({ any: {} });
    let Any = new Schema({ any: Schema.Types.Mixed });
    

    由于它是一个无模式的类型,您可以将值更改为任何您喜欢的,但Mongoose失去自动检测和保存这些更改的能力。 要“告诉”Mongoose混合类型的值已更改,请调用文档的.markModified(路径)方法,将路径传递到刚更改的混合类型。

    person.anything = { x: [3, 4, { y: "changed" }]};
    person.markModified('anything');
    person.save(); // anything will now get saved
    

    ObjectIds

    要指定ObjectId的类型,请在声明中使用Schema.Types.ObjectId。

    let mongoose = require('mongoose');
    let ObjectId = mongoose.Schema.Types.ObjectId;
    let Car = new Schema({ driver: ObjectId });
    

    Arrays

    提供模式类型或子文档数组的创建。

      let ToySchema = new Schema({ name: String });
      let ToyBox = new Schema({
        toys: [ToySchema], 
        buffers: [Buffer], 
        string: [String], 
        numbers: [Number] // ... etc});
    });
    

    注意:指定空数组等效于Mixed。 以下都创建了Mixed的数组:

    let Empty1 = new Schema({ any: [] });
    let Empty2 = new Schema({ any: Array });
    let Empty3 = new Schema({ any: [Schema.Types.Mixed] });
    let Empty4 = new Schema({ any: [{}] });

    相关文章

      网友评论

      本文标题:mongoose学习笔记2之Schemas

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