close

之前實習專案有玩過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表示從大到小排序

before.jpg after.jpg

查詢常用運算子

運算子            功能說明                 語法範例

$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)裡面,並且重複使用它,不用每一次都重複輸入查詢

存入建立腳本

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://atedev.wordpress.com/2007/11/23/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%A4%BA%E5%BC%8F-regular-expression/

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

遠端連線: https://ithelp.ithome.com.tw/articles/10186324

arrow
arrow
    文章標籤
    MongoDB NOSQL
    全站熱搜

    KV 發表在 痞客邦 留言(0) 人氣()