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)
Virtuals
是 document
的属性,但是不会被保存到 MongoDB
。 getter
可以用于格式化和组合字段数据, 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 自动添加 createdAt
和 updatedAt
字段, 其类型为 Date。
这两个字段的默认名是 createdAt
和 updatedAt
, 你可以通过设定 timestamps.createdAt
和 timestamps.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` 以时间戳的形式存储
网友评论