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集群搭建。如果本文由錯誤的地方,希望大家可以指出,我會及時修改,謝謝。