1. 程式人生 > >MongoDB安裝及基本使用

MongoDB安裝及基本使用

MongoDB是一個使用C++編寫的、開源的、面向文件的NoSQL(Not Only SQL)資料庫,也是當前最熱門的NoSql資料庫之一。

NoSQL簡介

NoSQL的意思是“不僅僅是SQL”,是目前流行的“非關係型資料庫”的統稱。常見的NoSQL資料庫如:Redis、CouchDB、MongoDB、HBase、Cassandra等。

背景

出現NoSQL的原因:為解決在Web2.0時代出現的三高要求:

  1. 對資料庫高併發讀寫的需求
  2. 對海量資料的高效率儲存和訪問的需求
  3. 對資料庫的高可擴充套件性和高可用性的需求
  4. 而RDB裡面的一些特性,在web2.0裡面往往變得不那麼重要,比如:
    1. 資料庫事務一致性
    2. 資料庫的實時讀寫
    3. 複雜的SQL查詢,特別是多表關聯查詢

從第4點可以看出MongoDB的使用場景,但具體的我在後面重點介紹。

CAP定理

又被稱作布魯爾定理(Eric Brewer)它指出對於一個分散式計算系統來說,不可能同時滿足以下三點:

  1. 強一致性(Consistency):系統在執行某項操作後資料狀態仍然處於一致,例如在分散式系統中,更新操作執行成功後所有的使用者都應該讀取到最新的值,這樣的系統被認為具有強一致性。
  2. 可用性(Availability):每一個操作總是能夠在一定的時間內返回結果
  3. 分割槽容錯性(Partition tolerance):單個節點故障不應導致整個系統崩潰,也就是說盡管網路在節點之間丟棄(或延遲)任意數量的訊息,但是系統繼續操作。

根據CAP原理將資料庫分成了滿足CA原則、滿足CP原則和滿足AP原則三大類

  1. CA:單點叢集,滿足一致性,可用性,通常在可擴充套件性上不太強大,比如RDB
  2. CP:滿足一致性和分割槽容錯性,通常效能不是特別高,如分散式資料庫
  3. AP:滿足可用性和分割槽容錯性,通常可能對一致性要求低一些,如大多數的NoSQL

BASE(Basically Available,Soft-state,Eventual consistency)

eBay的架構師Dan Pritchett源於對大規模分散式系統的實踐總結,在ACM上發表文章提出BASE理論,BASE理論是對CAP理論的延伸,核心思想是即使無法做到強一致性(Strong Consistency,CAP的一致性就是強一致性),但應用可以採用適合的方式達到最終一致性(Eventual Consitency)。

  1. 基本可用(Basically Available):系統能夠基本執行並一直提供服務
  2. 軟狀態(Soft-state):系統不要求一直保持強一致狀態
  3. 最終一致性(Eventual consistency):系統需要在某一時刻後達到一致性要求

NoSQL的特點

優點

  1. 擴充套件簡單方便,尤其是水平橫向擴充套件(縱向擴充套件是指用更強的機器;橫向擴充套件是指把資料分散到多個機器)
  2. 讀寫快速高效,多數都會對映到記憶體操作
  3. 成本低廉,用普通機器,分散式叢集即可
  4. 資料模型靈活,沒有固定的資料模型

缺點

  1. 不提供對SQL的支援
  2. 對事務操作的支援較弱

MongoDB使用場景

相信有很多人瞭解之後都會覺得,nosql好是好但我該在哪些情況下使用,又如何與實際專案結合呢?接下來我舉幾個例子,大家自己體會體會。

  • 遊戲場景,使用 MongoDB 儲存遊戲使用者資訊,使用者的裝備、積分等直接以內嵌文件的形式儲存,方便查詢、更新
  • 物流場景,使用 MongoDB 儲存訂單資訊,訂單狀態在運送過程中會不斷更新,以 MongoDB 內嵌陣列的形式來儲存,一次查詢就能將訂單所有的變更讀取出來。
  • 社交場景,使用 MongoDB 儲存儲存使用者資訊,以及使用者發表的朋友圈資訊,通過地理位置索引實現附近的人、地點等功能
  • 物聯網場景,使用 MongoDB 儲存所有接入的智慧裝置資訊,以及裝置彙報的日誌資訊,並對這些資訊進行多維度的分析
  • 視訊直播,使用 MongoDB 儲存使用者資訊、禮物資訊等

並沒有某個業務場景必須要使用 MongoDB才能解決,但使用 MongoDB 通常能讓你以更低的成本解決問題(包括學習、開發、運維等成本),下面是 MongoDB 的主要特性,大家可以對照自己的業務需求看看,匹配的越多,用 MongoDB 就越合適。

MongoDB 特性 優勢
事務支援 MongoDB 目前只支援單文件事務,需要複雜事務支援的場景暫時不適合
靈活的文件模型 JSON 格式儲存最接近真實物件模型,對開發者友好,方便快速開發迭代
高可用複製集 滿足資料高可靠、服務高可用的需求,運維簡單,故障自動切換
可擴充套件分片叢集 海量資料儲存,服務能力水平擴充套件
高效能 mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支援滿足各種場景需求
強大的索引支援 地理位置索引可用於構建 各種 O2O 應用、文字索引解決搜尋的需求、TTL索引解決歷史資料自動過期的需求
Gridfs 解決檔案儲存的需求
aggregation & mapreduce 解決資料分析場景需求,使用者可以自己寫查詢語句或指令碼,將請求都分發到 MongoDB 上完成

安裝

mongodb的安裝方式比較簡單,下面我演示在CentOS7上用原始碼和yum兩種方式安裝。

使用yum方式安裝

在實際生產環境中伺服器上的軟體通常都由運維人員安裝,我們通過此種傻瓜式安裝是為了方便現在學習。

  • 整個mongodb(社群版)包含如下軟體

    軟體名稱 描述
    mongodb-org-server 包含mongod守護程式和關聯的配置和init指令碼
    mongodb-org-mongos 包含mongos守護程式
    mongodb-org-shell 包含mongo shell,它是一個連線mongodb的命令列客戶端,允許使用者直接輸入nosql語法管理資料庫。
    mongodb-org-tools 包含以下工具的MongoDB:資料匯入、匯出、備份、恢復等等
  • 建立yum原始檔

vim /etc/yum.repos.d/mongodb-org-3.4.repo
  • 把下面的內容複製到上面的檔案中
[mongodb-org-3.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc
  • 啟動yum命令開始安裝
yum install -y mongodb-org
  • 如果使用SELinux,則必須配置SELinux,以允許在基於Red Hat Linux的系統(Red Hat Enterprise Linux或CentOS Linux)上啟動MongoDB。
vim /etc/selinux/config
#在開啟的檔案中將值設定為disabled
SELINUX=disabled
  • 啟動mongodb
#centos6用這種方式
service mongod start

#centos7用這種方式
systemctl start mongod
  • 檢查程序是否成功啟動
    可以通過檢視mongodb的日誌檔案來判斷,開啟 /var/log/mongodb/mongod.log 檢視裡面有無內容。我們可以將mongodb服務設定為開機就啟動 chkconfig mongod on

  • 其它控制命令

#停止mongodb服務
service mongod stop
#重啟mongodb
service mongod restart

命令列客戶端連線到MongoDB服務

./bin/mongo
#上面的命令輸入後直接回車,你應該可以看到和我相同的提示

[email protected] mongodb-linux-x86_64-rhel70-3.4.1]# ./bin/mongo
MongoDB shell version v3.4.1
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.1
Welcome to the MongoDB shell.

#我們可以輸入一個命令測試一下,現在教你一個最簡單的命令,檢視當前資料庫有哪些
> show dbs
admin  0.000GB
local  0.000GB
#可以看到有兩個預設的資料庫,如果要退出客戶端可以輸入
> exit
bye

到這裡第一種方式的安裝就結束了

使用原始碼安裝

運維人員都比較喜歡原始碼安裝,他們覺得可控性會比較強,但現在公司專案中都使用雲服務,所以作為開發者直接連線上就可以用了。我這裡使用的原始碼包實際上是二進位制包,也就是說已經被官方編譯過了,所以下載二進位制包的時候一定要確定你用的作業系統,否則包是不可用的。

  • 下載二進位制安裝包並解壓
cd /usr/local/src
wget -c https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.4.1.tgz
gunzip mongodb-linux-x86_64-rhel70-3.4.1.tgz
tar -xvf mongodb-linux-x86_64-rhel70-3.4.1.tar

#你現在應該可以看到和我相同的檔案目錄
.
├── bin
│   ├── bsondump
│   ├── mongo
│   ├── mongod
│   ├── mongodump
│   ├── mongoexport
│   ├── mongofiles
│   ├── mongoimport
│   ├── mongooplog
│   ├── mongoperf
│   ├── mongoreplay
│   ├── mongorestore
│   ├── mongos
│   ├── mongostat
│   └── mongotop
├── GNU-AGPL-3.0
├── MPL-2
├── README
└── THIRD-PARTY-NOTICES
  • 建立資料儲存目錄,預設是在 /data/db,如果這個目錄不存在則啟動時報錯。你可以自由建立一個目錄,在啟動時指定即可。
mkdir -p data/db1

#開始啟動服務
./bin/mongod --dbpath=data/db1

#你應該可以看到和我相同的結果
017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten]
2017-01-08T13:54:55.378+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-01-08T13:54:55.379+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-01-08T13:54:55.379+0800 I CONTROL  [initandlisten]
2017-01-08T13:54:55.446+0800 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory 'data/db1/diagnostic.data'
2017-01-08T13:54:55.447+0800 I NETWORK  [thread1] waiting for connections on port 27017
  • 細心的你應該發現一個問題,當前服務是前臺啟動的,如果你想做後續操作那必須再開啟一個終端。這樣肯定麻煩,那我們也可以採用後臺啟動的,在啟動時必須指定一個日誌檔案。
mkdir logs
touch logs/db1.log
./bin/mongod --dbpath=data/db1 --fork --logpath=logs/db1.log

到這裡原始碼安裝並啟動mongodb就結束了,下面對啟動時的可配置引數作一個介紹。

  • 基本配置
--quiet # 安靜輸出
--port arg # 指定服務埠號,預設埠27017
--bind_ip arg # 繫結服務IP,若繫結127.0.0.1,則只能本機訪問,不指定預設本地所有IP
--logpath arg # 指定MongoDB日誌檔案,注意是指定檔案不是目錄
--logappend # 使用追加的方式寫日誌
--pidfilepath arg # PID File 的完整路徑,如果沒有設定,則沒有PID檔案
--keyFile arg # 叢集的私鑰的完整路徑,只對於Replica Set 架構有效
--unixSocketPrefix arg # UNIX域套接字替代目錄,(預設為 /tmp)
--fork # 以守護程序的方式執行MongoDB,建立伺服器程序
--auth # 啟用驗證
--cpu # 定期顯示CPU的CPU利用率和iowait
--dbpath arg # 指定資料庫路徑
--diaglog arg # diaglog選項 0=off 1=W 2=R 3=both 7=W+some reads
--directoryperdb # 設定每個資料庫將被儲存在一個單獨的目錄
--journal # 啟用日誌選項,MongoDB的資料操作將會寫入到journal資料夾的檔案裡
--journalOptions arg # 啟用日誌診斷選項
--ipv6 # 啟用IPv6選項
--jsonp # 允許JSONP形式通過HTTP訪問(有安全影響)
--maxConns arg # 最大同時連線數 預設2000
--noauth # 不啟用驗證
--nohttpinterface # 關閉http介面,預設關閉27018埠訪問
--noprealloc # 禁用資料檔案預分配(往往影響效能)
--noscripting # 禁用指令碼引擎
--notablescan # 不允許表掃描
--nounixsocket # 禁用Unix套接字監聽
--nssize arg (=16) # 設定信資料庫.ns檔案大小(MB)
--objcheck # 在收到客戶資料,檢查的有效性,
--profile arg # 檔案引數 0=off 1=slow, 2=all
--quota # 限制每個資料庫的檔案數,設定預設為8
--quotaFiles arg # number of files allower per db, requires --quota
--rest # 開啟簡單的rest API
--repair # 修復所有資料庫run repair on all dbs
--repairpath arg # 修復庫生成的檔案的目錄,預設為目錄名稱dbpath
--slowms arg (=100) # value of slow for profile and console log
--smallfiles # 使用較小的預設檔案
--syncdelay arg (=60) # 資料寫入磁碟的時間秒數(0=never,不推薦)
--sysinfo # 列印一些診斷系統資訊
--upgrade # 如果需要升級資料庫
  • Replicaton 引數
--fastsync # 從一個dbpath裡啟用從庫複製服務,該dbpath的資料庫是主庫的快照,可用於快速啟用同步
--autoresync # 如果從庫與主庫同步資料差得多,自動重新同步,
--oplogSize arg # 設定oplog的大小(MB)
  • 主、從引數
--master # 主庫模式
--slave # 從庫模式
--source arg # 從庫 埠號
--only arg # 指定單一的資料庫複製
--slavedelay arg # 設定從庫同步主庫的延遲時間
  • 副本集
--replSet arg # 設定副本集名稱
  • 分片
--configsvr # 宣告這是一個叢集的config服務,預設埠27019,預設目錄
/data/configdb
--shardsvr # 宣告這是一個叢集的分片,預設埠27018
--noMoveParanoia # 關閉偏執為moveChunk資料儲存

解除安裝

如果你想解除安裝上面安裝mongodb,那麼可以參考下面的步驟。當然這個需求在實際部署時不會有,但是作為我們練習也是有用的。

要從系統中完全刪除MongoDB,你必須刪除MongoDB應用程式本身,配置檔案和任何包含資料和日誌的目錄。此過程不可逆,因此請確保在繼續之前備份所有配置和資料。

yum erase $(rpm -qa | grep mongodb-org)
rm -r /var/log/mongodb
rm -r /var/lib/mongo

如果是採用原始碼(二進位制)包安裝,刪除解壓出來的目錄即可(因為我們上面把資料和日誌都放進去了),如果你指定在其它位置,那找到相應位置刪除即可。

MongoDB基本使用

基本概念

  • 資料庫
    MongoDB的一個例項可以擁有一個或多個相互獨立的資料庫,每個資料庫都有自己的集合(表)。
  • 集合
    集合可以看作是擁有動態模式的表
  • 方檔
    文件是MongoDB中基本的資料單元,類似於RDB的行。文件是鍵值對的一個有序集合。在JS中,文件被表示成物件。
  • _id
    每個文件都有個特殊的 _id,在文件所屬集合中是唯一的,預設由mongodb自己維護,當然你也可以自己指定。
  • JavaScript shell
    MongoDB自帶了一個功能強大的JavaScript Shell,可以用於管理或操作MongoDB
  • MongoDB和RDB的概念對比
    1. 都有資料庫的概念
    2. 集合 –>RDB的表
    3. 文件 –>RDB表中的一條記錄
    4. 文件物件裡面的 key –> RDB表中的欄位
    5. 文件物件裡面的 value–> RDB表中欄位的值
    6. MongoDB中沒有外來鍵的概念

資料庫名稱定義規則

1:不能是空串
2:不得含有/、\、?、$、空格、空字元等等,基本只能使用ASCII中的字母和數字
3:區分大小寫,建議全部小寫
4:最多為64位元組
5:不得使用保留的資料庫名,比如:admin,local,config

注意:資料庫最終會成為檔案,資料庫名就是檔案的名稱

集合名稱定義規則

1:不能是空串
2:不能包含\0字元(空字元),這個字元表示集合名的結束,也不能包含”$”
3:不能以”system.”開頭,這是為系統集合保留的字首

文件的鍵的定義規則

1:不能包含\0字元(空字元),這個字元表示鍵的結束
2:“.”和“$”是被保留的,只能在特定環境下用
3:區分型別,同時也區分大小寫
4:鍵不能重複

注意:文件的鍵值對是有順序的,相同的鍵值對如果有不同順序的話,也是不同的文件

MongoDB基本的資料型別

資料型別 描述 舉例
null 表示空值或者未定義的物件 {“x”:null}
布林值 真或者假:true或者false {“x”:true}
32位整數 shell不支援該型別,預設會轉換成64位浮點數,也可以使用NumberInt類 {“x”:NumberInt(“3”)}
64位整數 shell不支援該型別,預設會轉換成64位浮點數,也可以使用NumberLong類 {“x”:NumberLong(“3”)}
64位浮點數 shell中的數字就是這一種型別 {“x”:3.14,”y”:3}
字串 UTF-8字串 {“foo”:”bar”}
符號 shell不支援,shell會將資料庫中的符號型別的資料自動轉換成字串
物件id 文件的12位元組的唯一id {“id”: ObjectId()}
日期 從標準紀元開始的毫秒數 {“date”:new Date()}
正則表示式 文件中可以包含正則表示式,遵循JavaScript的語法 {“foo”:/foobar/i}
程式碼 文件中可以包含JavaScript程式碼 {“x”:function() {}}
未定義 undefined {“x”:undefined}
陣列 值的集合或者列表 {“arr”: [“a”,”b”]}
內嵌文件 文件可以作為文件中某個key的value {“x”:{“foo”:”bar”}}

增刪改(CUD)操作

接下來我在命令列客戶端中操作資料,這裡假設你已經進入命令列。如果你沒有與我保持同步,請再看一看上面的教程。

# 執行shell,命令:mongo ip:port,在這裡我使用的預設的埠所以就沒有指定
[[email protected] mongodb-linux-x86_64-rhel70-3.4.1]# ./bin/mongo

# 顯示現有的資料庫,命令:show dbs;
> show dbs
admin  0.000GB
local  0.000GB

# 顯示當前使用的資料庫,命令:db
> db
test

# 切換當前使用的資料庫,命令:use 資料庫名稱
> use local
switched to db local

# 建立資料庫:MongoDB沒有專門建立資料庫的語句,可以使用“use” 來使用某個資料庫,如果要使用的資料庫不存在,那麼將會建立一個,會在真正向該庫加入文件後,儲存成為檔案。
> use mall
switched to db mall

# 刪除資料庫,命令:db.dropDatabase(),在使用這個命令前要記得切換到將被刪除的資料,可以用上面學到的命令確認一下。
> db
mall
> db.dropDatabase()
{ "dropped" : "mall", "ok" : 1 }

# 顯示現有的集合,命令:show collections,我們切換到一個新的資料庫,到裡面插入一個集合並儲存一條資料,這樣做只是為了造些語句便於演示這個命令。
> use mall
switched to db mall
> db.user.insert({name:'張三'})
WriteResult({ "nInserted" : 1 })
> show collections
user

# 建立集合:在MongoDB中不用建立集合,因為沒有固定的結構,直接使用db.集合名稱.命令 來操作就可以了。如果非要顯示建立集合的話,用:db.createCollecion('集合名稱');
> db.createCollection('system')
{ "ok" : 1 }

# 插入並儲存文件
# insert方法,可以單獨插入一個文件,也可以插入多個,用“[ ]”即可。注意:
# 1:MongoDB會為每個沒有“_id”欄位的文件自動新增一個”_id”欄位
# 2:每個Doc必須小於16MB
# 上面已經有演示,這裡就不再贅述

# 刪除文件,命令:remove , 可以按條件來刪除只是刪除文件,集合還在,如果使用 drop命令,會連帶集合和索引都刪掉
# 檢視集合中所有的文件,命令:db.集合名稱.find();
> db.user.find()
{ "_id" : ObjectId("5871e5f99423674edcea4eec"), "name" : "張三" }
> db.user.remove({})
WriteResult({ "nRemoved" : 1 })
> db.user.find()
>

# 檢視集合的狀態資訊:db.集合名.stats();
> db.user.stats()
{
    "ns" : "mall.user",
    "size" : 0,
    "count" : 0,
    "storageSize" : 20480,
    "capped" : false,
    "wiredTiger" : {...},
    ...
}

# 檢視集合中第一個文件,命令:db.集合名稱.findOne({條件物件}),造一些資料方便測試
> db.user.insert([{name:'李四'},{name:'王五'}])
BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 2,
    "nUpserted" : 0,
    "nMatched" : 0,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})
> db.user.findOne()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }

# 文件替換,命令: db.集合名稱. update(條件,新的文件);
> db.user.update({name:'王五'}, {name:'王五',sex:'男'})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
# 上面的操作說明:先找到名稱是王五的這一條記錄,然後用新文件替換資料原來的所有值,是一個整體替換的操作。

# save方法
# 如果文件存在就更新,不存在就新建,主要根據”_id”來判斷。可以看到我們插入了一個名稱相同的文件,結果是成功的。
> db.user.save({name:'李四'})
WriteResult({ "nInserted" : 1 })
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }

# upsert
# 找到了符合條件的文件就更新,否則會以這個條件和更新文件來建立一個新文件。指定update方法的第三個引數為true,可表示是upsert
> db.user.update({name:"張三"}, {name:"張三",sex:'女'})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
# 上面的修改沒有成功,原因就在於根據條件查詢不到張三這個文件,那下面我們給他換上第三個引數。我們希望找不到就增加,找到就修改。
> db.user.update({name:"張三"}, {name:"張三",sex:'女'}, true)
WriteResult({
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("5871ecba7f264dfcaeec2489")
})
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "女" }
#大家請看上面的操作,從查詢出來的結果中我們看到,要修改的記錄沒有找到就變成新增操作了。

# 查詢更新了多少個文件
# 使用命令:getLastError ,返回最後一次操作的相關資訊,裡面的n就是更新的文件的數量。
> db.runCommand({"getLastError":1})
{
    "connectionId" : 3,
    "n" : 0,
    "syncMillis" : 0,
    "writtenTo" : null,
    "err" : null,
    "ok" : 1
}

更新修改器,用來做複雜的更新操作

細心的人可能已經發現,上面介紹的修改操作是整體替換的,如果想區域性修改怎麼辦呢?這節就告訴你具體的方法。

# 我準備了測試資料如下
> db.user.find()
{ "_id" : ObjectId("5871e8fe9423674edcea4eed"), "name" : "李四" }
{ "_id" : ObjectId("5871e8fe9423674edcea4eee"), "name" : "王五", "sex" : "男" }
{ "_id" : ObjectId("5871eb8e9423674edcea4eef"), "name" : "李四" }
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "女" }

# 1$set :指定一個欄位的值,如果欄位不存在,會建立一個
> db.user.update({name:'張三'}, {$set:{sex:'保密'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密" }
# 如果指定修改的欄位不存在則新增加
> db.user.update({name:'張三'}, {$set:{nick:'小三'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "nick" : "小三" }

# 2$unset :刪掉某個欄位
> db.user.update({name:'張三'}, {$unset:{nick:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密" }


# 3$inc :用來增加已有鍵的值,如果欄位不存在,會建立一個。只能用於整型、長整型、或雙精度浮點型的值。
> db.user.update({name:'張三'}, {$inc:{age:12}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12 }


# 4$push:向已有陣列的末尾加入一個元素,要是沒有就新建一個數組
> db.user.update({name:'張三'}, {$set:{group:[4,2]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 4, 2 ] }
> db.user.update({name:'張三'}, {$push:{group:5}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 4, 2, 5 ] }


# 5$each:通過一次$push來操作多個值,同時push多個值
> db.user.update({name:'張三'}, {$push:{group:{$each:[4,2,6,5]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 4, 2, 5, 4, 2, 6, 5 ] }

# 6$slice:限制陣列只包含最後加入的n個元素,其值必須是負整數
> db.user.update({name:'張三'}, {$push:{group:{$each:[4,2,6,5],$slice:-3}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 2, 6, 5 ] }


# 7$sort:對陣列中的元素,按照指定的欄位來對資料進行排序(1為升序,-1為降序),然後再按照slice刪除。注意:不能只將$slice或者$sort$push配合使用,且必須使用$each
> db.user.update({name:'張三'}, {$push:{group:{$each:[4,2,6,5],$slice:-3, $sort:1}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6 ] }


# 8$ne:判斷一個值是否在陣列中,如果不在則新增進去
> db.user.update({name:'張三', group:{$ne:7}}, {$push:{group:7}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6, 7 ] }
> db.user.update({name:'張三', group:{$ne:7}}, {$push:{group:7}})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6, 7 ] }


# 9$addToSet:將陣列作為資料集使用,以保證陣列內的元素不會重複
> db.user.update({name:'張三'}, {$addToSet:{group:7}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })

# 10$pop :從陣列一端刪除元素,{$pop:{key:1}},從末尾刪掉一個,-1則從頭部刪除
> db.user.update({name:'張三'}, {$pop:{group:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })


# 11$pull:按照條件來刪除所有匹配的元素
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6, 7, 7 ] }
> db.user.update({name:'張三'}, {$pull:{group:7}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 6 ] }

# 12$:用來修改第一個匹配的元素
> db.user.update({name:'張三'}, {$set:{'group.2':8}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 6, 8 ] }
# 像上面這種就是根據陣列的索引來替換值
> db.user.update({name:'張三', 'group.1':6}, {$set:{'group.$':4}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find({name:'張三'})
{ "_id" : ObjectId("5871ecba7f264dfcaeec2489"), "name" : "張三", "sex" : "保密", "age" : 12, "group" : [ 5, 4, 8 ] }

以上的命令一定要自己都跟著練習幾次,如此這些東西才能算是你學到的。學了和學到是兩個完全不同的狀態。我後面還會進一步介紹其它高階內容,以及與實際專案的結合,歡迎關注。