mongoose.js 基础使用——Schemas

2022-10-21  本文已影响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 有:

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

创建一个 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:

示例:

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 使用的选项。

全部可用

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

String

Number

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

合法的选项有:

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` 以时间戳的形式存储
上一篇下一篇

猜你喜欢

热点阅读