mongoose 簡(jiǎn)介:
mongoose: elegant mongoldb object modeling for node.js
官網(wǎng)是這樣解釋mongoose。偉哥在 NodeJS 對(duì) MongoDB 封裝的文章中雖然對(duì) mongoldb 進(jìn)行封裝后簡(jiǎn)化了使用,但是對(duì)于編寫 MongoDB 驗(yàn)證,轉(zhuǎn)換和業(yè)務(wù)邏輯還是比較復(fù)雜的。對(duì)于之前使用 Java 習(xí)慣封裝 DAO 層來后端程序員來,這樣編寫來操作數(shù)據(jù)庫也是非常不方便。Mongoose 為模型提供了一種直接,基于scheme結(jié)構(gòu)去定義你的數(shù)據(jù)模型。它內(nèi)置數(shù)據(jù)驗(yàn)證,查詢構(gòu)建,業(yè)務(wù)邏輯鉤子等,開箱即用。
開始使用
快速上手 官方的指導(dǎo)先放在這。
大家瀏覽完官方文檔的快速上手之后,使用 mongoose 可以大致分為一下幾個(gè)步驟:
引入 mongoose 模塊
連接 mongodb 數(shù)據(jù)庫
創(chuàng)建 schema
創(chuàng)建 model 與數(shù)據(jù)庫中集合(表)建立聯(lián)系
-
增加數(shù)據(jù)
- model 實(shí)例化
- 實(shí)例.save()
修改數(shù)據(jù) model.updateOne()
刪除數(shù)據(jù) model.deleteOne()
查詢數(shù)據(jù) model.find()
mongodb 存儲(chǔ)的是一個(gè)json對(duì)象,這個(gè)schema可以理解成這個(gè)對(duì)象構(gòu)造函數(shù),這個(gè)schema的寫法寫過 Vue 和 React 的小伙伴都應(yīng)該非常熟悉,這個(gè)跟 component 的 props 是一樣的,前面是屬性名稱,后面是該屬性值的類型。寫過TypeScript的這個(gè)寫法就更熟悉了。當(dāng)然它也可以指定默認(rèn)值,這樣一看它就跟 Vue和React中 component 中的 props 完全一樣了。 schema 一定要與你將來創(chuàng)建model時(shí)映射數(shù)據(jù)中集合的屬性對(duì)應(yīng)起來,否則會(huì)造成插入或修改失敗。
代碼實(shí)例
/* 引入 mongoose 模塊 */
const mongoose = require('mongoose');
/* 與數(shù)據(jù)庫連接 第二參數(shù)還是傳入,否則會(huì)報(bào)出一個(gè)警告,看著別扭*/
mongoose.connect('mongodb://127.0.0.1:27017/test',{ useNewUrlParser: true });
/* 創(chuàng)建 schema */
const NewsSchema = mongoose.Schema({
title: String,
author: String,
content: String,
status: {
type: Number,
default:1
}
});
/* 創(chuàng)建 model 與 數(shù)據(jù)庫建立聯(lián)系 */
/* 第一個(gè)參數(shù) model 名稱,首字母最好大寫
* 第二個(gè)參數(shù) scheme
* 第三個(gè)參數(shù)就是與數(shù)據(jù)庫那個(gè)collection建立聯(lián)系
*/
const News = mongoose.model('News', NewsSchema,'news');
/* 增加數(shù)據(jù) 在news表中增加一條數(shù)據(jù)*/
/* 創(chuàng)建一個(gè)實(shí)例 */
const news = new News({title:'News Title',author:'author', content:'News Content', status:1});
/* 調(diào)用實(shí)例save方法進(jìn)行數(shù)據(jù)保存,即使數(shù)據(jù)庫中沒有news 這張表,它會(huì)先創(chuàng)建表在插入數(shù)據(jù)*/
news.save(callback)
/* 修改數(shù)據(jù) 修改status*/
News.updateOne({title:'News Title'},{status:2},callback);
/*刪除數(shù)據(jù)*/
News.deleteOne({title:'News Title'},callback);
/* 查詢數(shù)據(jù) */
News.find(callback) 或者 News.find({title:/^News/},callback)
mongoose 模塊化
通過上一節(jié)的了解 使用mongoose操作數(shù)據(jù)已經(jīng)可以,但是到我們需要對(duì)某一個(gè)collection(表)進(jìn)行操作時(shí)我們總不能先吧上面的定義一通,所有我們需要對(duì)其進(jìn)行模塊化。封裝的時(shí)候就跟 Java 中封裝的 DAO層一樣,不過在這里我們還是叫 model 比較切合 mongoose 中的概念。
mongoose的封裝我們分為 2 步走:
- 封裝一下如何獲取 mongodb 的實(shí)例,
- 講我們用到數(shù)據(jù)表的 model 都放在 model 文件夾中(當(dāng)然了這個(gè)名字隨意)
代碼示例:
/* 封裝 db.js 獲取鏈接數(shù)據(jù)庫實(shí)例 */
const mongoose = require('mongoose');
/*第一參數(shù)是數(shù)據(jù)庫連接*/
/*第二個(gè)參數(shù)根據(jù)官方配置,不然那個(gè)數(shù)據(jù)庫彈出警告*/
/*第三個(gè)參數(shù) 回調(diào)函數(shù)*/
mongoose.connect('mongodb://127.0.0.1:27017/test',{useNewUrlParser: true},(err)=>{
if(err) throw err,
console.log('數(shù)據(jù)庫連接成功')
})
module.exports = mongoose;
/* 封裝 modle (Java中的DAO) 以 news 表為例 */
const mongoose = require('./db.js');
const NewsSchema = mongoose.Schema({
title:String,
author:String,
content:String,
status:Number
})
module.exports = mongoose.model('News', NewsSchema, 'news');
/* 使用 */
const NewsModel = require('./model/news.js');
NewsModel.find({},(err,doc)=>{
if(err) throw err;
console.log(doc);
})
Mongoose 預(yù)定義修飾符 Getter 與 Setter
Mongoose 提供的一些預(yù)定義修飾符可以幫我們存取數(shù)據(jù)進(jìn)行一些格式化的操作。
Mongoose 本身提供了一些預(yù)定義的修飾符,設(shè)置這些屬性在數(shù)據(jù)存入數(shù)據(jù)庫的時(shí)候就會(huì)有對(duì)應(yīng)的操作。
// mongoose 預(yù)定義修飾符
trim:true //去首尾空格
lowercase:true //轉(zhuǎn)化為小寫
uppercase:true //轉(zhuǎn)化為大寫
var UserSchema=mongoose.Schema({
name:{
type:String,
trim:true //去首尾空格
},
age:Number,
status:{
type:Number,
default:1,
lowercase:true
}
})
Mongoose 也提供了自定義修飾符 Setter 和 Getter,Setter 會(huì)在數(shù)據(jù)存入數(shù)據(jù)庫時(shí)進(jìn)行格式化操作,Getter只是在輸出實(shí)例的時(shí)候會(huì)進(jìn)行定義的操作,讀數(shù)據(jù)庫的數(shù)據(jù)沒有任何影響(沒什么用)。
上代碼:
// 加入我們要對(duì) url 進(jìn)行格式化,如果數(shù)據(jù)以 http:// 開頭我們?cè)瓨哟嫒?,如果不?http:// 開頭,添加之后再存入數(shù)據(jù)庫
const ArticleSchema = mongoose.Schema({
title:String,
author:String,
img:{
type:String,
set(params){
if(!params) return params;
return /^http:\/\//.test(params) ? params : 'http://' + params
},
get(params){
return 'mongodb:' + params;
}
}
});
const article2 = new Article({
title:'武漢加油',
author: '人民日?qǐng)?bào)',
img:'wuhan/pic02'
});
// 這樣存入數(shù)據(jù)庫的數(shù)據(jù)就是 http://wuhan/pico2
console.log(article2.img) // mongo:wuhan/pic02 ---- getter就是這樣用,所有沒啥用處
Mongoose 內(nèi)置 CURD 方法,擴(kuò)展 Mongoose Model 的靜態(tài)方法和實(shí)例方法
// 內(nèi)置 CURD 方法
model.deleteMany()
model.deleteOne()
model.find()
model.findById()
model.findByIdAndDelete(),
model.findByIdAndRemove(),
model.findByIdAndUpdate(),
model.findOneAndDelete(),
model.findOneAndRemove(),
model.findOndAndUpdate(),
model.replaceOne(),
model.updateMany(),
model.updateOne()
ArticalModule.updateOne({'title':'武漢加油'},{$set:{'title':'中國加油,武漢加油'}})
在 mongoose 方法基礎(chǔ)上進(jìn)行封裝,例如 我們定義一個(gè)按照 title 查找的方法
Mongoose 分為靜態(tài)方法和實(shí)例方法兩種,這個(gè)跟各編程語言的靜態(tài)和實(shí)例方法一個(gè)意思
// 靜態(tài)方法
ArticleSchema.static({
findByTitle(title,cb){
this.find({title},function(err,docs){
cb(err,docs)
})
}
})
// 實(shí)例方法
ArticleSchema.method({
console.log('這是一個(gè)實(shí)例方法');
console.log(this.name);
})
Mongoose 數(shù)據(jù)校驗(yàn)
mongoose 聚合管道 (連表查詢)
聚合管道其實(shí)就是mongodb的連表查詢
舉個(gè)例子:現(xiàn)在有兩張表,訂單表(order)和訂單清單表(orderItem)
現(xiàn)在進(jìn)行查詢獲取所有訂單總價(jià)個(gè)大于90的訂單號(hào)訂單項(xiàng)清單
Order.aggregate([
{
$lookup:{
from:'orderItem', // 有集合
localField: 'id', // 做集合連接鍵
foreignFileId: 'order_id', // 有集合外鍵
as:'items' //查詢到集合的別名
},
$match:{
'all_price' : {$get:90}} 大于九十
}
}
])
// 上面的寫成SQL語句就是
// select * from order right join orderItem where order.id = orderItem.order_id and order.all_price > 90 這樣理解起來localField 和 foreignField 也許就簡(jiǎn)單了
上面是兩個(gè)表 多表查詢呢?
//有三張表
//分類表
articlecate: {
id: Schema.Types.ObjectId,
title:String,
description: String,
add_time: Date
}
//文章表
article:{
id:Schema.Types.ObjectId,
cid:Schema.Types.ObjectId,
title:String,
description:string,
author_id:Schema.Types.ObjectId,
author_name:String,
content:String,
add_addtime:Date
}
//用戶表
user:{
id:Schema.Types.ObjectId,
username:String,
password:String,
name:String,
age:Number,
sex:String,
tel:String,
add_time:Date
}
// 查詢文章信息,并顯示文章分類,以及作者信息
ArticleModule.aggregate([
{
$lookup:{
from: 'articlecate',
localField: 'cid',
foreignField:'id',
ad:articlecate
}
}{
$lookup:{
from: 'user',
localField:'auth_id',
foreignField:'id',
as:user
}
},{
$match:{
id: Schema.Type.ObjectId('123')
}
}
],(err,docs)=>{
console.log(JSON.stringify(docs))
})
管道管道,直接寫就OK了。還有一直用寫法是用Populate來進(jìn)行多表查詢,但是這種寫法性能上不如 $lookup,但是寫法上簡(jiǎn)單的。它的使用必須在創(chuàng)建 schema 時(shí)就要?jiǎng)?chuàng)建外鍵(跟關(guān)系性數(shù)據(jù)快快要一致了),具體用法可以看下面教程,寫的簡(jiǎn)單易懂,主要是作者頭像很好看 hhh
詳解 mongoose Populate
其他聚合需要了解的內(nèi)容