1.mongoDB簡介
mongoDB 是由C++語言編寫的,是一種分散式的面向文件儲存的開源nosql資料庫。nosql是Not Only SQL的縮寫,是對不同於傳統的關係型資料庫的資料庫管理系統的統稱。
mongoDB是無模式的文件資料庫,在關係型資料庫中,資料表的每一行都擁有一樣的欄位,欄位的名字和資料型別在建立table的時候就基本確定了,如student表的每一行都有學生編號、學生姓名、年齡等欄位;而在mongoDB中,儲存資料的格式類似於Json(格式為Bson),每一個document的欄位的名字和資料型別可以完全不同,如在一個collection下,第一個document可以儲存學生資訊(學生編號、姓名、年齡、性別等),第二個document可以儲存班級資訊(班級編號,班級名等)。正是因為無模式的特點,讓我們可以無需多餘操作就能完成資料的橫向擴充套件。下表是mongoDB和傳統資料庫術語的對應關係。
SQL術語 | MongoDB | 解釋/說明 |
---|---|---|
database | database | 資料庫 |
table | collection | 資料庫表/集合 |
row | document | 資料記錄行/文件 |
column | field | 資料欄位/域 |
index | index | 索引 |
join | $lookup | 表連線 |
primary key | primary key | 主鍵,MongoDB自動將_id欄位設定為主鍵 |
2. mongoDB安裝
1.安裝mongoDB
mongoDB的安裝步驟十分簡單,下載地址:https://www.mongodb.com/download-center#community。如果我們想在Windows上安裝mongoDB直接下載msi檔案,雙擊安裝即可。如果要將mongoDB安裝在Linux系統上,步驟如下:
####第1步 下載解壓mongdb #下載解壓二進位制包,解壓即安裝 cd /usr/local/src/ curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.9.tgz tar -zxvf mongodb-linux-x86_64-4.0.9.tgz mv mongodb-linux-x86_64-4.0.9 /usr/local/mongodb ####第2步 新增配置檔案 vim /usr/local/mongodb/bin/mongodb.conf 配置檔案內容如下: systemLog: destination: file logAppend: true path: /usr/local/mongodb/logs/mongodb.log storage: dbPath: /usr/local/mongodb/data journal: enabled: true processManagement: fork: true net: port: 27017 bindIp: 0.0.0.0 #配置檔案中指定的dbpath和log要自己新增,不然會報錯,執行命令 mkdir -p /usr/local/mongodb/data; mkdir -p /usr/local/mongodb/logs/;cd /usr/local/mongodb/logs/; touch mongodb.log ####第3步 載入配置檔案執行 /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/bin/mongodb.conf ####第4步 新增環境變數,用於可以在任意目錄下執行mongo命令 vim ~/.bash_profile #修改當前使用者下的環境變數 PATH=$PATH:$HOME/bin:/usr/local/mongodb/bin source ~/.bash_profile
安裝完成後,在任意目錄下使用命令 mongo 192.168.70.133:27017 (mongo命令會預設啟動127.0.0.1:27017)啟動mongodb,mongoDB使用的是javascript shell,我們簡單測試一下,如果出現下邊的介面表示安裝成功了
這裡簡單使用一下mongoDB的shell來新增、刪除資料庫和collection:
2.安裝Robomongo並連線資料庫
mongoDB的GUI有MongoDB Compass、studio 3T等,這裡使用的是Robomongo,下載地址:https://robomongo.org/,下載完成後一直Next安裝即可,連線mongoDB效果如下,Robomongo是傻瓜式的用法,可以通過客戶端新增collection,document,執行shell等,具體使用方法就不再詳細介紹了。
·
我們也可以通過shell指令碼連線資料庫,在Robomongo執行shell命令如下:
//連線遠端資料庫 var mongo=new Mongo("192.168.70.133:27017") //找到資料庫 var db=mongo.getDB("myDB"); //找到collecttion var collection=db.getCollection("userinfo"); //查詢colletion中所有資料 var list=collection.find().toArray(); //json形式列印結果 printjson(list);
3 mongoDB的shell
我們已經知道mongoDB是面向文件的nosql資料庫,因為其無模式的特點,造成它的操作要比關係型資料庫複雜一些,這裡簡單介紹一下mongoDB的CRUD操作。注意:從3.2版本開始,mongoDB新增了一些xxxOne()和xxxMany()方法,我們儘量使用這些新的方法。
1 新增(insert)
新增資料的指令是insert,使用方法如下:
insert方法的引數也可以是陣列,用於批量新增資料,如下:
db.userinfos.insert([ {_id:1, name: "張三", age: 23}, {_id:2, name: "李四", age: 24} ]);
從3.2版本,mongoDB新增了insertOne和insertMany方法,分別用於單條插入和批量插入,用法很簡單,如下:
//insertOne用於單條新增 db.userinfos.insertOne( {_id:1, name: "張三", age: 23} ); //insertMany用於批量新增 db.userinfos.insertMany([ {_id:1, name: "張三", age: 23}, {_id:2, name: "李四", age: 24} ]);
2 查詢(find)
mongoDB查詢使用find函式,語法如下:
mongoDB使用find查詢時,預設會返回主鍵_id,如果不想返回主鍵的話設定_id=0即可。mongoDB的查詢語法是比較簡單的,但是因為其無模式的特點,且field的值可以是物件和陣列,造成mongoDB的運算子要比傳統的關係型資料庫多很多,如運算子$exists可用於查詢field是否存在、$type用於判斷filed的型別等等,這裡彙總了一些常用的查詢相關的運算子,有興趣的小夥伴可以測試一下:
測試資料: db.userinfos.insertMany([ {_id:1, name: "張三", age: 23,level:10, ename: { firstname: "san", lastname: "zhang"}, roles: ["vip","gen" ]}, {_id:2, name: "李四", age: 24,level:20, ename: { firstname: "si", lastname: "li"}, roles:[ "vip" ]}, {_id:3, name: "王五", age: 25,level:30, ename: { firstname: "wu", lastname: "wang"}, roles: ["gen","vip" ]}, {_id:4, name: "趙六", age: 26,level:40, ename: { firstname: "liu", lastname: "zhao"}, roles: ["gen"] }, {_id:5, name: "田七", age: 27, ename: { firstname: "qi", lastname: "tian"}, address:'北京' }, {_id:6, name: "周八", age: 28,roles:["gen"], address:'上海' } ]); |
||||
類別 | 運算子 | 說明 | 例項 | 執行結果 |
比較運算子 |
$gt($gte) | 大於(大於等於) |
db.userinfos.find( ) |
查詢age>25的文件的name 結果: [{ "_id" : 4, "name" : "趙六" }, { "_id" : 5, "name" : "田七" }] |
$lt($lte) | 小於(小於等於) | db.userinfos.find(
{ age:{$lt:25}}, ) |
查詢age<25的文件的name 結果: [ { "_id" : 1, "name" : "張三" }, { "_id" : 2, "name" : "李四" } ] |
|
$eq | 等於 | db.userinfos.find(
{ age:{$eq:25}}, ) |
查詢age=25的文件的name 結果: [ { "_id" : 3, "name" : "王五" } ] |
|
$ne | 不等於 |
db.userinfos.find( |
查詢age!=25的文件的name 結果: [{"_id" : 1,"name" : "張三"}, |
|
$in | 包含 | db.userinfos.find(
{ age:{$in:[24,25]}}, |
查詢age在[24,25]中的文件的name 結果: [ { "_id" : 2, "name" : "李四" }, { "_id" : 3, "name" : "王五" } ] |
|
$nin | 不包含 | db.userinfos.find(
{ age:{$nin:[24,25]}}, |
查詢age不在[24,25]中的文件的name 結果: [{"_id" : 1,"name" : "張三"}, |
|
邏輯運算子 | $and | 與 |
db.userinfos.find( {$and: [ {name:{$eq:'張三'}}, {age:{$eq:23}} ]}, {name:1} )
|
查詢name='張三'且age=23的文件 結果: [ { "_id" : 1, "name" : "張三" } ] |
$not |
非 |
db.userinfos.find(
{age:{$not:{$in:[23,24,25]}}}, |
查詢age不在[23,24,24]中的文件 結果: [ { "_id" : 4, "name" : "趙六" }, { "_id" : 5, "name" : "田七" } ] |
|
$or | 或
|
db.userinfos.find(
{$or: [ {name:{$eq:'張三'}}, {age:{$eq:24}} ]}, {name:1} ) |
查詢name='張三'或者age=24的文件 結果: [ { "_id" : 1, "name" : "張三" }, { "_id" : 2, "name" : "李四" } ] |
|
$nor |
或的取反 |
db.userinfos.find(
{$nor: [ {name:{$eq:'張三'}}, {age:{$eq:24}} ]}, {name:1} ) |
上邊栗子的取反操作 結果: [ {"_id" : 3,"name" : "王五"}, |
|
評估運算子 | $mod | 取餘 |
db.userinfos.find( {age:{$mod:[10,3]}}, |
查詢name%10=3的文件 結果: [ { "_id" : 1, "name" : "張三" } ] |
$regex | 正則 |
db.userinfos.find( {name:{$regex:/^張/i}}, |
查詢name以張開頭的文件 結果: [ { "_id" : 1, "name" : "張三" } ] |
|
db.userinfos.find( {name:{$in:[/^張/,/四$/]}}, |
查詢name以張開頭或者以四結尾的文件 結果: [ { "_id" : 1, "name" : "張三" }, { "_id" : 2, "name" : "李四" } ] |
|||
$where | where過濾 |
db.userinfos.find( ) |
查詢名字為張三的記錄 結果: [ { "_id" : 1, "name" : "張三" } ] 注意:where可以實現所有的過濾,但是效率不高。 這是因為where採用逐行判斷而不使用索引 |
|
$expr | 表示式過濾 |
db.userinfos.find( ) |
查詢age<level的記錄 結果: [ { "_id" : 3, "name" : "王五" }, { "_id" : 4, "name" : "趙六" } ]
|
|
元素運算子 | $exists | field是否存在 |
db.userinfos.find( ) |
查詢存在address欄位的文件 結果: [ { "_id" : 5, "name" : "田七" }, { "_id" : 6, "name" : "周八" } ] |
$type | field型別 |
db.userinfos.find( {name:{$type:'string'}}, {name:1} ) |
查詢name為string的文件 結果: 所有文件都匹配 |
|
陣列運算子 | $all | 包含所有元素才匹配成功 | db.userinfos.find(
{roles:{$all:['vip','gen']}}, {name:1} ) |
查詢roles中包含vip和gen的文件 結果: [ { "_id" : 1, "name" : "張三" }, { "_id" : 3, "name" : "王五" } ] |
$eleMatch | 只要有一個元素符合就匹配成功 | db.userinfos.find(
{roles:{$elemMatch:{ $eq: 'vip', $ne: 'gen' }}}, {name:1} ) |
查詢roles中有元素等於vip,或有元素不等於gen的文件 結果: [{"_id" : 1,"name" : "張三"}, |
|
$size | 元素個數相同的匹配成功 | db.userinfos.find(
{roles:{$size:2}}, {name:1} ) |
查詢roles中有兩個元素的文件 結果: [ { "_id" : 1, "name" : "張三" }, { "_id" : 3, "name" : "王五" } ] |
3 修改(update)
mongoDB修改documen使用的命令是update,語法如下:
mongoDB的update預設只修改一條document,如果想修改所有符合條件的documet的話,可以設定multi:true。upsert表示當沒有符合過濾條件的文件時,就新增一條文件,並將修改的內容作為新增document的值。mongoDB的update功能比較豐富,如可以修改field的名字,刪除field,以及對陣列進行增刪改。從3.2版本開始,mongoDB新增了updateOne()和updateMany()方法,用於修改單條或者多條資料,推薦使用新的方法,語法如下:
//將age<25的記錄的level修改為50,只修改一條。updateOne相當於update設定multi:false db.userinfos.updateOne( {age:{$lt:25}}, {$set:{level:50}} ) //將age<25的記錄的level修改為50,所有符合條件的記錄都修改。updateMany相當於update設定multi:true db.userinfos.updateMany( {age:{$lt:25}}, {$set:{level:50}} )
這裡彙總了mongoDB中關於update的相關運算子,有興趣的小夥伴可以測試一下:
測試資料: db.userinfos.insertMany([ {_id:1, name: "張三", age: 23,level:10, ename: { firstname: "san", lastname: "zhang"}, roles: ["vip","gen" ]}, {_id:2, name: "李四", age: 24,level:20, ename: { firstname: "si", lastname: "li"}, roles:[ "vip" ]}, {_id:3, name: "王五", age: 25,level:30, ename: { firstname: "wu", lastname: "wang"}, roles: ["gen","vip" ]}, {_id:4, name: "趙六", age: 26,level:40, ename: { firstname: "liu", lastname: "zhao"}, roles: ["gen"] }, {_id:5, name: "田七", age: 27, ename: { firstname: "qi", lastname: "tian"}, address:'北京' }, {_id:6, name: "周八", age: 28,roles:["gen"], address:'上海' } ]); |
||||
值操作運算子 | $currentDate |
修改field值為當前時間, 如果field不存在則新增field |
db.userinfos.update( createtime:{$type:'timestamp'}} } |
將張三的createtime欄位值修改為當前時間 格式為時間戳("createtime" : Timestamp(1560663270, 1)) 補充:如果不設定$type,預設的格式為date 格式為date("createtime" : ISODate("2019-06-16T05:38:21.119Z"))
|
$set |
修改值 |
db.userinfos.update( {level:20} } |
將張三的level修改為20。如果要修改的field不存在,不會新增新的field。 | |
$setOnInsert |
只有在新增document時進行賦值, 一定要設定upsert:true |
db.userinfos.update( {level:30} }, |
因為已經有name=張三的document,所以不做任何操作 | |
db.userinfos.update( |
新增一個name=吳九的document,並設定level為30 | |||
$inc | 自增 |
db.userinfos.update( {age:10} } |
張三的age自增10,age修改為23+10=33 | |
$mul | 自乘 |
db.userinfos.update( {age:2} } |
張三的age自乘2,age修改為23*2=46 | |
$min | 取小 |
db.userinfos.update( {age:13} } |
張三的age取小值,因為23>13,所以修改age為13。如果修改的值比23大,那麼不做操作。 | |
$max | 取大 |
db.userinfos.update( {age:33} } |
張三的age取大值,因為23<33,所以修改age為33。如果修改的值比23小,那麼不做操作。 | |
欄位操作運算子 | $rename | 修改filed的名字 | db.userinfos.update(
{name:'張三'}, {age:'年齡'} } |
將張三的age欄位名改成年齡,值不變,年齡=23 |
$unset | 刪除field | db.userinfos.update(
{name:'張三'}, {level:''} } |
將張三的level欄位刪除 |
陣列相關的update運算子:
測試資料: db.students.insertMany( [{ "_id" : 1, "grades" : [ 85, 80, 80 ] }, { "_id" : 2, "grades" : [ 88, 90, 92 ] }] ) |
||||
陣列運算子 | $ | 單個佔位符 |
db.students.updateOne( |
修改_id=1的文件graders中第一個值為80的元素,值改成82 結果: [{ "_id" : 1, "grades" : [ 85, 82, 80 ] }, |
$[] | 所有元素佔位符 |
db.students.updateOne( |
修改_id=1的文件graders中所有元素,值改成100 結果: [{ "_id" : 1, "grades" : [ 100, 100, 100 ] }, |
|
$[<identifier>] | 符合arrayFilter過濾條件的元素佔位符 |
db.students.updateOne( |
修改_id=2的文件graders中大於等於90的元素,值改成100 結果: [{ "_id" : 1, "grades" : [ 85, 82, 80 ] }, |
|
$push | 新增元素 |
db.students.updateOne( |
在_id=1的文件graders中新增元素 結果: [{ "_id" : 1, "grades" : [ 85, 80, 80 ,98 ] }, |
|
$addToSet | 新增不存在的元素,如果元素已經存在則無操作 | db.students.updateOne( { _id: 1 }, { $addToSet: { "grades" : 100 } } ) |
在_id=1的文件graders中新增元素 結果: [{ "_id" : 1, "grades" : [ 85, 80, 80 ,98,100 ] }, 補充:如果新增的元素是85,因為85已經存在,所以不執行操作 |
|
$pop | 彈出(移除)元素 |
db.students.updateOne( |
移除最後一個元素 結果: [{ "_id" : 1, "grades" : [ 85, 80 ] }, 補充:如果{ $pop: { "grades" : -1 } }表示從前邊彈出,移除第一個元素 |
|
$pullAll | 根據值移除陣列中的元素 |
db.students.update( |
移除_id=2的文件的graders中值為88,90的所有元素 結果: [{ "_id" : 1, "grades" : [ 85, 80, 80 ] }, |
|
$pull | 移除符合條件的元素 |
db.students.update( |
移除_id=2的文件的graders中大於90的所有元素 結果: [{ "_id" : 1, "grades" : [ 85, 80, 80 ] }, |
|
陣列批量新增 相關運算子 |
$each | 和$push,$addToSet配合使用,用於批量新增元素 |
db.students.update( {grades:{$each:[99,100]}} } ) |
在_id=1的文件graders中新增元素[99,100] 結果: [{ "_id" : 1, "grades" : [ 85, 80, 80 ,99,100 ] }, |
$slice | 和$push,$each配合使用,用於限制元素個數 |
db.students.update( {grades:{$each:[99,100],$slice:4}} } |
在_id=1的文件graders中新增元素[99,100]
結果: [{ "_id" : 1, "grades" : [ 85, 80, 80 ,99] }, 如果使用$slice:-4,則保留後4個元素 |
|
$sort | 和$push,$each配合使用,在新增元素後進行排序 |
db.students.update( {grades:{$each:[70,100],$sort:1}} } |
在_id=1的文件graders中新增元素[99,100],並排序
結果: [{ "_id" : 1, "grades" : [70,80,80,85,100]}, 如果使用$sort:-1,則倒序排序 |
|
$position | 和$push,$each配合使用,指定插入元素的位置 |
db.students.update( {grades:{$each:[70,100],$position:1}} } |
在_id=1的文件graders中,從索引1開始插入元素[99,100]
結果: [{ "_id" : 1, "grades" : [85,70,100,80,80]}, |
4.刪除(remove/delete)
在3.2以前的版本中,mongoDB使用remove方法來刪除文件,用法如下:
從3.2版本開始,提供了deleteOne()和deleteMany()方法,分別用於刪除單條和多條document,語法如下:
//刪除單條document,功能類似於remove設定justOne:true db.userinfos.deleteOne({age:{$gt:25}}) //刪除所有符合條件的document,功能類似於remove設定justOne:false db.userinfos.deleteMany({age:{$gt:25}})
本篇是mongoDB的第一篇,簡單介紹了mongoDB的安裝方法,通過js shell進行mongoDB的CRUD操作,後續會逐步介紹mongoDB的索引、資料聚合、GridFS和C#驅動,以及副本集和sharing叢集搭建。如果本文由錯誤的地方,希望大家可以指出,我會及時修改,謝謝。