之前實習專案有玩過MongoDB,剛好前幾天在圖書館看到黃士嘉老師寫的書 7天學會大數據處理資料NoSQL-MongoDB入門與活用 真的是一個禮拜看完這本書,大概是一天看3個小時加上實作,覺得讓我更加了解MongoDB,因此來做一下筆記,把重點和語法記錄下來,希望日後用到能更加上手。
MongoDB幾個特色
P.S 常常聽到有同事說NoSQL其實意思就是Not Only SQL的意思
- 在Relational Database中的表格(table)類似MongoDB的Collection(集合),而資料列(Row)類似MongoDB的document(文件)
- MongoDB提供了Map-Reduce 的聚合工具來進行資料的統計
- 為無綱要(Schemaless) 設計(->像是char(30)先給予限制),不需要事先定義資料庫的綱要,比傳統關聯式資料庫更加有彈性
- 可以儲存非文件的大型物件,MongoDB除了可以儲存文件(最大16MB),也可以儲存非文件的物件(圖片或影片),但需使用MongoDB的GirdFS(為MongoDB所定義的一種規範)
- MongoDB一種強大,靈活、且易於擴展的文件導向式(document-oriented)資料庫
- MongoDB中的文件是採取JSON的格式作為儲存資料的方式
- NoSQL DB 強調的是效能與可用度,所以「CAP Theorem」會比 RDBMS 的「ACID」更為重要
- 常採用本地儲存、而非共同儲存設備(如 SAN 或 NAS),成本隨之下降,也更加彈性
- 相較於 RAID (mirroring / stripping)或同步複製機制,NoSQL DB採用的是非同步的複製。這種方式較不會受到額外網路流量影響,所以能使寫入動作更快完成。又因為資料不會立即複製,所以某些時候可能發生資料遺失的狀況。此外,也沒有 lock 機制以保護某些特定資料。
- 優點是應用在網站登出入、擴充快取記憶體、大型檔案儲存有較優的表現,相反的在需要高交易的系統(像是銀行/會計系統)就不太適合,因為可能會有資料不一致的現象
安裝
MongoDB壓縮檔案目錄以及常用的檔案
可以把bin資料結新增至環境變數內,就不用每次都要在bin資料夾裡面開啟cmd
C:\Program Files\MongoDB\Server\4.0\bin
在啟動MongoDB前,必須啟動下面兩個指令用來產指定db與log目錄
mongod --dbpath=C:\Program Files\MongoDB\Server\4.0\data
mongod --logpath=C:\Program Files\MongoDB\Server\4.0\log\mongod.log
啟動MongoDB服務
使用命令提示字元啟動一個MongoDB服務 (電腦重啟時,必須要重新在自己開啟)(若此事窗關閉,即關閉MongoDB服務)
mongod --config "C:\Program Files\MongoDB\Server\4.0\bin\mongod.cfg"
使用Windo Service啟動 MongoDB 服務,即可以在Window服務視窗中出現MongoDB項目 (要以系統管理員身分來執行)
mongod --config "C:\Program Files\MongoDB\Server\4.0\bin\mongod.cfg" -install
之後要啟動或停止只要輸入如下指令
net start MongoDB //啟動 net stop MongoDB //停止 mongod -remove //在Window service移除MongoDB服務
mongod.cfg (幾項重要配置)
storage: dbPath: C:\Program Files\MongoDB\Server\4.0\data journal: enabled: true systemLog: destination: file logAppend: true (加入後就不會每次關閉開啟服務時建立一個檔案,都集中在一個檔案) path: C:\Program Files\MongoDB\Server\4.0\log\mongod.log net: port: 27017 bindIp: 127.0.0.1
連結MongoDB 伺服器
mongo
連結MongoDB 伺服器
mongo admin -u 使用者名稱 -p 密碼
CRUD操作
- 所有操作的運算子都有大小寫之分
- 用Robo 3T就在背景黑黑的Shell中輸入語法
- CRUD語法在NoSQL之間有點差異,不像是SQL都差不多@@
- 通常都是要先指定field名稱,其後再放運算子搜尋條件
Query (查詢)
db.getCollection('library').find({}) //搜尋全部不設條件,也等同於db.library.find({})
db.library.find({name:"book of CUU"}) //就是 db.collection名.find({key:value}) key:value就是搜尋條件
db.library.find({name:/^方.文$/, id:/^F129/}, {_id:false}) //以正則表示法來搜尋符合的條件字串
db.getCollection('library').find({},{name:true,person:true}) //find的第2個參數為映射所要查詢顯示需求的值,有點像是select
db.getCollection('library').find({price:{$eq:300}}) //設定條件,找尋price等於300的document
db.getCollection('library').find({price:{$ne:300}}) //找尋price不等於300
db.getCollection('library').find({price:{$gt:300}}) //找尋price大於300
db.getCollection('library').find({price:{$lt:300}}) //找尋price小於300
db.getCollection('library').find({price:{$gte:300}}) //找尋price大於等於300
db.getCollection('library').find({price:{$lte:300}}) //找尋price小於等於300
db.getCollection('library').find({member:{$in:[“Kevin”]}}) //找尋資料型態為陣列的members欄位包含Kevin
db.getCollection('library').find({member:{$nin:[“Kevin”]}})
db.getCollection('chatroom').find({messages:{$size:0}})
db.getCollection('chatroom').find({messages:{$elemMatch:{content:”Hello”}}})
db.getCollection('library').find({name:{$regex:"cuu", $options:"i"}})
db.getCollection('chatroom').find({members:{$exists:true}})
db.getCollection('contacts').find({}).sort({age:-1}) //此為查詢後排序,...sort({field:1 or -1)),1表示從小到大,-1表示從大到小排序
查詢常用運算子
運算子 功能說明 語法範例 $eq 查詢某個欄位等於某值 db.getCollection('library').find({price:{$eq:300}}) $ne 查詢某個欄位不等於某值 db.getCollection('library').find({price:{$ne:300}}) $gt 查詢某個欄位大於某值 db.getCollection('library').find({price:{$gt:300}}) $lt 查詢某個欄位小於某值 db.getCollection('library').find({price:{$lt:300}}) $gte 查詢某個欄位大於等於某值 db.getCollection('library').find({price:{$gte:300}}) $lte 查詢某個欄位小於等於某值 db.getCollection('library').find({price:{$lte:300}}) $in 查詢某個陣列存在某值 db.getCollection('library').find({member:{$in:["Kevin"]}}) $nin 查詢某個陣列不存在某值 db.getCollection('library').find({member:{$nin:["Kevin"]}}) $size 查詢某個陣列大小等於某值 db.getCollection('chatroom').find({messages:{$size:0}}) $elemMatch 查詢某個陣列內部元素等於某條件 …..find({messages:{$elemMatch:{content:/走/}}}) ->任何包含走這個字的元素,EX: "走啊", "快走" /…..find({messages:{$elemMatch:{content:"Hello"}}}) >限定只能"Hello" $or(或$and) 將多條查詢式做邏輯運算 db.getCollection('chatroom').find({$and:[{members:{$in:["Bob"]}}, {messages:{$size:0}}]}) $exists 查詢某個欄位存不存在 db.getCollection('chatroom').find({members:{$exists:true}}) ->true就是要查詢存所有存在此欄位名稱的document,也可以用false來查詢 $type 查詢欄位資料型別等於BSON Type db.getCollection('library').find({studentId:{$type:16}}) ->BSON在google就有了,常用幾個-> Double是2,String是2,int是16, Object是3, Array是4.... $regex 正則表示式來定義字串搜尋條件來查詢 db.getCollection('library').find({name:{$regex:"cuu", $options:"i"}}) ->options有幾個擴充功能,i 就是不管大小寫, m為支援多行, x為忽略Pattern空字串 $where 透過以JavaScript Expression 的字串來進行查詢 db.getCollection('library').find({$where:"this.cost>=199"}) $slice 映射運算子,主要是用來取得欄位陣列前、中、後段的元素 db.getCollection('chatroom').find({},{messages:{$slice:2}}) ->表示取得欄位陣列最前面的兩個元素 db.getCollection('chatroom').find({},{messages:{$slice:-2}}) ->表示取得欄位陣列最後面的兩個元素 db.getCollection('chatroom').find({},{messages:{$slice:[1,3]}}) ->1表示為略過此陣列前面元素的數量,3為取得陣列元素的數量
Insert (新增)
db.getCollection('members').insert({_id:123, name:"方凱凱", age:25, phone:"0975-264565"}) db.contacts.update({name:"方文均"},{$set:{age:NumberInt(18)}}) //insert進去預設資料型態是Double,我想要integer就要加上NumberInt()
Update (修改)
db.members.update({name:"方文均"},{$set:{age:18}}) //=> 第一個參數{name:"方文均"} 為查詢式(Query),第二個參數{$set:{age:18} 為更新式 db.accounts.update( // query 查詢式 { "name" : "小明" }, // update 修改式 { $inc:{balance:NumberInt(-500)} }, // options { "multi" : false, // update only one document "upsert" : false // insert a new document, if no existing document match the query } ); db.accounts.update({name : "小明"},{$mul:{balance:NumberInt(2)}}); //把欄位的值做乘法 db.accounts.update({name : "小明"},{$set:{balance:NumberInt(1234)}}); //直接更改欄位的值 db.accounts.update({name : "小明"},{$rename:{balance:"newbalance"}}); //直接變更欄位的名稱,條件是{name : "小明"} db.accounts.update( //高於400的都降為 400,Query沒有要設定還是要保留{} {}, {$min:{balance:NumberInt(400)}}, {"multi":true,"upsert":false} ); db.accounts.update( //刪除所有balance欄位 {}, {$unset:{balance:""}}, {"multi":true,"upsert":false} ); db.getCollection('drivers').update({"_id" : "002"},{$currentDate:{status:"rest"},$currentDate:{"lastModified":{$type:"timestamp"}}}); //更新時間,$type可以是date或timestamp db.getCollection('drivers').update({"_id" : "001"},{$push:{location:NumberInt(10)}}); //在欄位的是陣列型態,增加元素到最後面 db.getCollection('drivers').update({"_id" : "001"},{$push:{location:{$each:[NumberInt(10)],$position:2}}}); //在欄位的是陣列型態,增加元素到指定位址 => {$push:{<field>:{$each:[],$position:<num>}} db.getCollection('drivers').update({"_id" : "001"},{$pop:{location:1}}); //移除元素,1表示移除最後面元素,-1表示移除最前面元素 db.getCollection('drivers').update({"_id" : "001"},{$pull:{location:NumberInt(60)}}); //移除指定值60
運算子 |
功能 |
$inc |
針對欄位值進行增/減某值的操作 |
$mul |
針對欄位值進行乘某值的操作 |
$set |
針對欄位值進行更改值的操作 |
$rename |
針對欄位值進行更改欄位名稱的操作 |
$max |
將低於門檻值的欄位,提高至門檻值 |
$min |
將高於門檻值的欄位,降低至門檻值 |
$unset |
針對欄位值進行刪除的操作 |
$currentDate |
針對時間欄位值進行更新到當前時間的操作 |
Delete (刪除)
db.contacts.remove({age:15})
remove()的第一個參數{age:15} 為查詢式(Query),只要符合條件的就刪除
補充:
*Map-Reduce來進行資料統計分析,其實是鈄果Collection內的欄位或者是組合欄位來做為分群標準,分完群後即可以分別進行資料統計
*Function: 可以建立腳本,有點像是SQL Server的Stored Procedure,我們可以將查詢的指令定義在腳本(Function)裡面,並且重複使用它,不用每一次都重複輸入查詢
- Provides the ability to run JavaScript code on the MongoDB server.
- 在Function資料夾按右鍵,點選Add Function,但怎樣加都沒用,只能在Shell裡面,自己打指令,MongoDB不推薦人在DB上面做邏輯運算,參考如下
- https://zhuanlan.zhihu.com/p/59895938
- https://stackoverflow.com/questions/46419071/in-robo3t-how-to-add-a-custom-function
- https://stackoverflow.com/questions/30461729/error-in-db-eval-in-mongodb
存入建立腳本
db.system.js.save( { _id: "findSpecial", value: function findSpecial() { var results = []; //宣告一個空陣列 query = {"age":{$gt:22}} //設定查詢式 cursor = db.contacts.find({query}) //對Collection contacts進行查詢,並把查詢結果放於cursor中 while(cursor.hasNext()) //看是否集合內還有資料 { var document = cursor.next(); //從cursor集合內取出一筆資料 results.push(document); //把資料放入陣列中 } return document; } } )
執行腳本 (在Shell執行)
db.eval("findSpecial()") //-------------or-------------------- db.runCommand({ eval:"findSpecial()", args: null, nolock: true });
遠端連線:
* 每次在cfg修改設定,都需要重新開啟設定才有效,意思就是要先net stop MongoDB,接著再開啟
Step1. 找到 mongod.cfg
預設位址:
C:\Program Files\MongoDB\Server\4.0\bin\mongod.cfg
Step2. 開啟 mongod.cfg,並修改network那部分,更改內容如下
# network interfaces net: port: 27123 bindIp: 0.0.0.0,140.123.173.224 #0.0.0.0 表示本機可直接localhost連接,140.123.173.224則表示我要給外部的連線ip security: authorization: enabled #表示登入資料庫需要帳號密碼
*設定權限也可以輸入指令:
mongod --auth --dbpath=YOURDBPATH --logpath=YOURLOGFILEPATH
*mongod.cfg 設定完後示意圖
*前後對比:
step3. 存取權限設定,在要遠端連線之本機設定,即在Robo 3T的Shell輸入即可
* 正式上線沒有設定權限就非常危險,所以我們要加入帳號登入機制以保護我們的資料
* 官方文件參考: https://docs.mongodb.com/manual/reference/method/db.createUser/
use admin #切到admin 資料庫 db.createUser( { user: "myUserAdmin", #帳號 pwd: "123", #密碼 roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ] } )
* 只開啟特定DB給特定使用者,為資料庫獨立帳號
* 可以針對多個資料庫進行不同的權限處理,在roles的陣列裡面操作即可
db.createUser( { user: "myUserTest", #帳號 pwd: "123", #密碼 roles: [ { role: "read", db: "test" }] #開放test 資料庫,權限只有read,也可以設定readWrite等等,需要參考官方文件 } )
* 可以在Robo 3T的system的admin資料庫找到
step4. 使用 Robo 3T 連線,就沒問題了
*如果是用cmd命令提示字元連線就需要輸入語法 mongo -u myUserAdmin -p abc123
mongo -u yourAccount -p yourPassword
Reference:
7天學會大數據處理資料NoSQL-MongoDB入門與活用/ 黃士嘉
https://docs.mongodb.com/manual/introduction/
http://garyliutw.blogspot.com/2014/05/mongodb-nosql.html
部分參考: https://ithelp.ithome.com.tw/articles/10184679
遠端連線: https://qops.blogspot.com/2019/02/mongodb-ubuntu-1804.html