請關(guān)注專題:我的NodeJS學(xué)習(xí)之路(實踐之路)
小弟初涉node領(lǐng)域,不足之處纹腌,還請多多指教!
歡迎Star、Fork:https://github.com/gefangshuai/ANodeBlog
這篇主要來講Mongodb數(shù)據(jù)庫有關(guān)的內(nèi)容砌溺。
早已久仰NoSQL的大名,知道它相對有關(guān)系型數(shù)據(jù)庫冠绢,有很多的優(yōu)點抚吠,只是一直沒有時間來研究這個東西。所以借這個項目弟胀,對Mongodb進行了一次深入了解楷力。
Mongodb(或者是其他NoSQL數(shù)據(jù)庫)給我印象最深的就是高度的靈活性!
關(guān)系型數(shù)據(jù)庫與非關(guān)系型數(shù)據(jù)庫的簡單對比
假如我們用關(guān)系型數(shù)據(jù)庫設(shè)計了一張文章表孵户,字段如下:
- title:文章標題
- content:文章內(nèi)容
- authorId:作者ID(通诚舫可能是外鍵)
同時根據(jù)我們的設(shè)計,項目已經(jīng)開始線上測試并且錄入了很多文章數(shù)據(jù)夏哭。
這個時候產(chǎn)品狗要求我們開發(fā)一個文章的喜歡功能:類似“簡書”检柬。
只需要在文章中看到喜歡的用戶即可,所以是一個單向的關(guān)聯(lián)關(guān)系竖配。
關(guān)系型數(shù)據(jù)庫的解決思路:
需要添加一張喜歡表: favorite
何址,里面兩個字段:articleId
和userId
,表述的意思是:xx文章被xxx些用戶喜歡了
进胯。文章和喜歡的用戶為多對多關(guān)系
用爪。
可能,習(xí)慣了關(guān)系型數(shù)據(jù)庫的你沒覺出什么來胁镐,下面看一下非關(guān)系型數(shù)據(jù)庫的設(shè)計思路偎血。
非關(guān)系型數(shù)據(jù)庫的解決思路:
在文章的Collection中增加一個SubCollection诸衔,SubCollection中可以存放用戶信息,如用戶名
颇玷,只要有用戶喜歡了文章笨农,在這篇文章的文檔中的子文檔下插入一條記錄即可!
{
id: xxx,
title: '學(xué)習(xí)NodeJS',
content: `xxxx`,
favorite: [
{name: '張三'},
{name: '李四'}
]
}
表述的意思就是“張三帖渠、李四喜歡了《學(xué)習(xí)NodeJS》”谒亦。
是不是比關(guān)系型數(shù)據(jù)庫的設(shè)計思路更加靈活清晰?阿弃!
好了诊霹,對于關(guān)系型數(shù)據(jù)庫
和非關(guān)系型數(shù)據(jù)庫
的討論就不再深入了,沒有好壞之分渣淳,各有優(yōu)勢脾还。
項目中的Mongodb設(shè)計
NodeJS的流行,離不開豐富的中間件支持入愧,對于操作Mongoose的中間件鄙漏,我推薦“mongoosejs”, 官網(wǎng)稱之為:“Mongoose ODM”棺蛛。
關(guān)于orm和odm:
- ORM:Object Relational Mapping怔蚌,對象關(guān)系映射
- ODM:Object Document Mapping,對象文檔映射
其實兩者知識技術(shù)名詞上的區(qū)別旁赊,表象是一樣的桦踊,都是對象和數(shù)據(jù)庫的映射罷了。
Mongoose內(nèi)部實現(xiàn)了一套驗證機制及靈活的數(shù)據(jù)庫操作终畅,也是我推薦的一大理由籍胯。
先學(xué)習(xí)以下Mongoose的基本用法
- 將Mongoose集成到項目中
npm install --save mongoose
- 連接數(shù)據(jù)庫
var mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/blog');
- 定義一個Schema(也就是Mongodb中的Collections
集合
),更多字段類型离福,請參考SchemaTypes
var userSchema = {
username: {type: String, required: true, unique: true},
password: {type: String, required: true}
}
- 將Schema進行“Model化”
var User = mongoose.model('User', userSchema );
- 增加記錄
User.create({username: '張三', password: 'md5-pass'}, function(err, user){
if(!err){
console.log(user.username + ' 保存成功!');
}else{
console.log('數(shù)據(jù)保存失斦壤恰:' + err);
}
});
- 修改記錄
User.findOneAndUpdate({_id: req.params.userId}, {
username: newUsername
}, function (err, raw) {
if(!err) {
console.log( '修改成功!');
}else{
console.log('修改失敗');
}
});
- 刪除記錄
User.deleteById(userId, function(err, doc){
if(!err){
console.log('刪除成功');
}
});
- 查詢記錄
User.findById(userId, callback); // one record
User.findOne({username: '張三'}, callback); // one record
User.find(); // multi records
了解了Mongoose的基本用法,在進行數(shù)據(jù)庫設(shè)計妖爷,就容易很多了蝶涩。
本例中用戶User
和文章Article
的設(shè)計可直接參考dhHelper中的具體代碼。
Mongoose高級用法
關(guān)聯(lián)關(guān)系的建立
大家會發(fā)現(xiàn)dhHelper中userSchema
和articleSchema
是有對應(yīng)關(guān)系的(具體的說是“一對多的關(guān)系”)絮识,那么這個對應(yīng)關(guān)系是怎么設(shè)計的呢绿聘?
說到這里,我們先來說一個其他的問題:“關(guān)于Mongodb中兩個集合之間的對應(yīng)關(guān)系次舌,設(shè)計呢熄攘?”
通過查找資料我的總結(jié)如下:
- 如果只需要通過A集合查詢B集合,而不需要反過來查詢垃它,也就是單向的關(guān)系(如文章和評論,只需要展示文章的時候,將其評論展示即可)国拇,那么可以在A集合中建立一個子集合B洛史。這樣的查詢速度是最快的。
- 如果既需要通過A查詢B酱吝,又需要通過B查詢A(如作者和文章也殖,需要查詢某作者下的所有文章,展示文章的時候务热,有需要展示作者的相關(guān)信息)忆嗜,那么可以在子集合中通過一個唯一字段關(guān)聯(lián)父集合。
在articleSchema
中增加一個字段_user
崎岂,類型為Schema.Types.ObjectId
捆毫,關(guān)聯(lián)User
:
_user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
這樣,User和Article的關(guān)聯(lián)關(guān)系就建立好了冲甘。
Tip:
雖然關(guān)聯(lián)關(guān)系建立好了绩卤,但是當我們
var article = Article.findById(id, callback);
查詢出來的article
調(diào)用article._user.username
是出不來數(shù)據(jù)的,原因就是我們需要用到populate()
函數(shù)江醇。
var article = Article.findById(id, callback).populate('_user');
這樣article._user.username
就有數(shù)據(jù)了濒憋。類似Hibernate
懶加載機制,需要做一下特殊處理陶夜。
加入時間戳
所謂時間戳凛驮,就是當我們對數(shù)據(jù)進行增加或修改的時候,數(shù)據(jù)庫能自動記錄增加時間
和修改時間
条辟,不需要手動來維護黔夭。
以前使用Hibernate,默認是沒有這個功能的捂贿,要想實現(xiàn)需要通過
@PrePersist
注解和@PreUpdate
注解來手動定義好纠修,很是麻煩。而Mongoose的解決方法是在數(shù)據(jù)庫的定義時就可以將這些信息定義好厂僧。這可能也就是約定優(yōu)于配置(convention over configuration)
的好處吧扣草!
定義時間戳,很簡單颜屠,在new Schema()
的時候辰妙,將時間戳的定義當作第二個參數(shù)傳入即可:
new Schema({xxx: xxx}, {timestamps: {createdAt: 'created_at', updatedAt: 'updated_at'});
其中createdAt
和updatedAt
是固定的key,created_at
和updated_at
是對應(yīng)的字段名字甫窟。
關(guān)于NodeJS中數(shù)據(jù)庫的知識密浑,就寫這么多了,想要更多的了解有關(guān)Mongoose的用法粗井,請參考官方文檔:Mongoosejs Guide尔破。文檔寫得非常詳細街图!
Have a good luck~
未完待續(xù)
請關(guān)注專題:我的NodeJS學(xué)習(xí)之路(實踐之路)