Node.js 15:《Mongodb 與 Mongoose 的使用》

2018-08-07 15:23 更新

目標

無明確目標

知識點

  1. 了解 mongodb (http://www.mongodb.org/ )
  2. 學習 mongoose 的使用 (http://mongoosejs.com/ )

課程內容

mongodb

mongodb 這個名詞相信大家不會陌生吧。有段時間 nosql 的概念炒得特別火,其中 hbase redis mongodb couchdb 之類的名詞都相繼進入了大眾的視野。

hbase 和 redis 和 mongodb 和 couchdb 雖然都屬于 nosql 的大范疇。但它們關注的領域是不一樣的。hbase 是存海量數(shù)據的,redis 用來做緩存,而 mongodb 和 couchdb 則試圖取代一些使用 mysql 的場景。

mongodb 的官網是這樣介紹自己的:

MongoDB (from "humongous") is an open-source document database, and the leading NoSQL database. Written in C++

開源、文檔型、nosql。

其中文檔型是個重要的概念需要理解。

在 sql 中,我們的數(shù)據層級是:數(shù)據庫(db) -> 表(table) -> 記錄(record)-> 字段;在 mongodb 中,數(shù)據的層級是:數(shù)據庫 -> collection -> document -> 字段。這四個概念可以對應得上。

文檔型數(shù)據這個名字中,“文檔”兩個字很容易誤解。其實這個文檔就是 bson 的意思。bson 是 json 的超集,比如 json 中沒法儲存二進制類型,而 bson 拓展了類型,提供了二進制支持。mongodb 中存儲的一條條記錄都可以用 bson 來表示。所以你也可以認為,mongodb 是個存 bson 數(shù)據的數(shù)據庫,或是存哈希數(shù)據的數(shù)據庫。

mongodb 相對于它的競爭對手們來說——比如 couchdb,它的一大優(yōu)勢就是盡可能提供與 sql 對應的概念。之前說了,sql 中的記錄對應 mongodb 中的 document,記錄這東西是一維的,而 document 可以嵌套很多層。在某些場景下,比如存儲一個文章的 tags,mongodb 中的字段可以輕松存儲數(shù)組類型,而 sql 中就需要設計個一對多的表關系出來。

假設有一個 blog 應用,其中有張 Post 表,表中有用戶發(fā)表的一些博客內容(post)。

這些 post 文檔的樣子大概會是這樣:

var post = {
  title: '呵呵的一天',
  author: 'alsotang',
  content: '今天網速很差',
  tags: ['呵呵', '網速', '差'],
};

mongodb 中有個最亮眼的特性,就是 Auto-Sharding,sharding 的意思可以理解成我們 scale sql 時的分表。

在 mongodb 中,表與表之間是沒有聯(lián)系的,不像 sql 中一樣,可以設定外鍵,可以進行表連接。mongodb 中,也無法支持事務。

所以這樣的表,無債一身輕。可以很輕易地 scale 至多個實例(假設實例都有不同的物理位置)上。在 mongodb 中,實時的那些查詢,也就只能進行條件查詢:某某大于一個值或某某等于一個值。而 sql 中,如果一張表的數(shù)據存在了多個實例上的話,當與其他表 join 時候,表之間的運來運去會是個很慢的過程,具體我也不太懂。

反正使用 mongodb 時,一定要思考的兩點就是:表 join 到底要不要,事務支持到底要不要。

mongodb 中的索引特性跟 sql 中差不多,只是它對于嵌套的數(shù)據類型也提供了支持。在建立復合索引時,mongodb 可以指定不同字段的排序,比如兩個字段 is_top(置頂) 和 create_time(創(chuàng)建時間) 要建立復合索引,我們可以指定 is_top 按正序排,create_time 按逆序排。mysql 說是有計劃支持這個特性,不過目前也沒什么消息。不過這點不重要。

mongodb 中,collection 是 schema-less 的。在 sql 中,我們需要用建表語句來表明數(shù)據應該具有的形式,而 mongodb 中,可以在同一張里存各種各樣不同的形式的數(shù)據。同一個 collection 中,可以有些 document 具有 100 個字段,而另一些,則只具有 5 個字段。如果你分不清這個特性的使用場景,那么請像使用 sql 一樣的,盡可能保證一個 collection 中數(shù)據格式是統(tǒng)一的。這個 schema-less 的特性,有個比較典型的場景是用來存儲日志類型的數(shù)據,可以搜搜看這方面的典型場景。

mongodb 和 mysql 要我選的話,無關緊要的應用我會選擇 mongodb,就當個簡單的存 json 數(shù)據的數(shù)據庫來用;如果是線上應用,肯定還是會選擇 mysql。畢竟 sql 比較成熟,而且各種常用場景的最佳實踐都有先例了。

我所在的阿里巴巴數(shù)據平臺,有各種各樣的大數(shù)據系統(tǒng)。有些做離線計算,一算就是幾個小時,算出來的結果被緩存起來,查詢時候就可以實時得到結果,只是數(shù)據一致性上,不可避免會有 delay;有些做實時運算,可以在 1s 內從幾千萬條數(shù)據中算出一個復雜條件的結果。但它們都提供了 sql 的接口,也就是說,無論底層他們是如何讓幾百臺機器 mapreduce,都讓你可以用已有的 sql 知識進行查詢。所以還是選擇 sql 省事啊。

這里還有個很好玩的網站:http://www.mongodb-is-web-scale.com/

順便說說 mongodb 與 redis 的不同。mongodb 是用來存非臨時數(shù)據的,可以認為是存在硬盤上,而 redis 的數(shù)據可以認為都在內存中,存儲臨時數(shù)據,丟了也無所謂。對于稍微復雜的查詢,redis 支持的查詢方式太少太少了,幾乎可以認為是 key-value 的。據說 instagram 的數(shù)據就全部存在 redis 中,用了好幾個幾十 G 內存的 aws ec2 機器在存。redis 也是支持把數(shù)據寫入硬盤的,aof 貌似都過時了,好久沒關注了。

mongodb 與 hbase 的區(qū)別。如果說你已經在考慮使用 hbase 了的話,應該也不用我介紹它們的區(qū)別了吧..

主題所限,就不展開講了。這之間的選擇和權衡,說起來真的是個很大的話題。

我對這方面的話題很感興趣,如果要討論這方面話題的話,可以去 https://cnodejs.org/ 發(fā)個帖,詳細描述一下場景然后 at 我(@alsotang)。

mongodb 的官網中有一些特性介紹:

其中標有箭頭的是基本概念,圓圈的是進階概念,畫叉的不必了解。

安裝 mongodb

課程到這,一直忘記說 mongodb 的安裝了。

ubuntu: http://docs.mongodb.org/manual/tutorial/install-mongodb-on-ubuntu/

mac: $ brew install mongodb

裝好以后,在命令行 $ mongod,然后另外開個 shell 窗口,輸入 $ mongo 就能使用了。

mongoose

mongoose 是個 odm。odm 的概念對應 sql 中的 orm。也就是 ruby on rails 中的 activerecord 那一層。orm 全稱是 Object-Relational Mapping,對象關系映射;而 odm 是 Object-Document Mapping,對象文檔映射。

它的作用就是,在程序代碼中,定義一下數(shù)據庫中的數(shù)據格式,然后取數(shù)據時通過它們,可以把數(shù)據庫中的 document 映射成程序中的一個對象,這個對象有 .save .update 等一系列方法,和 .title .author 等一系列屬性。在調用這些方法時,odm 會根據你調用時所用的條件,自動轉換成相應的 mongodb shell 語句幫你發(fā)送出去。自然地,在程序中鏈式調用一個個的方法要比手寫數(shù)據庫操作語句具有更大的靈活性和便利性。

mongoose 的官網給出了類似這樣一個示例,我改造了一下:

// 首先引入 mongoose 這個模塊
var mongoose = require('mongoose');
// 然后連接對應的數(shù)據庫:mongodb://localhost/test
// 其中,前面那個 mongodb 是 protocol scheme 的名稱;localhost 是 mongod 所在的地址;
// 端口號省略則默認連接 27017;test 是數(shù)據庫的名稱
// mongodb 中不需要建立數(shù)據庫,當你需要連接的數(shù)據庫不存在時,會自動創(chuàng)建一個出來。
// 關于 mongodb 的安全性,mongodb 我印象中安全機制很殘廢,用戶名密碼那套都做得不好,更
// 別提細致的用戶權限控制了。不過不用擔心,mongodb 的默認配置只接受來自本機的請求,內網都連不上。
// 當需要在內網中為其他機器提供 mongodb 服務時,或許可以去看看 iptables 相關的東西。
mongoose.connect('mongodb://localhost/test');

// 上面說了,我推薦在同一個 collection 中使用固定的數(shù)據形式。
// 在這里,我們創(chuàng)建了一個名為 Cat 的 model,它在數(shù)據庫中的名字根據傳給 mongoose.model 的第一個參數(shù)決定,mongoose 會將名詞變?yōu)閺蛿?shù),在這里,collection 的名字會是 `cats`。
// 這個 model 的定義是,有一個 String 類型的 name,String 數(shù)組類型的 friends,Number 類型的 age。
// mongodb 中大多數(shù)的數(shù)據類型都可以用 js 的原生類型來表示。至于說 String 的長度是多少,Number 的精度是多少。String 的最大限度是 16MB,Number 的整型是 64-bit,浮點數(shù)的話,js 中 `0.1 + 0.2` 的結果都是亂來的。。就不指望什么了。。
// 這里可以看到各種示例:http://mongoosejs.com/docs/schematypes.html
var Cat = mongoose.model('Cat', {
  name: String,
  friends: [String],
  age: Number,
});

// new 一個新對象,名叫 kitty
// 接著為 kitty 的屬性們賦值
var kitty = new Cat({ name: 'Zildjian', friends: ['tom', 'jerry']});
kitty.age = 3;

// 調用 .save 方法后,mongoose 會去你的 mongodb 中的 test 數(shù)據庫里,存入一條記錄。
kitty.save(function (err) {
  if (err) // ...
  console.log('meow');
});

我們可以驗證一下

$ mongo
MongoDB shell version: 2.6.4
connecting to: test
> show dbs
> use test
> show collections
> db.cats.find()

會發(fā)現(xiàn)里面就有一條記錄了。

設計個簡單博客程序

如果要寫個博客程序練手。數(shù)據庫可以這樣設計

var Post = mongoose.model('Post', {
  title: String,
  content: String,
  author: String,
  create_at: Date,
});

評論就不要自己做了,接入多說:http://duoshuo.com/

編輯器就純文本好了,用 markdown 寫。

用戶系統(tǒng)也不做,硬編碼幾個管理員賬號在配置文件中,然后用 http basic auth:https://github.com/jshttp/basic-auth 來做驗證。

示例程序

Nodeclub 是使用 Node.js 和 MongoDB 開發(fā)的社區(qū)系統(tǒng)

https://github.com/cnodejs/nodeclub

完。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號