Why MongoDB & mongoose
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
与数据库交互一般有两种方法:
- SQL
- 对象数据模型(Object Data Model,简称 ODM)或对象关系模型(Object Relational Model,简称 ORM)
使用 ODM / ORM 通常可以降低开发和维护成本!除非你非常熟悉本地查询语言,或者项目对性能要求很高,否则强烈推荐使用 ODM。
mongoose是ODM/ORM的一种流行、活跃的解决方案。
MongoDB
基本概念
集合
Capped collections 就是固定大小的collection。它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 "RRD" 概念类似。
db.createCollection("mycoll", {capped:true, size:100000})
size是byte
MongoDB 数据类型
除了基本类型,还有Array,Object(用于内嵌文档,类似表联合),ObjectId,Binary Data,Code,Regular expression等。
ObjectId:
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
ongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象。
MongoDB连接
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
常用操作(不要忘记ODM的实质)
创建集合
show dbs
use foo
db.createCollection('dashen')
db.dashen.insert({..})
show tables
da.dashen.drop()//删除
在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
插入文档
db.dashen.insert({})
更新文档
update
db.collection.update(
,
,
{
upsert: ,
multi: ,
writeConcern:
}
)
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
db.dashen.save({_id:ObjectId("5d147e652104d5e0077b44f6"),skill:"java"})
删除文档
db.inventory.deleteOne( { status: "D" } )
db.inventory.deleteMany({})
查询文档
db.col.find({"likes": {or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
MongoDB 查询数据的语法格式如下:
db.collection.find(query, projection)
>db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
排序
1 为升序排列,而 -1 是用于降序排列
>db.COLLECTION_NAME.find().sort({KEY:1})
索引
“A database index is a data structure that improves the speed of data retrieval operations on a database table at the cost of additional writes and storage space to maintain the index data structure. Indices are used to quickly locate data without having to search every row in a database table every time a database table is accessed.”
默认情况下,MongoDB都会建立id的索引,所以对id的检索会比较效率
db.col.getIndexes()
db.col.dropIndex("索引名称")
db.col.createIndex({"createDate": 1},{expireAfterSeconds: 180})
聚合
聚合一系列管道操作,用于处理集合中文档数据(诸如统计平均值,求和等),并返回计算后的数据结果。
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
。。。。
分片、复制、备份和恢复等。
mongoose
基本流程操作
const mongoose = require('mongoose')
// 1.创建连接
const mongoUri = 'mongodb://localhost/foo'
mongoose.connect(mongoUri, {
useNewUrlParser: true
})
mongoose.Promise = global.Promise
const db = mongoose.connection
// open
db.on('error', (err) => console.log('err'))
db.on('open', (err) => {
console.log('open')
// collectCb()
})
// 2.定义模式
const { Schema } = mongoose
const CatSchema = new Schema({
name :{
type: String,
required: true
},
birthday :{
type: Date,
default: Date.now()
}
})
// 2.1 模式有一些类型,和验证
const schema = new Schema({
name: String,
binary: Buffer,
living: Boolean,
updated: {
type: Date,
default: Date.now
},
age: {
type: Number,
min: 18,
max: 65,
required: true
},
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
array: [],
ofString: [String], // 其他类型也可使用数组
nested: {
stuff: {
// 是否将 String 字段自动转换为小写、大写,或截断两端空格(例如{ type: String, lowercase: true, trim: true })
type: String,
lowercase: true,
trim: true
}
}
})
const breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, '鸡蛋太少'],
max: 12
},
drink: {
type: String,
enum: ['咖啡','茶']
}
})
// 3.定义Model(创建collection的原型)
const CatModel = mongoose.model('cat', CatSchema)
// 4.新建doc
const createFunc = (() => {
let catTest;
for (let i = 3; i > 0; i--) {
catTest = new CatModel({
name: `kitty,num:${i}`
})
catTest.save((err, doc) => {
if (!err) {
console.log(`kitty,num:${i} save success!`)
}
})
}
})()
// 5.查询
const queryFunc = (() => {
CatModel.find().where('name').equals('kitty,num:1').select('birthday').exec((err,doc) => {
// ...
})
})()
文档间协同
可以使用 ObjectId 模式字段来创建两个文档/模型实例间一对一的引用,(一组 ObjectIds 可创建一对多的引用)。该字段存储相关模型的 id。如果需要相关文档的实际内容,可以在查询中使用 populate() 方法,将 id 替换为实际数据。
Story
.findOne({ title: '司马迁是历史学家' })
.populate('author') // 使用作者 id 填充实际作者信息
.exec(function (err, story) {
if (err) {
return handleError(err);
}
console.log('作者是 %s', story.author.name);
// 控制台将打印 "作者是 司马迁"
});
虚拟属性
自定义某个属性(field)get和set方法,该属性并不存于数据库中。
虚拟属性是可以获取和设置、但不会保存到 MongoDB 的文档属性。
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const AuthorSchema = new Schema(
{
first_name: {type: String, required: true, max: 100},
family_name: {type: String, required: true, max: 100},
date_of_birth: {type: Date},
date_of_death: {type: Date},
}
);
// 虚拟属性'name':表示作者全名
AuthorSchema
.virtual('name')
.get(function () {
return this.family_name + ', ' + this.first_name;
});
// 虚拟属性'lifespan':作者寿命
AuthorSchema
.virtual('lifespan')
.get(function () {
return (this.date_of_death.getYear() - this.date_of_birth.getYear()).toString();
});
// 虚拟属性'url':作者 URL
AuthorSchema
.virtual('url')
.get(function () {
return '/catalog/author/' + this._id;
});
// 导出 Author 模型
module.exports = mongoose.model('Author', AuthorSchema);
一模式(模型)一文件
强烈建议将单一模式定义在单一模块(文件)中,并通过导出方法来创建模型。
mongoose默认使用当前connect的数据库。
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BookSchema = new Schema({
title: {type: String, required: true},
author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},
summary: {type: String, required: true},
isbn: {type: String, required: true},
genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]
});
// 虚拟属性'url':藏书 URL
BookSchema
.virtual('url')
.get(function () {
return '/catalog/book/' + this._id;
});
// 导出 Book 模块
module.exports = mongoose.model('Book', BookSchema);
测试脚本:https://raw.githubusercontent.com/roy-tian/mdn-examples/master/server/express-locallibrary-tutorial/populatedb.js