MongoDB 是一種NoSQL 資料庫,本文主要介紹MongoDB裡面的一些基本概念和操作、以及視覺化工具的安裝和使用。
作者:奔放的辣條妹
閱讀時間大約 20min+
1 MongoDB 簡介
MongoDB 是一種NoSQL 資料庫,儲存的資料物件由鍵值對組成。MongoDB 所有儲存在集合中的資料都是 BSON 格式。BSON 是一種類似 JSON 的二進位制形式的儲存格式,是 Binary JSON 的簡稱。如下所示:
{
"_id" : ObjectId("5c89f787ca6e4e3ac1ecabkk"),
"_plat" : "test_plat0",
"update_time" : ISODate("2019-06-03T15:00:42.142Z"),
"create_time" : ISODate("2019-03-14T14:41:11.217Z"),
"creator" : "test_user",
"admin" : [
"admin1",
"admin2"
],
"ops" : [
"ops1"
],
"labels" : {
"department" : "departmentA",
"main_class" : "mainClassA"
}
}
複製程式碼
下面對照關係型資料庫介紹一些 MongoDB 裡面的基本概念:
關聯式資料庫術語 | MongoDB術語 | 說明 |
---|---|---|
database | database | 資料庫 |
table | collection | 資料庫表/集合 |
row | document | 記錄行/文件 |
column | field | 資料欄位/域 |
index | index | 索引 |
primary key | primary key | 主鍵,Mongodb自動將_id欄位設定為主鍵 |
通過以下對比可以更理解 MongoDB :
id | 姓名 | 年齡 | 性別 |
---|---|---|---|
1 | 張三 | 23 | 男 |
2 | 李四 | 21 | 男 |
上述關係型資料在 MongoDB 中的資料形式為:
{
"_id" : ObjectId("5c89f787ca6e4e3ac1ehhb23"),
"姓名" : "張三",
"年齡" : 23,
"性別" : "男"
}
{
"_id" : ObjectId("5c89f787ca6e4e3ac1ehhb24"),
"姓名" : "李四",
"年齡" : 21,
"性別" : "男"
}
複製程式碼
2 MongoDB 基本操作
本節主要介紹通過命令列操作 MongoDB,以 MongoDB 安裝在 CentOS 上為例進行說明。
2.1 db、集合等基本操作
DB 的檢視、建立、刪除,集合的檢視、建立、刪除等操作方式如下:
[root] mongo #命令列輸入mongo進入MongoDB命令互動模式
> show dbs #列出已有db
> use my_db #如果my_db存在,則切換到my_db,如果不存在,則建立之
> db #顯示當前db
> show dbs #發現列表裡面沒有my_db,因為此時db裡面沒有實際資料或者集合哦
> db.createCollection("my_col") #建立集合my_col
> db.my_col_new.insert({"name":"測試一下"}) #往集合my_col_new裡面插入一條資料,如果集合不存在,會自動建立
> show collections #列出改db下面所有的集合
> show tables #功能跟show collections是一樣的哦
> db.my_col.drop() #刪除集合my_col
> db.dropDatabase() #刪除當前資料庫,執行之前用db命令確認一下當前資料庫是不是你要刪除的這個哦
複製程式碼
2.2 資料插入
插入資料有4種方法:insert、insertOne、insertMany、save,下面通過例子詳細介紹。
> db.my_col.insert({"name":"xiaoming"}) #insert可以插入一條資料
> db.my_col.insert([{"name":"xiaoming"},{"name":"test_user"}]) #insert也可以插入多條資料
> db.my_col.insertOne({"name":"xiaoming"}) #insertOne只能插入一條資料
> db.my_col.insertMany([{"name":"xiaoming"}]) #insertMany可以插入一條或多條資料,但是必須以列表(list)的形式組織資料
> db.my_col.save([{"name":"xiaoming"},{"name":"test_user"}]) #如果不指定_id,save的功能與insert一樣
> db.my_col.save({"_id":ObjectId("5d07461141623d5db6cd4d43"),"name":"xiaoming"}) #如果指定_id,mongodb就不為該條記錄自動生成_id了,只有save可以指定_id,insert、insertOne、insertMany都不可以
複製程式碼
2.3 資料修改
修改資料有2種方法:update、save,下面詳細介紹。
2.3.1 update
首先,看一下 update 的語法格式,請格外注意一些可選引數的值,這將直接影響你的修改結果:
db.collection.update(
<query>, #update的查詢條件,類似sql update語句where後面的部分
<update>, #update的物件和一些更新的操作符等,也可以理解為sql update語句set後面的
{
upsert: <boolean>, #可選,這個引數的意思是,如果不存在update的記錄,是否插入objNew,true為插入,預設是false,不插入
multi: <boolean>, #可選,mongodb 預設是false,只更新找到的第一條記錄,如果這個引數為true,就把按條件查出來多條記錄全部更新
writeConcern: <document> #可選,丟擲異常的級別
}
)
複製程式碼
假設有這樣一張學生成績表:
> db.my_col.insert([{"name":"xiaoming","class":"c++", "score":60},{"name":"xiaoming","class":"python", "score":95}])
> db.my_col.find().pretty()
{
"_id" : ObjectId("5d0751ef41623d5db6cd4d44"),
"name" : "xiaoming",
"class" : "c++",
"score" : 60
}
{
"_id" : ObjectId("5d0751ef41623d5db6cd4d46"),
"name" : "xiaoming",
"class" : "python",
"score" : 95
}
複製程式碼
xiaoming 同學發現老師把她的 c++ 課程分數錄錯了,需要修改為75分:
> db.my_col.update({"_id":ObjectId("5d0751ef41623d5db6cd4d44")},{$set:{"score":75}})
複製程式碼
老師發現把 xiaoming 同學的名字錄錯了,需要全部修改過來:
> db.my_col.update({"name":"xiaoming"},{$set:{"name":"xming"}}) #這樣是不對的,只會修改一條記錄
> db.my_col.update({"name":"xiaoming"},{$set:{"name":"xming"}},{multi:true}) #這樣才對
複製程式碼
將 xming 的 java 課程分數改為95分,如果找不到,就插入一條記錄
> db.my_col.update({"name":"xming", "class": "java"},{$set:{"score": 95}},true)
複製程式碼
2.3.2 save
save 方法通過傳入的文件來替換已有文件。語法格式如下:
db.collection.save(
<document>, #文件資料
{
writeConcern: <document> #可選,丟擲的異常級別
}
)
複製程式碼
還以上面那張學生成績表為例:
> db.my_col.save({
"_id" : ObjectId("5d0751ef41623d5db6cd4d44"), #指定_id,新的文件會將舊的文件覆蓋
"name" : "xming",
"class" : "c++",
"score" : 80
})
複製程式碼
2.4 資料刪除
資料刪除可以使用 deleteOne、deleteMany、remove,下面詳細介紹。
2.4.1 deleteOne 和 deleteMany
使用方法如下:
> db.my_col.deleteOne({"name":"xming"}) #刪除xming的一條成績記錄
> db.my_col.deleteMany({"name":"xming"}) #刪除xming的所有成績記錄
> db.my_col.deleteMany({}) #刪除成績表裡面的所有內容
複製程式碼
2.4.2 remove
首先還是來看語法格式:
db.collection.remove(
<query>, #可選,查詢條件
{
justOne: <boolean>, #可選,設定為true或者1,表示只刪除一個文件,設定為false,表示刪除所有匹配的文件,預設為false
writeConcern: <document> #可選,丟擲異常的級別
}
)
複製程式碼
刪除 xming 的所有成績記錄:
> db.col.remove({"name":"xming"})
> db.repairDatabase() #remove方法並不會真正釋放空間,需要繼續執行 db.repairDatabase() 來回收磁碟空間
> db.runCommand({ repairDatabase: 1 }) #與上一句等效,仍以執行一句即可
複製程式碼
ps:remove 現在已經過時了現在官方推薦使用 deleteOne 和 deleteMany 方法。
2.5 資料查詢
資料查詢的方法有 findOne 和 find,二者引數等用法一樣,但是 findOne 只返回一條匹配的資料,find 返回全部的匹配資料,下面主要介紹 find 的用法。
2.5.1 條件操作符
操作 | sql查詢寫法 | mongo查詢寫法 |
---|---|---|
等於 | select * from my_col where score = 75; | db.my_col.find({"score": 75}).pretty() |
小於 | select * from my_col where score < 75; | db.my_col.find({"score": {$lt: 75}}).pretty() |
小於等於 | select * from my_col where score <= 75; | db.my_col.find({"score": {$lte: 75}}).pretty() |
大於 | select * from my_col where score > 75; | db.my_col.find({"score": {$gt: 75}}).pretty() |
大於等於 | select * from my_col where score >= 75; | db.my_col.find({"score": {$gte: 75}}).pretty() |
不等於 | select * from my_col where score != 75; | db.my_col.find({"score": {$ne: 75}}).pretty() |
ps:pretty 能讓查詢結果以格式化的 json 形式列印出來,便於檢視
2.5.2 排序、limit 與 skip
以分數從高到低顯示學生的 c++ 課程成績,只顯示第10名到第20名的學生:
> db.my_col.find({"class": "c++"}).sort({"score": -1}).skip(9).limit(11).pretty()
#sort:1為升序,-1為降序,預設升序
#limit:顯示多少條資料
#skip:跳過多少條資料
複製程式碼
2.5.3 複合條件查詢 and、or
and:find 方法可以傳入多個鍵值對,每個鍵值對以逗號隔開,即常規 SQL 的 AND 條件
查詢 xiaoming 同學的 c++ 課程成績:
> db.my_col.find({"name": "xiaoming", "class": "c++"}).pretty()
複製程式碼
查詢分數在75到85分之間的成績記錄:
> db.my_col.find({"score": {$gt: 75, $lt: 85}}).pretty()
複製程式碼
or:MongoDB OR 條件語句使用了關鍵字 $or,語法格式如下:
> db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
複製程式碼
查詢 xiaoming 或 zhangsan 的課程成績:
> db.my_col.find({$or: [{"name": "xiaoming"}, {"name": "zhangsan"}]}).pretty()
複製程式碼
and + or 複合查詢:
查詢 xiaoming 的 c++ 或者 python 課程的成績:
> db.my_col.find({"name": "xiaoming", $or: [{"class": "c++"}, {"class": "python"}]}).pretty()
複製程式碼
2.5.4 包含in、不包含nin、全部all
查詢 xiaoming、zhangsan 和 lisa 的成績:
> db.my_col.find({"name": {$in: ["xiaoming","zhangsan","lisa"]}}).pretty()
複製程式碼
查詢除了 xiaoming、zhangsan 和 lisa 之外,其他人的成績:
> db.my_col.find({"name": {$nin: ["xiaoming","zhangsan","lisa"]}}).pretty()
複製程式碼
in和nin比較好理解,跟sql的用法類似,all類似於in,不同的地方是,in只需要滿足列表中的一個值即可,而all需要滿足列表中的全部值。比如,有下面這樣一張課程表,表示每個學生修的課程:
> db.course.find().pretty()
{
"_id" : ObjectId("5d084f1541623d5db6cd4d4c"),
"name" : "xiaoming",
"course" : [
"c++",
"python",
"java"
]
}
{
"_id" : ObjectId("5d084f1c41623d5db6cd4d4d"),
"name" : "lisa",
"course" : [
"c++",
"python",
"java"
]
}
{
"_id" : ObjectId("5d084f4a41623d5db6cd4d4e"),
"name" : "tom",
"course" : [
"c++",
"python"
]
}
複製程式碼
需要找出修了 c++ 和 java課程的學生:
> db.course.find({"course": {$all: ["c++", "java"]}}).pretty() # 用 all 操作符,表示需要滿足 c++ 和 java 兩項
複製程式碼
2.5.5 判斷欄位是否存在 exists
比如,有下面一張表,表示學生資訊:
> db.stu_info.find().pretty()
{
"_id" : ObjectId("5d08519a41623d5db6cd4d4f"),
"name" : "xiaoming",
"tel" : "138xxxxxxxx"
}
{
"_id" : ObjectId("5d08531641623d5db6cd4d50"),
"name" : "lisa"
}
{
"_id" : ObjectId("5d08542e41623d5db6cd4d51"),
"name" : "tom",
"tel" : null
}
複製程式碼
需要找出沒有 tel 欄位的學生:
> db.stu_info.find({"tel": {$exists: false}}).pretty() #欄位不存在就用false,存在就用true
複製程式碼
2.5.6 空值處理 null
以上面的學生資訊表為例,找出 tel 為空值的學生:
> db.stu_info.find({"tel": null}).pretty()
{
"_id" : ObjectId("5d08531641623d5db6cd4d50"),
"name" : "lisa"
}
{
"_id" : ObjectId("5d08542e41623d5db6cd4d51"),
"name" : "tom",
"tel" : null
}
複製程式碼
這時候把 tel 欄位不存在和 tel 值為 null 的情況都查出來了!如果只想找 tel 值為 null 的情況:
> db.stu_info.find({"tel": {$in:[null], $exists:true}}).pretty()
複製程式碼
2.5.7 取模運算 mod
比如,查詢學生成績取模10 等於0 的資料(即100、90、80...等等):
> db.my_col.find({"score": {$mod: [10, 0]}}).pretty()
複製程式碼
2.5.8 正則匹配 regex
查詢學生名字以a開頭的學生成績:
> db.my_col.find({"name": {$regex: /^a.*/}})
複製程式碼
2.5.9 獲取查詢結果條數 count
獲取學生成績記錄的條數:
> db.my_col.find().count()
複製程式碼
當使用 limit 方法限制返回的記錄數時,預設情況下 count 方法仍然返回全部記錄條數。如果希望返回限制之後的記錄數量,要使用 count(true) 或者 count(非0):
> db.my_col.find().count()
4
> db.my_col.find().limit(1).count()
4
> db.my_col.find().limit(1).count(true)
1
複製程式碼
2.5.10 distinct
查詢課程成績表中所有學生的名單:
> db.my_col.distinct("name")
複製程式碼
2.6 聚合aggregate
在MongoDB中,使用聚合框架可以對集合中的文件進行變換和組合,完成一些複雜的查詢操作。聚合框架通過多個構件來建立一個管道(pipeline),用於對一連串的文件進行處理。這些構件包括但不限於:
操作符 | 意義 |
---|---|
$match | 篩選 |
$project | 投射,選擇想要的欄位或對欄位進行重新命名 |
$group | 分組 |
$unwind | 拆分 |
$sort | 排序 |
$limit | 限制查詢條數 |
$skip | 跳過一些條數 |
當需要使用多個操作符來完成文件的聚合時,我們可以傳入一個陣列條件,這也是aggregate的常見用法:
> db.my_col.aggregate([
{$match: {"name": "xiaoming"}}, #查詢 xiaoming 同學的課程成績
{$project: {"_id": 0}}, #不需要_id欄位
{$sort: {"score": -1, "class": 1}}, #按分數降序排序;同樣分數的,按課程名字升序排序
{$skip: 1}, #跳過一條資料
{$limit: 1} #只顯示一條資料
])
複製程式碼
$match用於對文件集合進行篩選,之後就可以在篩選得到的文件子集上做聚合。"$match"可以使用所有常規的查詢操作符("$gt"、"$lt"、"$in"等)。通常,在實際使用中應該儘可能將"$match"放在管道的前面位置。這樣做有兩個好處:一是可以快速將不需要的文件過濾掉,以減少管道的工作量;二是如果在投射和分組之前執行"$match",查詢可以使用索引。
$project可以從子文件中提取欄位,可以重新命名欄位。例如,查詢學生課程成績,不顯示 _id 欄位,顯示姓名、課程、成績欄位,同時將 name 欄位重新命名為 student_name:
> db.my_col.aggregate([
{$project: {"_id": 0, "student_name": "$name", "core": 1, "class": 1}}
])
複製程式碼
$sort、$skip、$limit 的用法比較好理解,就不多做說明。
$group 類似於 sql 中的 group by,主要用於資料處理,比如,計算每個學生的總課程成績:
> db.my_col.aggregate([
{$group: {_id: "$name", total: {$sum: "$score"}}}
])
複製程式碼
$sum 可以 替換成操作符 $avg、$min、$max,分別表示求平均成績、最低成績、最高成績。
$unwind 可以將陣列中的每一個值拆分為單獨的文件,比如有下面一條記錄,記錄了一篇部落格以及下面的評論:
> db.blog.findOne().pretty()
{
"_id":ObjectId("5359f6f6ec7452081a7873d7"),
"title":"這是一篇部落格",
"auth":"xiaoming",
"comments":[
{
"author":"lisa",
"date":ISODate("2019-01-01T17:52:04.148Z"),
"text":"Nice post"
},
{
"author":"tom",
"date":ISODate("2019-01-01T17:52:04.148Z"),
"text":"I agree"
}
]
}
複製程式碼
現在要找到 lisa 的評論,可以先使用 $unwind 將每條評論拆分為一個獨立的文件,然後再進行 match 查詢:
> db.blog.aggregate({"$unwind":"$comments"})
{
"results":
{
"_id":ObjectId("5359f6f6ec7452081a7873d7"),
"title":"這是一篇部落格",
"author":"xiaoming",
"comments":{
"author":"lisa",
"date":ISODate("2019-01-01T17:52:04.148Z"),
"text":"Nice post"
}
},
{
"_id":ObjectId("5359f6f6ec7452081a7873d7"),
"title":"這是一篇部落格",
"author":"xiaoming",
"comments":{
"author":"tom",
"date":ISODate("2019-01-01T17:52:04.148Z"),
"text":"I agree"
}
}
}
> db.blog.aggregate([
{"$unwind":"$comments"},
{"$match":{"comments.author":"lisa"}}
])
複製程式碼
2.7 索引
索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取資料時必須掃描集合中的每個檔案並選取那些符合查詢條件的記錄。這種掃描全集合的查詢效率是非常低的,特別在處理大量的資料時,查詢可以要花費幾十秒甚至幾分鐘,這對網站的效能是非常致命的。索引是特殊的資料結構,索引儲存在一個易於遍歷讀取的資料集合中,索引是對資料庫表中一列或多列的值進行排序的一種結構。
建立索引的基本語法如下:
db.collection.createIndex(
{key1: option1, key2: option2}, #key為要建立索引的欄位,option為建立索引的方式:1 為升序,-1 為降序,可以對多個欄位建立索引,稱為複合索引
{
background: <boolean>, #可選,建索引過程會阻塞其它資料庫操作,background 設定為 true 可指定以後臺方式建立索引,預設值為 false
unique: <boolean> #可選,建立的索引是否唯一。指定為true建立唯一索引。預設值為false
name: <string> #可選,索引的名稱。如果未指定,MongoDB的通過連線索引的欄位名和排序順序生成一個索引名稱
sparse: <boolean> #可選,對文件中不存在的欄位資料不啟用索引;這個引數需要特別注意,如果設定為true的話,在索引欄位中不會查詢出不包含對應欄位的文件。預設值為 false
}
)
複製程式碼
對學生成績表建立索引:
> db.my_col.createIndex({"score": 1}, {background: true}) #在後臺建立
> db.my_col.getIndexes() #檢視集合索引
> db.my_col.totalIndexSize() #檢視集合索引大小
> db.my_col.dropIndex("索引名稱") #刪除集合指定索引
> db.my_col.dropIndexes() #刪除集合所有索引
複製程式碼
3 視覺化工具
MongoDB有一款跨平臺的視覺化工具Robo 3T,非常簡潔易用。
下載安裝地址:robomongo.org/download
下載安裝成功之後,點選 file → connect,彈出以下小視窗,然後點選create,新建配置:
填入正確的地址和埠,儲存即可。
按照上述教程安裝之後,如果你連不上所配置的MongoDB,排除網路原因,可能是因為DB服務配置的監聽地址是127.0.0.1,需要改成0.0.0.0哦。
雙擊配置好的連線,即可進入互動介面,如下圖所示,左邊是資料庫和集合等資訊,右邊是查詢結果。可以在命令列輸入查詢命令然後點選執行,進行過濾等操作。
還可以直接右鍵對文件進行檢視、編輯等操作,非常方便。
4 參考文件
MongoDB菜鳥教程:www.runoob.com/mongodb/mon…
MongoDB高階查詢:cw.hubwiz.com/card/c/543b…
MongoDB聚合:cw.hubwiz.com/card/c/5481…
MongoDB官方文件:docs.mongodb.com/manual/intr…
Robo 3T 使用教程:www.cnblogs.com/tugenhua070…> MongoDB 是一種NoSQL 資料庫,本文主要介紹MongoDB裡面的一些基本概念和操作、以及視覺化工具的安裝和使用。
關注【IVWEB社群】公眾號獲取每週最新文章,通往人生之巔!