MongoDB Tutorial(2)MongoDB 的 Query Language
在 MongoDB Tutorial(1)雲端時代的 MongoDB 環境建置 裡頭,我們帶大家註冊了 MongoLab 帳號、建立了 MongoDB 本身其實是有提供自己的 Client 端命令列工具。因為 MongoDB 上層透過 JSON 格式來交換資料、底層透過 BSON (Binary JSON) 格式來儲存資料,所以 Client 端的命令列工具很自然地就採用 JavaScript 作為彼此溝通的語言。不過因為是命令列工具,在 Windows 作業系統底下,就脫離不了 Command Prompt 的原罪,編輯中文的時候游標位置很容易算錯。 Robomongo 提供的圖形介面比起 MongoDB 的 JavaScript Shell 要方便的多,而且支援 Windows、Mac OS X、與 Linux 等平台,所以我們先安裝 Robomongo,再進行 Query Language 的練習。 Robomongo Installation安裝 Robomongo 的方式很簡單,只要到 Robomongo 網站下載 MongoLab
|
publisherId |
publisherName |
---|---|
PH |
Prentice Hall PTR |
OA |
O'Reilly & Associates |
指令如下:
db.publishers.insert( {publisherId: "OA", publisherName: "O'Reilly & Associates"}); db.publishers.insert( {publisherId: "PH", publisherName: "Prentice Hall PTR"});
畫面如下:
Robomongo 的指令輸入區是個神奇的地方:
- 看起來似乎只是個單行的 Text Box,實際上是個多行的 Text Area,可以按
ENTER
鍵換行,無聊的話也可以將指令跟參數縮排對齊,方便檢查有無輸入錯誤。 - 因為是透過 JavaScript 語法進行 MongoDB 操作,每個指令其實都是一個 JavaScript 敘述,所以每個指令之間記得用
;
結尾的話,就可以同時輸入多個指令。 - 手不想離開鍵盤去按上面的綠色三角執行圖示,也可以直接按
Ctrl+ENTER
鍵執行指令。
很方便吧!
展開左邊的樹狀架構的 Cities、Collection,在 publishers
上點兩下,就可以看到剛剛輸入的結果:
剛剛看到的是 Text Mode 檢視。畫面右邊有三個小圖示,可以分別切換 Tree Mode、Table Mode、與剛剛的 Text Mode:
如果要建立 books
Collection,然後新增底下 4 個 Document:
_id |
isbn |
title |
releaseDate |
listPrice |
pubId |
---|---|---|---|---|---|
1 |
0131002872 |
Thinking in Java |
2002-12-01 |
54.99 |
PH |
2 |
059600530X |
Enterprise JavaBeans |
2004-06-02 |
44.95 |
OA |
3 |
0596005717 |
Head First EJB |
2003-10-03 |
44.95 |
OA |
4 |
0596004656 |
Head First Java |
2003-05-04 |
39.95 |
OA |
指令如下:
db.books.insert({_id: 1, isbn: "0131002872", title: "Thinking in Java", releaseDate: "2002-12-01", listPrice: 54.99, pubId: "PH"}); db.books.insert({_id: 2, isbn: "059600530X", title: "Enterprise JavaBeans", releaseDate: "2004-06-02", listPrice: 44.95, pubId: "OA"}); db.books.insert({_id: 3, isbn: "0596005717", title: "Head First EJB", releaseDate: "2003-10-03", listPrice: 44.95, pubId: "OA"}); db.books.insert({_id: 4, isbn: "0596004656", title: "Head First Java", releaseDate: "2003-05-04", listPrice: 39.95, pubId: "OA"});
CRUD – Retrieve
MongoDB Document 的 Query 方式:
- 只能從單一一個 Collection 找出符合條件的 Document
- 透過
db.collectionName.find(criteria, projection)
方法達成,回傳 Cursor - 跟 SQL 語言的 WHERE 子句很像
findOne
方法跟find
方法一樣,不過只傳回一個 Documentcriteria
就是一個 JSON 物件
比方說,如果要查詢所有的出版社與書籍資料,指令如下:
db.publishers.find(); db.books.find();
也就是剛剛在 publishers
或 books
Collection 上面用滑鼠點兩下,Robomongo 直接帶出的指令。
如果要查詢 OA
出版社出版的所有書籍資料,指令如下:
db.books.find({pubId: "OA"}); /* 0 */ { "_id" : 2, "isbn" : "059600530X", "title" : "Enterprise JavaBeans", "releaseDate" : "2004-06-02", "listPrice" : 44.95, "pubId" : "OA" } /* 1 */ { "_id" : 3, "isbn" : "0596005717", "title" : "Head First EJB", "releaseDate" : "2003-10-03", "listPrice" : 44.95, "pubId" : "OA" } /* 2 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "releaseDate" : "2003-05-04", "listPrice" : 39.95, "pubId" : "OA" }
Query Selector 提供類似 SQL 敘述裡面 where
子句的各種運算,方便撰寫查詢條件:
$lt
、$lte
、$gt
、$gte
$neq
$exists
$in
、$nin
、$all
$or
、$not
$mod
- …
比方說,如果要查詢所有定價超過 50 元美金的書籍資料,指令如下:
db.books.find({listPrice: {$gte: 50}}); /* 0 */ { "_id" : 1, "isbn" : "0131002872", "title" : "Thinking in Java", "releaseDate" : "2002-12-01", "listPrice" : 54.99, "pubId" : "PH" }
如果要查詢 OA
出版社出版的所有書籍中,書名出現過 Java
的書籍資料,指令如下:
db.books.find({pubId: "OA", title: /.*Java.*/g}); /* 0 */ { "_id" : 2, "isbn" : "059600530X", "title" : "Enterprise JavaBeans", "releaseDate" : "2004-06-02", "listPrice" : 44.95, "pubId" : "OA" } /* 1 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "releaseDate" : "2003-05-04", "listPrice" : 39.95, "pubId" : "OA" }
如果要查詢 OA
或 PH
出版社出版的所有書籍資料,指令如下:
db.books.find({$or: [{pubId: "OA"}, {pubId: "PH"}]}); 或 db.books.find({pubId: {$in: ["OA", "PH"]}}); /* 0 */ { "_id" : 1, "isbn" : "0131002872", "title" : "Thinking in Java", "releaseDate" : "2002-12-01", "listPrice" : 54.99, "pubId" : "PH" } /* 1 */ { "_id" : 2, "isbn" : "059600530X", "title" : "Enterprise JavaBeans", "releaseDate" : "2004-06-02", "listPrice" : 44.95, "pubId" : "OA" } /* 2 */ { "_id" : 3, "isbn" : "0596005717", "title" : "Head First EJB", "releaseDate" : "2003-10-03", "listPrice" : 44.95, "pubId" : "OA" } /* 3 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "releaseDate" : "2003-05-04", "listPrice" : 39.95, "pubId" : "OA" }
Query 預設會傳回所有欄位,所以如果想要指定回傳的欄位與順序,請加上 projection
參數,field: 1
表示 Query 結果必須包含這個欄位,field: 0
表示不要。除非特別指明,否則 _id
欄位預設都會回傳。
比方說,如果要查詢 OA
出版社出版的所有書籍,只需要書名與定價資料,指令如下:
db.books.find({pubId: "OA"}, {title:1, listPrice: 1, _id: 0}); /* 0 */ { "title" : "Enterprise JavaBeans", "listPrice" : 44.95 } /* 1 */ { "title" : "Head First EJB", "listPrice" : 44.95 } /* 2 */ { "title" : "Head First Java", "listPrice" : 39.95 }
如果要查詢所有書籍,不想要出版日期,指令如下:
db.books.find(null, {releaseDate: 0}); /* 0 */ { "_id" : 1, "isbn" : "0131002872", "title" : "Thinking in Java", "listPrice" : 54.99, "pubId" : "PH" } /* 1 */ { "_id" : 2, "isbn" : "059600530X", "title" : "Enterprise JavaBeans", "listPrice" : 44.95, "pubId" : "OA" } /* 2 */ { "_id" : 3, "isbn" : "0596005717", "title" : "Head First EJB", "listPrice" : 44.95, "pubId" : "OA" } /* 3 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "listPrice" : 39.95, "pubId" : "OA" }
搭配 sort
與 limit
、skip
等方法,可以控制輸出的順序,或是進行分頁。
比方說,如果要查詢所有書籍,只需要書名與定價資料,根據定價由小排到大或由大排到小,指令如下:
db.books.find(null, {title:1, listPrice: 1, _id: 0}).sort({listPrice: 1}); /* 0 */ { "title" : "Head First Java", "listPrice" : 39.95 } /* 1 */ { "title" : "Enterprise JavaBeans", "listPrice" : 44.95 } /* 2 */ { "title" : "Head First EJB", "listPrice" : 44.95 } /* 3 */ { "title" : "Thinking in Java", "listPrice" : 54.99 } db.books.find(null, {title:1, listPrice: 1, _id: 0}).sort({listPrice: -1}); /* 0 */ { "title" : "Thinking in Java", "listPrice" : 54.99 } /* 1 */ { "title" : "Enterprise JavaBeans", "listPrice" : 44.95 } /* 2 */ { "title" : "Head First EJB", "listPrice" : 44.95 } /* 3 */ { "title" : "Head First Java", "listPrice" : 39.95 }
如果要查詢所有書籍,根據定價由小排到大,只要前兩筆,指令如下:
db.books.find().sort({listPrice: 1}).limit(2); /* 0 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "releaseDate" : "2003-05-04", "listPrice" : 39.95, "pubId" : "OA" } /* 1 */ { "_id" : 2, "isbn" : "059600530X", "title" : "Enterprise JavaBeans", "releaseDate" : "2004-06-02", "listPrice" : 44.95, "pubId" : "OA" }
透過 count
之類的方法,可以輕易達到彙總的功能。
比方說,如果要查詢 OA
出版社出版的所有書籍總數,指令如下:
db.books.count({pubId: "OA"}); 3
CRUD – Update
MongoDB Document 的 Update 方式:
- 透過
db.collectionName.update(query, update, options)
方法達成 - 根據
query
找出資料之後,以update
取代 - Update 預設是 Full-Document Replacement,也就是會用新內容取代舊內容,跟我們一般對 Update 的想像非常不一樣
比方說,如果要修改 Thinking in Java
的定價,假設輸入指令如下:
db.books.update({title: "Thinking in Java"}, {listPrice: 55.99});
這時受影響的 Document 內容,會從原來底下的內容:
{ _id: 1, isbn: "0131002872", title: "Thinking in Java", releaseDate: "2002-12-01", listPrice: 54.99, pubId: "PH" }
變成令人驚訝的結果:
{ "_id" : 1, "listPrice" : 55.99 }
所以如果只想要修改某幾個特定 Field 的內容,也就是想要比較接近一般想像的 Update,請使用 $
開頭的 Update Operator:
$set
:設定 Field 內容,Field 存在就修改,Field 不存在就新增$unset
:從 Document 移除這個 Field$inc
:將 Field 加上特定值,Field 存在就修改,Field 不存在就新增,而且只能用在數值型別- …
比方說,如果只想要修改 Thinking in Java
的定價,指令如下:
db.books.update({title: "Thinking in Java"}, {$set: {listPrice: 55.99}}); db.books.find({title: "Thinking in Java"}); /* 0 */ { "_id" : 1, "isbn" : "0131002872", "title" : "Thinking in Java", "releaseDate" : "2002-12-01", "listPrice" : 55.99, "pubId" : "PH" }
Update 預設只會處理第一筆符合 query
條件的資料,這也是跟以往熟悉 SQL 的人想像中比較不一樣的地方。如果想要啟用 Multiple Update 功能,也就是所有符合 query
條件的 Document 都要更新,請加上 options
參數,並且將 multi
屬性值設定為 true
。
比方說,如果想要將 OA
出版的所有書籍定價調高 5 美金,但是使用底下的指令:
db.books.find({pubId: "OA"}, {"listPrice": 1}); /* 0 */ { "_id" : 2, "listPrice" : 44.95 } /* 1 */ { "_id" : 3, "listPrice" : 44.95 } /* 2 */ { "_id" : 4, "listPrice" : 39.95 } db.books.update({pubId: "OA"}, {$inc: {listPrice: 5.0}}); db.books.find({pubId: "OA"}, {"listPrice": 1}); /* 0 */ { "_id" : 2, "listPrice" : 49.95 } /* 1 */ { "_id" : 3, "listPrice" : 44.95 } /* 2 */ { "_id" : 4, "listPrice" : 39.95 }
就只會修改符合條件的第一筆。如果想要修改所有符合條件的 Document,那就必須補上第 3 個參數,指令如下:
db.books.find({pubId: "OA"}, {"listPrice": 1}); /* 0 */ { "_id" : 2, "listPrice" : 49.95 } /* 1 */ { "_id" : 3, "listPrice" : 44.95 } /* 2 */ { "_id" : 4, "listPrice" : 39.95 } db.books.update({pubId: "OA"}, {$inc: {listPrice: 5.0}}, {multi: true}); db.books.find({pubId: "OA"}, {"listPrice": 1}); /* 0 */ { "_id" : 2, "listPrice" : 54.95 } /* 1 */ { "_id" : 3, "listPrice" : 49.95 } /* 2 */ { "_id" : 4, "listPrice" : 44.95 }
如果想要啟用 UpSert 功能,也就是找得到符合 query
條件的 Document 就進行修改,找不到符合 query
條件的 Document 就新增 Document 的話,一樣要加上第 3 個參數,但是將 upsert
屬性設定為 true
,指令如下:
db.books.find({pubId: "OA"}, {"listPrice": 1}); /* 0 */ { "_id" : 2, "listPrice" : 54.95 } /* 1 */ { "_id" : 3, "listPrice" : 49.95 } /* 2 */ { "_id" : 4, "listPrice" : 44.95 } db.books.update({pubId: "OA"}, {$inc: {listPrice: 5.0}}, {upsert: true}); db.runCommand({getLastError: 1}); /* 0 */ { "updatedExisting" : true, "n" : 1, "lastOp" : Timestamp(1400464977, 1), "connectionId" : 118463, "err" : null, "ok" : 1 } db.books.find({pubId: "OA"}, {"listPrice": 1}); /* 0 */ { "_id" : 2, "listPrice" : 59.95 } /* 1 */ { "_id" : 3, "listPrice" : 49.95 } /* 2 */ { "_id" : 4, "listPrice" : 44.95 } db.books.update({pubId: "XX"}, {$inc: {listPrice: 5.0}}, {upsert: true}); db.runCommand({getLastError: 1}); /* 0 */ { "updatedExisting" : false, "upserted" : ObjectId("537967ca611c8b4dcdc2e815"), "n" : 1, "lastOp" : Timestamp(1400465354, 1), "connectionId" : 118463, "err" : null, "ok" : 1 } db.books.find({pubId: "XX"}); /* 0 */ { "_id" : ObjectId("537967ca611c8b4dcdc2e815"), "listPrice" : 5, "pubId" : "XX" }
執行完 Update 或 Remove 動作之後馬上接著執行 db.runCommand({getLastError: 1})
指令,傳回資料內的 n
欄位值,就是受到剛剛動作影響的 Document 總數。
CRUD – Delete
MongoDB Document 的 Delete 方式:
- 透過
db.collectionName.remove(query, options)
指令達成 - 根據
query
條件找出資料之後加以刪除
比方說,如果想要刪除剛剛沒處理好的 XX
出版社的書籍資料,指令如下:
db.books.remove({pubId: "XX"}); db.runCommand({getLastError: 1}); /* 0 */ { "n" : 1, "lastOp" : Timestamp(1400465476, 1), "connectionId" : 118463, "err" : null, "ok" : 1 }
Delete 預設會刪除所有符合條件的 Document,但是也可以透過 options
參數裡頭的 justOne
屬性設定成只刪除一個 Document。
比方說,如果想要刪除找到的第一本 OA
出版社的書籍資料,指令如下:
db.books.find({pubId: "OA"}); /* 0 */ { "_id" : 2, "isbn" : "059600530X", "title" : "Enterprise JavaBeans", "releaseDate" : "2004-06-02", "listPrice" : 59.95, "pubId" : "OA" } /* 1 */ { "_id" : 3, "isbn" : "0596005717", "title" : "Head First EJB", "releaseDate" : "2003-10-03", "listPrice" : 49.95, "pubId" : "OA" } /* 2 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "releaseDate" : "2003-05-04", "listPrice" : 44.95, "pubId" : "OA" } db.books.remove({pubId: "OA"}, {justOne: true}); db.books.find({pubId: "OA"}); /* 0 */ { "_id" : 3, "isbn" : "0596005717", "title" : "Head First EJB", "releaseDate" : "2003-10-03", "listPrice" : 49.95, "pubId" : "OA" } /* 1 */ { "_id" : 4, "isbn" : "0596004656", "title" : "Head First Java", "releaseDate" : "2003-05-04", "listPrice" : 44.95, "pubId" : "OA" }
這時,就只會刪除 OA
出版社的第一本書。