課程資源
課程作業說明。
主線任務:
週末任務一:極速切版(需開源) AdobeXD 設計稿
週末任務二:mongoose todolist kata
觀看此章節影片,將 todolist kata 任務內容,再加上 Mongoose 連接 MongoDB 雲端資料庫,錄製壓在 30 min 內。
前置作業
請建立一個 database,並建立一個 posts
collection
將此 JSON 資料,透過 Compass 倒入到 posts collection
1 2 3 4 5 6 7 8 | - name:貼文姓名 - image:貼文圖片 - content:貼文內容 - likes:按讚數 - comments:留言數 - createdAt:發文時間 - type:貼文種類[fan(粉絲)、group(社團)] - tags:貼文標籤 |
compass 加入 posts
collection 設定
在 mongoDB compass 應用程式選取 hotel 資料庫上點按加圖示按鈕。
collection Name 輸入 posts 點按建立按鈕。
左側選單中的 hotel 資料庫中,會多一個 posts ,開啟新的 posts collection 都會是空的,點按下中間 Import Data 按鈕,準備將下載的 JSON 檔 final.json 倒入。
選檔 final.json 後,點按右下角的 IMPORT 按鈕, final.json 檔裡的資料就會倒入到 posts 集合中。
造訪 posts
collection 與搜尋資料
篩選 document 中有 type: 'group' 的資料。
1 | db.posts.find({'type': 'group'}) |
輸入指令後會尋找到的資料會列於終端機上。
使用 mongoDB compass 應用程式,在搜尋列將 FILTER 輸入框中輸入 {'type': 'group'} 後,點按右方的 FIND 按鈕,會找到 792 筆的相關資料。
題庫
課程範圍
搜尋 name 欄位為 Ray Xu
的 document 列表
1 | db.posts.find({"name":"Ray Xu"}) |
新增一筆 document,請全部欄位皆填寫
1 | db.posts.insertOne({}) |
1 | db.posts.insert({}) |
1 2 3 4 5 6 7 8 9 10 | db.posts.insert({ 'name' : 'JimmyWU', 'type' : "group", 'image' : "http://dummyimage.com/197x100.png/dddddd/000000", 'createdAt' : "2022-03-08 08:02:43 UTC", 'content' : '貼文內容', 'likes' : 99999, 'comments' : 99999, 'tags' : ['fan(粉絲)', 'group(社團)'], }) |
新增多筆 document,請全部欄位皆填寫
1 | db.posts.insertMany([{},{}, ...]) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | db.posts.insertMany( [ { 'name' : 'JimmyWU-1', 'type' : "group", 'image' : "http://dummyimage.com/197x100.png/dddddd/11111111", 'createdAt' : "2022-03-08 08:02:43 UTC", 'content' : '貼文內容', 'likes' : 99999, 'comments' : 99999, 'tags' : ['fan(粉絲)', 'group(社團)'], }, { 'name' : 'JimmyWU-2', 'type' : "group", 'image' : "http://dummyimage.com/197x100.png/dddddd/22222222", 'createdAt' : "2022-03-08 08:02:43 UTC", 'content' : '貼文內容', 'likes' : 99999, 'comments' : 99999, 'tags' : ['fan(粉絲)', 'group(社團)'], } ] ) |
修改一筆 document,filter 條件請用 _id
指定其中一筆資料,content
欄位調整為測試資料
1 | db.conllection.updateOne(<filter>, <updata>, <option>) |
1 2 3 4 5 6 7 8 9 10 | db.posts.updateOne( { "_id": ObjectId("625119a513c5c032aff4b5e0") }, { "$set":{ "content":"測試資料" } } ) |
修改多筆 name
欄位為 "Ray Xu"
的 document 列表,content
欄位都調整為哈哈你看看你
。
1 | db.conllection.updateMany(<filter>, <updata>, <option>) |
第二參數 <updata> 向第一參數 <filter 指定的對象,配合 $set 語法做為屬性欄位名,將屬性欄位值中再以 {} 一個物件包裝,將對象所指定的屬性欄位做值的更換 (局部),而不是整個 document 都進行完整更換。
1 | db.posts.updateMany({ 'name': 'Ray Xu' }, { $set: { 'content': '哈哈你看看你' } }) |
刪除一筆 document,filter 條件請用 _id
任意指定其中一筆資料
1 | db.clolection.deleteOne(<filter>, <options>) |
1 2 3 | db.posts.deleteOne({ '_id': ObjectId("625119a513c5c032aff4b5e0"), }); |
刪除多筆 document,filter 條件請用 type
為 group
的值,刪除所有社團貼文
1 2 3 | db.posts.deleteMany({ 'type': 'group', }); |
刪除多筆 document,filter 條件為以下條件
a. name:"Ray Xu"
b. likes: 500(含) 個讚以下
先使用複合條件方式進行查找,使用 {'屬性欄位1': '', '屬性欄位2': '', ...} 帶入條件。
1 2 3 4 | db.posts.find({ "name": "Ray Xu", "likes": { $lt: 500 } }); |
"likes": { $lt: 500 } 以 $lt 比較語法取出 likes 屬性欄位小於 500 的數值。
以 mongoDB compass 應用軟體,查找低於 500 只會有 1 筆,而低於 2000 有 8。
以相同的參數設定,以刪除多筆資料方式進行資料操作。
1 2 3 4 | db.posts.deleteMany({ "name": "Ray Xu", "likes": { $lt: 500 } }) |
下完指令後,會回應 { "acknowledged" : true, "deletedCount" : 1 } 刪除一筆。
再使用 mongoDB Compass 軟體查看,小於 500 找不到資料而 2000 變成 7 筆。
查詢全部 posts
的 document 列表
1 | db.posts.find() |
1 | db.posts.find().pretty() |
關鍵字搜尋 name
裡面含有 o
的 document 列表
這裡需要使用到正則表達式取對象時,做為單字元的判斷,會使用 /<比對字元或字串>/ 的方式進行字串比對,以 compass 軟體使 {'name': /o/} 送出。
終端機指令如下。
1 | db.posts.find({'name': /o/}) |
Compass 執行念有 'o' 的字串,與預設完全比對筆數差別。
查詢 name
欄位為 "Ray Xu"
,filter 篩選出介於 500~1000(含) 個讚
mongoDB compass 軟體使用 { 'likes': {'$gte': 500, '$lte':1000 } } ( $gte:大於等於 / $lte: 小於等於),會帶出多比 500 ~ 1000 (含) 的資料,共 52 筆。
以複合條件上再加上 'name': 'Ray Xu',寫成 { 'name': "Ray Xu", 'likes': {'$gte': 500, '$lte':1000 } },只會找到一筆資料。
使用終端機指令如下。
1 2 3 4 5 6 7 8 9 | db.posts.find( { 'name': "Ray Xu", 'likes': { '$gte': 500, '$lte':1000 } } ) |
結果也只會印出一筆資料。
查詢 comments
有超過 500(含)以上的 document 列表
mongoDB compass 軟體使用 {'comments': { '$gte': 500 }},會比對到 145 筆資料。
終端機指令如下。
1 2 3 4 5 | db.posts.find( { 'comments': { '$gte': 500 } } ) |
查詢 tags
欄位,有 謎因
或(or) 幹話
的 document 列表
tags 欄位本身就是陣列格式,會需要配合 $in 語法進行操作,陣列內可能會是單筆或是多筆的字串資料,所以在取用的值對象是陣列時,就需要另外以此方法進行操作,才能再取出陣列裡指定的值。
1 | db.posts.find({ 'tags': { $in: ['謎因', '幹話'] } }) |
在終端機上只顯示二十筆與 'tags' 屬性欄位。
1 2 3 4 | db.posts.find( { 'tags': { $in: ['謎因', '幹話'] } }, { 'tags': 1 } ) |
終端機執行結果如下。
MongoDB compass 軟體使用指令 { 'tags': { $in: ['謎因', '幹話'] } } 查詢,萉得 573 筆資料,其中屬性欄位 'tags' 陣列中有 '謎因' 與 '幹話' 其中一個條件都會被列出。
查詢 tags
欄位,有 幹話
的 document 列表,需隱藏 _id
欄位
.find() 有二個參數使用,第二個參數 <options> 用來設定隱藏或顯示屬性欄位使用。
1 | db.collection.find(<filter>, <options>) |
參考資料:
– mongoDB 官方文件:Explicitly Exclude the _id Field
– DAY9 MongoDB 文件與嵌入式(巢狀)文件查詢(Find)
先將所有 documents 的 tags 屬性欄位值為陣列中的 '幹話' 以 $in 比對取出,先確定可以取用。
1 | db.posts.find({ 'tags': { $in: ['幹話'] } }) |
在 mongoDB compass 軟體中,查到的有 65 筆資料,確任正確取得 tags 中的 幹話,目前來看也還是有帶上 _id。
隱藏 _id 欄位,設定第二個參數 <options> 值為 0 就是隱藏欄位。
1 | db.posts.find({ 'tags': { $in: ['幹話'] } }, {'_id': 0}) |
指令執行結果如下圖,每筆資料中的 _id 屬性欄位都沒顯示出來。
mongoDB compass 軟體中設定上比較特別,在第二參數 <options> 設定上要對應到第二個輸入框 PROJECT 中。
輸入指令每筆資料的屬性欄位與內容過多,可配合二個參數 <options> 值為 1 設定,只針對要顯示的屬性欄位顯示,沒列在第二個參數設為 1 就不被印出。
1 2 3 4 | db.posts.find( { 'tags': {$in: ['幹話']} }, { '_id': 1, 'tags': 1 } ) |
mongoDB compass 軟體執行後,只會出現 _id 與 tags 二個屬性欄位,而過濾條件也由 $in: [] 語法指定陣列只取出 幹話 列出。
請嘗試用 Mongo Shell 指令刪除全部 Documents
只對 hotel Database 下的 posts 集合中進行刪除,指令配合 . (點選取) 方式來操作,以 .remove() 方法執行,參數帶入 {} 空的物件就可處理。
1 | db.posts.remove({}) |
執行指令後,終端機會回應出 WriteResult({ "nRemoved" : 207 }),使用 compass 軟體查看,在 poste 集合中會完全沒有資料,出現 Import Data 的按鈕。
自主研究題
posts 所有 document 數量為?(回傳數字)
1 | db.posts.find().count() |
以重新匯入資料庫 final.json 檔,其中有 1000 筆資料, .conut() 方法可合配合鏈式方法,在將處理過的資料做計算筆數。
1 2 3 4 5 | db.posts.find( { 'comments': { '$gte': 500 } } ).count() |
指令執行回應如下。
請查詢 name
為 Ray Xu
的 document 列表,排序為由新到舊
MongoDB 官網:Ascending/Descending Sort (升序/降序排序) 提到內容如下。
在 .sort 參數中指定要排序的一個或多個字段,值 1 或 -1 分別指定升序或降序排序。
Specify in the sort parameter the field or fields to sort by and a value of 1 or -1 to specify an ascending or descending sort respectively.
1 db.users.find({ }).sort( { age : -1, posts: 1 } )
1 | db.posts.find({'name': 'Ray Xu'}).sort({'createdAt': -1}) |
在終端機與 compass 軟體以 -1 執行結果,會由新至舊排列。
在終端機與 compass 軟體以 1 執行結果,會由舊至新排列。
請查詢 name
為 Ray Xu
的 document 列表,顯示前 30 筆資料
mongoDB 官方文件:cursor.limit()
1 | db.collection.find(<query>).limit(<number>) |
終端機執行指令。
1 | db.posts.find({'name': 'Ray Xu'}).limit(30) |
compass 軟體, FLIER 輸入 {'name': 'Ray Xu'},指定限制筆數在 LIMIT 輸入 30,與 compass 軟體執行結果。
mongodb .sort() 和 .limit() 先後順序。
這兩個語法是等重;鏈接 .limit() 和 .sort() 方法的順序,會先執行 .sort(),接著才執行 .limit()。– stackoverflow – How do you tell Mongo to sort a collection before limiting the results?
請查詢 name
為 Ray Xu
,顯示100(含) 個讚以上的前 30 筆 document 列表,時間排序由新到舊
1 2 3 4 | db.posts.find({ 'name': 'Ray Xu', 'likes': { '$gte': 100 } }).limit(30) |
請查詢 comments
超過 100
的 document 列表,跳過前 30 筆資料,再顯示 30 筆資料
官方文件:cursor.skip()
1 | cursor.skip(<offset>) |
1 | db.collection.find({ <屬性欄位名>: <屬性欄位值>}).limit(<NUMBER>).skip(<NUMBER>) |
comments 超過 100,本身沒含 100 為大於使用 $gt。
在 compass 軟體以 SKIP 設定為 0 而 LIMIT 設為 30 執行結果,第一筆的資料的 _id 是 6251613fcf58670f3ea69fde,而第二筆資料 6251613fcf58670f3ea69fdf,直接列出最前面的 30 筆。
1 2 3 | db.posts.find({ 'comments': { '$gt': 100 } }).limit(30).skip(0) |
終端機使用指令,執行後比對前面筆 _id。
刻意以在 compass 軟體以 SKIP 設定為 1 而 LIMIT 設為 30 執行結果,原本的篳二筆變成第一筆,這指令操作上一樣取得 30 筆資料。
終端機設定 .skip(1) 跳過第一筆資料,接著印出後面的 30 筆。
1 2 3 | db.posts.find({ 'comments': { '$gt': 100 } }).limit(30).skip(1) |
開頭第一筆資料變成了 6251613fcf58670f3ea69fdf。
終端機輸入完整指令如下,跳過前 30 筆與只顯示前 30 筆。
1 2 3 | db.posts.find({ 'comments': { '$gt': 100 } }).limit(30).skip(30) |
取得第一筆的 _id 都是 6251613fcf58670f3ea6a001。
尋找超夯熱門貼文,請查詢 likes
且(and) comments
都超過 1,500(含) 的 document 列表
且 (and) 表示二個或多個條件都要達到才算成立。
二個條件中都需要包含 1500 以上,也就是大於等於使用 $gte 語法。
在 mongoDB 中 and 的語法為 $and,後方要透過 [] 陣列包多筆物件方式,來包裝多筆條件設定。
1 | { $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] } |
完整終端機指令如下。
1 2 3 4 5 6 | db.posts.find({ '$and': [ { 'comments': { '$gte': 1500 } }, { 'likes': { '$gte': 1500 } } ] }) |
終端機執行結果,內容太多太長。
改用 .find() 方法第二個參數設定,只顯示的設定 'comments' 與 'likes' 執行設定,終端機指令如下。
1 2 3 4 5 6 7 8 9 10 11 12 | db.posts.find( { '$and': [ { 'comments': { '$gte': 1500 } }, { 'likes': { '$gte': 1500 } } ] }, { 'comments': 1, 'likes': 1 } ) |
執行結果指令執行印出前二十筆,方便查看到二個條件都要成立,都是大於等於 1500 才被印出來。
mongoDB 的 compass 設定,輸入框輸入 { '$and': [{ 'comments': { '$gte': 1500 } }, { 'likes': { '$gte': 1500 } }] },執行結果如下。
終端機輸指令查看,全部筆數都是 56 筆。
尋找普通熱門貼文,請查詢 likes
或(or) comments
超過 1,000(含)
的 document 列表
且 (or) 表示二個或多個條件,只要一項有成立就算是成立。
多條件中都需要包含 1000 以上,也就是大於等於使用 $gte 語法。
在 mongoDB 中 or 的語法為 $or,也和 $and 語法一樣,後方要透過 [] 陣列包多筆物件方式,來包裝多筆條件設定。
1 | { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] } |
終端機使用 .find() 方法第二個參數設定,只顯示的設定 'comments' 與 'likes' 執行設定,執行指令如下。
1 2 3 4 5 6 7 8 9 10 11 12 | db.posts.find( { '$or': [ { 'comments': { '$gte': 1000 } }, { 'likes': { '$gte': 1000 } } ] }, { 'comments': 1, 'likes': 1 } ) |
執行結果指令執行印出前二十筆,只要有大於等於 1000 就會被印出來,與 $and 方法的方式不相同。
印出全部筆數
1 2 3 4 5 6 7 8 9 10 11 12 | db.posts.find( { '$or': [ { 'comments': { '$gte': 1000 } }, { 'likes': { '$gte': 1000 } } ] }, { 'comments': 1, 'likes': 1 } ).count() |
終端機與 mongoDB compass 軟體也都是共 748 筆。
查詢 image
欄位為 null
的 document 列表
1 2 3 | db.posts.find( {'image': null}, ) |
特別要注意屬性欄位的值 null 如果是輸入字串 'null',在型別上使用錯誤會找不到資料,當終端機輸入指令後會直接跳到下一行。
mongoDB compass 軟體在輸入框中輸入 {'image': null},比對指令帶上 .count() 計算筆數,都是 296 筆。
隨意找一筆 document 資料,將 tags
欄位裡的陣列,新增一個新 tags 為 遊記
參考資料:
iT邦幫忙 – 30-6之新手村CRUD—更新之陣列欄位攻略 => $push
1 | db.posts.aggregate([{ $sample: { size: 1 } }]) |
六角學院助教 致愷回應如下:
任意挑一筆資料,並將 tags 欄位的陣列新增一個新 tags 為 遊記 的內容就可以囉
所以只要使用 updateOne 語法抓取任一筆 id 加入資料即可。
執行指令後回傳訊息如下,取得的唯一屬性欄位 "_id" : ObjectId("62527f0997bab21da7356a75") 為操作對象。
1 | { "_id" : ObjectId("62527f0997bab21da7356a75"), "name" : "Nissie Burchall", "tags" : [ "感情" ], "type" : "group", "image" : "http://dummyimage.com/178x100.png/5fa2dd/ffffff", "createdAt" : "2022-03-09 21:26:44 UTC", "content" : "tortor sollicitudin mi sit amet lobortis sapien sapien non mi integer ac neque duis bibendum morbi non quam nec dui luctus rutrum nulla tellus in sagittis dui vel nisl duis ac nibh fusce lacus purus aliquet at feugiat non", "likes" : 1266, "comments" : 255 } |
使用 .updateOne() 語法進行資料對象操作,
1 2 3 4 5 6 | db.posts.updateOne( { "_id" : ObjectId("62527f0997bab21da7356a75") }, { $push: { "tags": '遊記' } } ) |
執行指令後回應訊息如下。
1 | { "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 } |
透過 .find() 方法在執行查找對象資料。
1 | db.posts.find( { "_id" : ObjectId("62527f0997bab21da7356a75") }, { "tags": 1} ) |
執行結果回應如下,成功將 "遊記" 加入屬性欄位 "tags" 的資料中。
1 | { "_id" : ObjectId("62527f0997bab21da7356a75"), "tags" : [ "感情", "遊記" ] } |
將所有 tags
陣列裡的 感情
都移除
參考資料:
– MongoDB中陣列型別相關的操作 => 3.2.1 移除陣列中等於指定值 的元素
– MongoDB 官網文件 – Remove All Items That Equal a Specified Value
– MongoDB 陣列查詢
iT邦幫忙 – 30-6之新手村CRUD—更新之陣列欄位攻略 => $pop與$pull
裡面的 mongoDB 的陣列語法拆解與整理如下。
$in 操作符語法
1 | <document_name> : { $in: [<value1>, <value2>, ... <valueN>] } |
欄位陣列資料中指定 <value2> (值),只要設定就會將該筆欄位列出,也可結合在 db.collection.find() 的參數使用。
以此題的使用方式要針對 tags 屬性欄位操作,寫成 { 'tags': { $in: [ '感情' ] } } 配合著 .find() 使用查找。
mongoDB caompass 軟體執行結果,取到 345 筆資料,列出三筆資料中 tags 屬性欄下的陣列資料,索引位置也都不太相同。
1 2 3 4 | db.posts.find( { 'tags': { $in: [ '感情' ] } }, { 'tags': 1, '_id': 0 } ) |
終端機執行結果如下,列出前二十筆。
$pull 操作符語法
參考資料:
MongoDB 官方文件:$pull
$pull 修饰符會刪除陣列中符合條件的元素,在處理時需配合 .updateMany() 等等的資料庫操作資料語法。
在 .updateMany() 更新資料庫使用二個參數,第一個參數 {} 可理解成像是 .find() 所使用預設參數 {} 空物件,指向的是全部的欄位資料。
第二個參數使用 $pull: { 'tags': "感情" } 直接指定將 'tags' 屬性欄位下的陣列資料中有的值 "感情",進行刪除的動作。
1 2 3 4 | db.posts.updateMany( { }, { $pull: { 'tags': "感情" } } ) |
執行指令後,會得到 { "acknowledged" : true, "matchedCount" : 1000, "modifiedCount" : 345 },一共處處理了 345 筆的資料。
透過 compass 軟體以指令 { 'tags': { $in: [ '感情' ] } } 查看,原先有的資料也確實都找不到。
目前的資料已操作過,使用 db.posts.deleteMany({}) 將資料庫都清空並重新倒入原本的範例 JSON 檔,FIND 查找第一筆資料的 tags 屬性欄位下的陣列中,索引位置裡有著一筆 '感情'。
在使用上,如果要一次在 'tags' 屬性欄位下陣列資料中,會進行刪除多個值的操作,可帶入多個值在 $in: [] 中去查找,比對到符合多個值的屬性欄位後的 document,會進行取出由 $pull 將該 document 中所指定多筆的值進行刪除的動作,當然這裡也可以刻意只針對陣列中一筆以 $in: [] 方式指定操作。
1 2 3 4 5 6 7 8 9 10 | db.posts.updateMany( { }, { $pull: { tags: { $in: [ '感情' ] } } } ) |
執行指令後,也和先前以 $pull 操作單筆陣列值的方式一樣,會得到 { "acknowledged" : true, "matchedCount" : 1000, "modifiedCount" : 345 } 一樣的訊息。
以 mongoDB compass 軟體重新查看第一筆有 '感情' 的 tags 屬性欄位下的陣列資料以不在其中。
再以指令 { 'tags': { $in: [ '感情' ] } } 查看,也都查不到相關的資料列出。