美文网首页
mongoose.js 基础使用——Schemas

mongoose.js 基础使用——Schemas

作者: 没名字的某某人 | 来源:发表于2022-10-21 11:26 被阅读0次

    MongoDB是由C++语言编写的非关系型数据库,是一个基于分布式文件储存的开源数据库系统,其内容存储形式类似JSON对象,它的字段值可以包含其他文档、数组及文档数组,非常灵活,和JavaScript可以说是很般配了。

    小试牛刀

    MongoDB分为三个概念:数据库,集合,文档。mongoose操作数据需要先定义Schema(集合的约束),将要连接的集合加以定义好的约束,便形成Model构造函数,利用model我们可以轻松的操作集合里的Document

    let mongoose = require('mongoose')
    mongoose.connect('mongodb://localhost/music')
    let db = mongoose.connection;
    // 第二个参数为函数,所以使用bind包装一下
    db.on('error', console.error.bind(console, 'connection error;'));
    
    // 创建集合约束
    let ownSongsSchema = mongoose.Schema({
      songTitle: String,
      album: { type: String, default: '未知专辑' },
      duration: String,
      singer: String,
      issueDate: Date
    }, {
      timestamps: {
        createdAt: 'createTime', updatedAt: 'updateTime',
        currentTime: () => Date.now()
      }
    })
    // 将集合加以约束,形成数据模型
    let OwnSongs = mongoose.model('ownSongs', ownSongsSchema)
    // 利用数据模型插入一条文档
    let songs = new OwnSongs({
      songTitle: '一路向北',
      ablum: '11月的萧邦',
      duration: '04:55',
      singer: '周杰伦',
      issueDate: '2005-11-01',
    })
    // 保存到数据库中
    songs.save(function (err, info) {
      console.log(err, info);
    })
    

    Schemas

    每个schema都会映射一个MongoDB collection,并定义这个collection里的文档的构成。
    document 里每个属性的类型都会被转换为 在 blogSchema 里定义对应的 SchemaType。
    允许使用的 SchemaTypes 有:

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

    Schema的功能不只是定义文档结构和属性类型,它可以定义——

    • document 的 instance methods
    • model 的 static Model methods
    • 复合索引
    • 文档的生命周期钩子,也成为中间件

    创建一个 model

    通过 mongoose.model函数将Schema转换为一个Model

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

    实例方法(method)

    documents 是 Models 的实例。 Document 有很多自带的实例方法, 当然也可以自定义我们自己的方法。

    // 定义一个 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
      });
    

    注意,不要在schema上挂在mongoose已经有的方法,且挂在方法不要使用箭头函数,否则调用时会导致this指向错误

    静态方法(static)

    继续用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);
      });
    

    查询助手(query helper)

    查询助手作用于 query 实例,方便你自定义拓展你的链式查询

      animalSchema.query.byName = function(name) {
        return this.find({ name: new RegExp(name, 'i') });
      };
    
      var Animal = mongoose.model('Animal', animalSchema);
      Animal.find().byName('fido').exec(function(err, animals) {
        console.log(animals);
      });
    

    索引(index)

    用来提高查询性能

    虚拟值(Virtuals)

    Virtualsdocument 的属性,但是不会被保存到 MongoDBgetter 可以用于格式化和组合字段数据, setter 可以很方便地分解一个值到多个字段。

     // define a schema
      var personSchema = new Schema({
        name: {
          first: String,
          last: String
        }
      });
    
      // compile our model
      var Person = mongoose.model('Person', personSchema);
    
      // create a document
      var axl = new Person({
        name: { first: 'Axl', last: 'Rose' }
      });
    

    如果你要log出全名,可以这么做:

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

    但是每次都这么拼接实在太麻烦了, 推荐你使用 virtual property getter, 这个方法允许你定义一个 fullName 属性,但不必保存到数据库。

    personSchema.virtual('fullName').get(function () {
      return this.name.first + ' ' + this.name.last;
    });
    console.log(axl.fullName); // Axl Rose
    

    如果对 document 使用 toJSON()toObject(),默认不包括虚拟值, 你需要额外向 toObject() 或者 toJSON() 传入参数 { virtuals: true }
    你也可以设定虚拟值的 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'; // Now `axl.name.first` is "William"
    

    模式类型(SchemaTypes)

    SchemaType 处理字段路径各种属性的定义,以下是 mongoose 的所有合法 SchemaTypes:

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

    示例:

    var schema = new Schema({
      name:    String,
      binary:  Buffer,
      living:  Boolean,
      updated: { type: Date, default: Date.now },
      age:     { type: Number, min: 18, max: 65 },
      mixed:   Schema.Types.Mixed,
      _someId: Schema.Types.ObjectId,
      decimal: Schema.Types.Decimal128,
      array:      [],
      ofString:   [String],
      ofNumber:   [Number],
      ofDates:    [Date],
      ofBuffer:   [Buffer],
      ofBoolean:  [Boolean],
      ofMixed:    [Schema.Types.Mixed],
      ofObjectId: [Schema.Types.ObjectId],
      ofArrays:   [[]],
      ofArrayOfNumbers: [[Number]],
      nested: {
        stuff: { type: String, lowercase: true, trim: true }
      }
    })
    
    // example use
    
    var Thing = mongoose.model('Thing', schema);
    
    var m = new Thing;
    m.name = 'Statue of Liberty';
    m.age = 125;
    m.updated = new Date;
    m.binary = new Buffer(0);
    m.living = false;
    m.mixed = { any: { thing: 'i want' } };
    m.markModified('mixed');
    m._someId = new mongoose.Types.ObjectId;
    m.array.push(1);
    m.ofString.push("strings!");
    m.ofNumber.unshift(1,2,3,4);
    m.ofDates.addToSet(new Date);
    m.ofBuffer.pop();
    m.ofMixed = [1, [], 'three', { four: 5 }];
    m.nested.stuff = 'good';
    m.save(callback);
    
    SchemaType 选项

    你可以直接声明 schema type 为某一种 type,或者赋值一个含有 type 属性的对象,除此之外,还可以对字段路径指定其他属性,例如你要在保存之前要把字母都改成小写:

    var schema2 = new Schema({
      test: {
        type: String,
        lowercase: true // Always convert `test` to lowercase
      }
    });
    

    lowercase 属性只作用于字符串。以下有一些全部 type 可用的选项和一些限定部分 type 使用的选项。

    全部可用

    • required: 布尔值或函数 如果值为真,为此属性添加 required 验证器
    • default: 任何值或函数 设置此路径默认值。如果是函数,函数返回值为默认值
    • select: 布尔值 指定 query 的默认projections
    • validate: 函数 adds a validator function for this property
    • get: 函数 使用 Object.defineProperty()定义自定义 getter
    • set: 函数 使用 Object.defineProperty()定义自定义 setter
    • alias: 字符串 仅mongoose >= 4.10.0。 为该字段路径定义虚拟值 gets/sets

    索引相关
    你可以使用 schema type 选项定义MongoDB indexes。

    • index: 布尔值 是否对这个属性创建索引
    • unique: 布尔值 是否对这个属性创建唯一索引
    • sparse: 布尔值 是否对这个属性创建稀疏索引

    String

    • lowercase: 布尔值 是否在保存前对此值调用 .toLowerCase()
    • uppercase: 布尔值 是否在保存前对此值调用 .toUpperCase()
    • trim: 布尔值 是否在保存前对此值调用 .trim()
    • match: 正则表达式 创建验证器检查这个值是否匹配给定正则表达式
    • enum: 数组 创建验证器检查这个值是否包含于给定数组

    Number

    • min: 数值 创建验证器检查属性是否大于或等于该值
    • max: 数值 创建验证器检查属性是否小于或等于该值

    Date

    • min: Date
    • max: Date
    使用注意

    Dates
    内建 Date 方法 mongoose 修改跟踪逻辑,因此保存不到数据库。

    Mixed
    一个啥都可以放的 SchemaType , 虽然便利,但也会让数据难以维护。 Mixed 可以通过 Schema.Types.Mixed 或 传入一个空对象定义。以下三种方法效果一致:

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

    因为这是个 schema-less type, 所以你可以赋值为任意类型, 但是 mongoose 无法自动检测并保存你的修改。 要告诉 Mongoose 你修改了 Mixed type 的值,调用 文档的 .markModified(path) 方法, 传入你的 Mixed 字段路径。

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

    Arrays
    创造 SchemaTypes 或子文档数组。

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

    数组的默认值是 [] (空数组)。

    var Toy = mongoose.model('Test', ToySchema);
    console.log((new Toy()).toys); // []
    

    要手动把默认值设置为 undefined,从而覆盖 []

    var ToySchema = new Schema({
      toys: {
        type: [ToySchema],
        default: undefined
      }
    });
    

    选项

    Schemas 有很多可配置选项,你可以在构造时传入或者直接 set

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

    合法的选项有:

    • autoIndex 用来提高查询性能

    • bufferCommands 文件传输时中断提示

    • capped 数据库大小限制

    • collection Mongoose 通过 utils.toCollectionName 方法 默认生成 collection 的名称(生成 model 名称的复数形式)。 设置这个选项可以自定义名称。

    • id Mongoose 会默认生成一个虚拟值 id,指向文档的 _id 字段。 如果你不需要 id 虚拟值,可以通过这个选项禁用此功能。

    • _id Mongoose 默认给你的 Schema 赋值一个 _id。 这个值的类型是 ObjectId,这与MongoDB的默认表现一致。 如果你不需要 _id,可以通过这个选项禁用此功能。

    • minimize 配置是否保存空对象,默认不保存空对象

    • read 用于连接时的别名

    • shardKey 分片相关

    • strict 默认不能 save schema 里没有声明的属性

    • strictQuery strict 模式不适用于查询的filter参数。

    • toJSON 下文中解释

    • toObject 下文中解释

    • typeKey 在schema里type字段被视为关键字

    • validateBeforeSave 对插入的文档进行检查

    • versionKey 在document里__v字段被视为关键字

    • collation 为查询(query和) 聚合(aggregation)设置 collation

    • skipVersioning 跳过版本控制

    • timestamps 下文中解释

    • useNestedStrict mongoose 会忽略嵌套的 strict 设定。

    toObject

    Documents 的 toObject 方法可以把文档转换成一个 plain javascript object (也就是去掉里面的方法)。 这是一个可以接收多个参数的方法,我们可以在 schemas 定义这些参数。

    例如要打印出虚拟值,可以向 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' }
    
    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' }
    
    timestamps

    如果设置了 timestamps 选项, mongoose 会在你的 schema 自动添加 createdAtupdatedAt 字段, 其类型为 Date。

    这两个字段的默认名是 createdAtupdatedAt, 你可以通过设定 timestamps.createdAttimestamps.updatedAt 自定义字段名称。

    且,可指定存储值

    var thingSchema = new Schema({..}, { timestamps: {
      createdAt: 'createTime',
      updatedAt: 'updateTime',
      currentTime: () => Date.now()
    } });
    var Thing = mongoose.model('Thing', thingSchema);
    var thing = new Thing();
    thing.save(); // `createTime` & `updateTime` 以时间戳的形式存储
    

    相关文章

      网友评论

          本文标题:mongoose.js 基础使用——Schemas

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