請關(guān)注專題:我的NodeJS學(xué)習(xí)之路(實(shí)踐之路)
小弟初涉node領(lǐng)域,不足之處,還請多多指教!
歡迎Star、Fork:https://github.com/gefangshuai/ANodeBlog
這篇主要來講Mongodb數(shù)據(jù)庫有關(guān)的內(nèi)容。
早已久仰NoSQL的大名,知道它相對有關(guān)系型數(shù)據(jù)庫,有很多的優(yōu)點(diǎn),只是一直沒有時間來研究這個東西。所以借這個項目,對Mongodb進(jìn)行了一次深入了解。
Mongodb(或者是其他NoSQL數(shù)據(jù)庫)給我印象最深的就是高度的靈活性!
關(guān)系型數(shù)據(jù)庫與非關(guān)系型數(shù)據(jù)庫的簡單對比

假如我們用關(guān)系型數(shù)據(jù)庫設(shè)計了一張文章表,字段如下:
- title:文章標(biāo)題
- 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í)兩者知識技術(shù)名詞上的區(qū)別,表象是一樣的,都是對象和數(shù)據(jù)庫的映射罷了。
Mongoose內(nèi)部實(shí)現(xiàn)了一套驗證機(jī)制及靈活的數(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進(jìn)行“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的基本用法,在進(jìn)行數(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)系建立好了,但是當(dāng)我們
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懶加載機(jī)制,需要做一下特殊處理。
加入時間戳
所謂時間戳,就是當(dāng)我們對數(shù)據(jù)進(jìn)行增加或修改的時候,數(shù)據(jù)庫能自動記錄增加時間和修改時間,不需要手動來維護(hù)。
以前使用Hibernate,默認(rèn)是沒有這個功能的,要想實(shí)現(xiàn)需要通過
@PrePersist注解和@PreUpdate注解來手動定義好,很是麻煩。而Mongoose的解決方法是在數(shù)據(jù)庫的定義時就可以將這些信息定義好。這可能也就是約定優(yōu)于配置(convention over configuration)的好處吧!
定義時間戳,很簡單,在new Schema()的時候,將時間戳的定義當(dāng)作第二個參數(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。文檔寫得非常詳細(xì)!
Have a good luck~
未完待續(xù)
請關(guān)注專題:我的NodeJS學(xué)習(xí)之路(實(shí)踐之路)