nosql介紹
nosql,全稱是 not only sql, 即“不僅於sql”,相較於關係型數據庫,nosql更加靈活,無需去維護複雜的數據關係。數據是json格式,更加直觀易讀。
mongodb是應用最廣泛的一種nosql數據庫
mongdb
安裝mongodb
以mac爲例:
☁ ~ brew install mongodb
開啓服務
☁ ~ sudo brew services start mongo Password: ==> Successfully started `mongodb` (label: homebrew.mxcl.mongodb)
連續服務
☁ ~ mongo MongoDB shell version v3.4.2 connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 4.0.2 WARNING: shell and server versions do not match Server has startup warnings: 2018-09-08T10:56:57.451+0800 I CONTROL [initandlisten] 2018-09-08T10:56:57.451+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. 2018-09-08T10:56:57.451+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. 2018-09-08T10:56:57.451+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended. 2018-09-08T10:56:57.451+0800 I CONTROL [initandlisten]
配置文件
☁ mysite cat /usr/local/etc/mongod.conf systemLog: destination: file path: /usr/local/var/log/mongodb/mongo.log logAppend: true storage: dbPath: /usr/local/var/mongodb net: bindIp: 127.0.0.1
庫操作
查看所有庫
> show dbs; admin 0.000GB config 0.000GB local 0.000GB
使用庫
> use test; # 如果庫不存在,會自動創建該庫 switched to db test > show dbs; # 新建的庫沒有數據,是不會顯示在庫列表中的 admin 0.000GB config 0.000GB local 0.000GB
刪除庫
> use test switched to db test > db.dropDatabase() { "ok" : 1 }
集合操作
創建集合
> db.createCollection('col1') { "ok" : 1 }
顯示集合
> show collections; col1
刪除集合
> db.col1.drop() true
文檔操作
插入文檔
> db.col.insert({ # 創建文檔時,如果集合不存在,會自動創建集合 ... 'name': '郭靖', ... 'gender': true, ... 'age': 20 ... }); WriteResult({ "nInserted" : 1 })
查詢文檔
> db.col.find() # 查出所有文檔 { "_id" : ObjectId("5b933ce95032d051d0771953") } { "_id" : ObjectId("5b933ced5032d051d0771954") } { "_id" : ObjectId("5b933d1f565bbd5e857dbd89"), "name" : "郭靖", "gender" : true, "age" : 20 } > db.col.find({name: '郭靖'}) # 根據條件篩選文檔 { "_id" : ObjectId("5b933d1f565bbd5e857dbd89"), "name" : "郭靖", "gender" : true, "age" : 20 }
更新文檔
> db.col.update({'name': '郭靖'}, {$set: {'age': 25}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.col.find({name: '郭靖'}) { "_id" : ObjectId("5b933d1f565bbd5e857dbd89"), "name" : "郭靖", "gender" : true, "age" : 25 }
刪除文檔
> db.col.remove({'name': '郭靖'}) WriteResult({ "nRemoved" : 1 })
數據類型
名稱 | 釋義 |
---|---|
Object ID | 文檔ID |
String | 字符串,最常用,必須是有效的UTF-8 |
Boolean | 存儲一個布爾值,true或false |
Integer | 整數可以是32位或64位,這取決於服務器 |
Double | 存儲浮點值 |
Arrays | 數組或列表,多個值存儲到一個鍵 |
Object | 用於嵌入式的文檔,即一個值爲一個文檔 |
Null | 存儲Null值 |
Timestamp | 時間戳 |
Date | 存儲當前日期或時間的UNIX時間格式 |
object id 每個文檔都有一個屬性,爲_id,保證每個文檔的唯一性, objectID是一個12字節的十六進制數 前4個字節爲當前時間戳 接下來3個字節的機器ID 接下來的2個字節中MongoDB的服務進程id 最後3個字節是簡單的增量值
查詢進階
數據初始化
let data = [ {'name':'郭靖', 'age':20, 'skill':'降龍十八掌', 'gender':true}, {'name':'黃蓉', 'age':18, 'skill':'桃花島武功', 'gender':false}, {'name':'黃藥師', 'age':58, 'skill':'碧海潮生曲', 'gender':true}, {'name':'一燈大師', 'age':60, 'skill':'一陽指', 'gender':true}, {'name':'小龍女', 'age':25, 'skill':'玉女心經', 'gender':false}, {'name':'李莫愁', 'age':30, 'skill':'赤練神掌', 'gender':false}, {'name':'喬峯', 'age':35, 'skill':'降龍十八掌', 'gender':true}, {'name':'王語嫣', 'age':22, 'skill':'懂得各派武功', 'gender':false}, ] for (i in data) { data[i] db.person.insert(data[i]) }
- findOne
> db.person.findOne(); // 只查詢一條 { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖", "age" : 20, "skill" : "降龍十八掌", "gender" : true } > db.person.findOne({'name': '小龍女'}); { "_id" : ObjectId("5b9363badfee996b08be20b3"), "name" : "小龍女", "age" : 25, "skill" : "玉女心經", "gender" : false }
- 比較運算
> db.person.find({age: {$gt: 50}}) // 大於 { "_id" : ObjectId("5b9363badfee996b08be20b1"), "name" : "黃藥師", "age" : 58, "skill" : "碧海潮生曲", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師", "age" : 60, "skill" : "一陽指", "gender" : true } > db.person.find({age: {$gte: 60}}) // 大於等於 { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師", "age" : 60, "skill" : "一陽指", "gender" : true } > db.person.find({age: {$lt: 20}}) // 小於 { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉", "age" : 18, "skill" : "桃花島武功", "gender" : false } > db.person.find({age: {$lte: 20}}) // 小於等於 { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖", "age" : 20, "skill" : "降龍十八掌", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉", "age" : 18, "skill" : "桃花島武功", "gender" : false }
- 邏輯運算
> db.person.find({$or:[{name:'郭靖'}, {age: {$gt: 50}}]}) // or查詢 { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖", "age" : 20, "skill" : "降龍十八掌", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b1"), "name" : "黃藥師", "age" : 58, "skill" : "碧海潮生曲", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師", "age" : 60, "skill" : "一陽指", "gender" : true } > db.person.find({age: {$in: [18, 20]}}) // in查詢 { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖", "age" : 20, "skill" : "降龍十八掌", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉", "age" : 18, "skill" : "桃花島武功", "gender" : false }
- 正則查詢
> db.person.find({name: /^小/}) { "_id" : ObjectId("5b9363badfee996b08be20b3"), "name" : "小龍女", "age" : 25, "skill" : "玉女心經", "gender" : false }
- js函數查詢
> db.person.find({$where:function(){return this.age>40}}) { "_id" : ObjectId("5b9363badfee996b08be20b1"), "name" : "黃藥師", "age" : 58, "skill" : "碧海潮生曲", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師", "age" : 60, "skill" : "一陽指", "gender" : true } > db.person.find({$where:function(){return this.skill.indexOf('掌')>=0}}) { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖", "age" : 20, "skill" : "降龍十八掌", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b4"), "name" : "李莫愁", "age" : 30, "skill" : "赤練神掌", "gender" : false } { "_id" : ObjectId("5b9363badfee996b08be20b5"), "name" : "喬峯", "age" : 35, "skill" : "降龍十八掌", "gender" : true }
- skip與limit
> db.person.find().skip(1).limit(1) { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉", "age" : 18, "skill" : "桃花島武功", "gender" : false } > db.person.find().limit(1).skip(1) { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉", "age" : 18, "skill" : "桃花島武功", "gender" : false }
skip與limit可單獨,也可組合使用。雖然在這種查詢條件下,兩者的順序不會影響結果。但推薦使用skip().limit()的順序。因爲在聚合查詢時兩者的順序不同會導致結果不同。
- 顯示字段
> db.person.find({}, {name: 1}); // 查詢條件爲空時,也要有{}空json,_id默認是顯示的 { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖" } { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉" } { "_id" : ObjectId("5b9363badfee996b08be20b1"), "name" : "黃藥師" } { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師" } { "_id" : ObjectId("5b9363badfee996b08be20b3"), "name" : "小龍女" } { "_id" : ObjectId("5b9363badfee996b08be20b4"), "name" : "李莫愁" } { "_id" : ObjectId("5b9363badfee996b08be20b5"), "name" : "喬峯" } { "_id" : ObjectId("5b9363badfee996b08be20b6"), "name" : "王語嫣" } > db.person.find({}, {name: 1, _id: 0}); // 不顯示_id { "name" : "郭靖" } { "name" : "黃蓉" } { "name" : "黃藥師" } { "name" : "一燈大師" } { "name" : "小龍女" } { "name" : "李莫愁" } { "name" : "喬峯" } { "name" : "王語嫣" }
- sort排序
> db.person.find({}, {_id:0, name:1, age:1}).sort({age: 1}); // 正序 { "name" : "黃蓉", "age" : 18 } { "name" : "郭靖", "age" : 20 } { "name" : "王語嫣", "age" : 22 } { "name" : "小龍女", "age" : 25 } { "name" : "李莫愁", "age" : 30 } { "name" : "喬峯", "age" : 35 } { "name" : "黃藥師", "age" : 58 } { "name" : "一燈大師", "age" : 60 } > db.person.find({}, {_id:0, name:1, age:1}).sort({age: -1}); // 倒序 { "name" : "一燈大師", "age" : 60 } { "name" : "黃藥師", "age" : 58 } { "name" : "喬峯", "age" : 35 } { "name" : "李莫愁", "age" : 30 } { "name" : "小龍女", "age" : 25 } { "name" : "王語嫣", "age" : 22 } { "name" : "郭靖", "age" : 20 } { "name" : "黃蓉", "age" : 18 }
- count統計
> db.person.count() 8 > db.person.count({age: {$gt: 20}}) 6
- distinct過濾重複
> db.person.find({age: {$gt: 20}}, {_id:0, gender:1}) { "gender" : true } { "gender" : true } { "gender" : false } { "gender" : false } { "gender" : true } { "gender" : false } > db.person.distinct('gender', {age: {$gt: 20}}) [ true, false ]
聚合
$group分組
// 按gender字段進行分組 db.person.aggregate([ {$group:{_id:'$gender'}} ]); { "_id" : false } { "_id" : true } // 分組後再進行求和統計:求各個組的記錄條數 db.person.aggregate([ {$group:{_id:'$gender', counter:{$sum: 1}}} ]); { "_id" : false, "counter" : 4 } { "_id" : true, "counter" : 4 } // 指定某個字段求和 db.person.aggregate([ {$group:{_id:'$gender', counter:{$sum: '$age'}}} ]); { "_id" : false, "counter" : 95 } { "_id" : true, "counter" : 173 } // 將分組中指定字段的值歸爲一個數組 db.person.aggregate([ {$group:{_id:'$gender', counter:{$push: '$name'}}} ]); { "_id" : false, "counter" : [ "黃蓉", "小龍女", "李莫愁", "王語嫣" ] } { "_id" : true, "counter" : [ "郭靖", "黃藥師", "一燈大師", "喬峯" ] } // 將分組中包括的文檔歸爲一個數組 db.person.aggregate([ {$group:{_id:'$gender', counter:{$push: '$$ROOT'}}} ]); { "_id" : false, "counter" : [ { "_id" : ObjectId("5b9363badfee996b08be20b0"), "name" : "黃蓉", "age" : 18, "skill" : "桃花島武功", "gender" : false }, { "_id" : ObjectId("5b9363badfee996b08be20b3"), "name" : "小龍女", "age" : 25, "skill" : "玉女心經", "gender" : false }, { "_id" : ObjectId("5b9363badfee996b08be20b4"), "name" : "李莫愁", "age" : 30, "skill" : "赤練神掌", "gender" : false }, { "_id" : ObjectId("5b9363badfee996b08be20b6"), "name" : "王語嫣", "age" : 22, "skill" : "懂得各派武功", "gender" : false } ] } { "_id" : true, "counter" : [ { "_id" : ObjectId("5b9363badfee996b08be20af"), "name" : "郭靖", "age" : 20, "skill" : "降龍十八掌", "gender" : true }, { "_id" : ObjectId("5b9363badfee996b08be20b1"), "name" : "黃藥師", "age" : 58, "skill" : "碧海潮生曲", "gender" : true }, { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師", "age" : 60, "skill" : "一陽指", "gender" : true }, { "_id" : ObjectId("5b9363badfee996b08be20b5"), "name" : "喬峯", "age" : 35, "skill" : "降龍十八掌", "gender" : true } ] }
$match匹配
// 匹配age大於20的文檔 db.person.aggregate([ {$match: {age:{$gt:20}}}, ]); { "_id" : ObjectId("5b9363badfee996b08be20b1"), "name" : "黃藥師", "age" : 58, "skill" : "碧海潮生曲", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b2"), "name" : "一燈大師", "age" : 60, "skill" : "一陽指", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b3"), "name" : "小龍女", "age" : 25, "skill" : "玉女心經", "gender" : false } { "_id" : ObjectId("5b9363badfee996b08be20b4"), "name" : "李莫愁", "age" : 30, "skill" : "赤練神掌", "gender" : false } { "_id" : ObjectId("5b9363badfee996b08be20b5"), "name" : "喬峯", "age" : 35, "skill" : "降龍十八掌", "gender" : true } { "_id" : ObjectId("5b9363badfee996b08be20b6"), "name" : "王語嫣", "age" : 22, "skill" : "懂得各派武功", "gender" : false } // 在匹配的基礎上再進行分組統計 db.person.aggregate([ {$match: {age:{$gt:20}}}, {$group: {_id:'$gender', counter:{$sum:1}}} ]); { "_id" : false, "counter" : 3 } { "_id" : true, "counter" : 3 }
$project顯示字段
db.person.aggregate([ {$match: {age:{$gt:20}}}, {$group: {_id:'$gender', counter:{$sum:1}}}, {$project: {_id:0, counter:1}} ]); { "counter" : 3 } { "counter" : 3 }
$sort排序
db.person.aggregate([ {$match: {age: {$gt: 20}}}, {$group: {_id: '$gender', counter: {$sum:1}}}, {$project: {counter: 1}}, {$sort: {_id: -1}} ]); { "_id" : true, "counter" : 3 } { "_id" : false, "counter" : 3 }
limit
// skip 和 limit 在聚合時有順序區分的。開發時養成先skip,再limit的習慣 db.person.aggregate([ {$match: {age: {$gt: 20}}}, {$group: {_id: '$gender', counter: {$sum:1}}}, {$project: {counter: 1}}, {$sort: {_id: -1}}, {$skip: 1}, {$limit: 1} ]); { "_id" : false, "counter" : 3 } db.person.aggregate([ {$match: {age: {$gt: 20}}}, {$group: {_id: '$gender', counter: {$sum:1}}}, {$project: {counter: 1}}, {$sort: {_id: -1}}, {$limit: 1}, {$skip: 1} ]); // 結果爲空
$unwind
將文檔中的數組解開
db.shirt.insert({_id:1, title:'t-shirt', size:['M', 'L', 'S']}); db.shirt.aggregate([ {$unwind: '$size'} ]); { "_id" : 1, "title" : "t-shirt", "size" : "M" } { "_id" : 1, "title" : "t-shirt", "size" : "L" } { "_id" : 1, "title" : "t-shirt", "size" : "S" } // unwind作用的字段爲不同值時的情況 db.shirt.insert({_id: 2, title: 't2', size:[]}); db.shirt.insert({_id: 3, title: 't3'}); db.shirt.insert({_id: 4, title: 't4', size:null}); db.shirt.insert({_id: 5, title: 't5', size:'M'}); // 空值,沒有size的數據丟了 db.shirt.aggregate([ {$unwind: '$size'} ]); { "_id" : 1, "title" : "t-shirt", "size" : "M" } { "_id" : 1, "title" : "t-shirt", "size" : "L" } { "_id" : 1, "title" : "t-shirt", "size" : "S" } { "_id" : 5, "title" : "t5", "size" : "M" } // 阻止空值丟失 db.shirt.aggregate([ {$unwind: {path: '$size', preserveNullAndEmptyArrays:true}} ]); { "_id" : 1, "title" : "t-shirt", "size" : "M" } { "_id" : 1, "title" : "t-shirt", "size" : "L" } { "_id" : 1, "title" : "t-shirt", "size" : "S" } { "_id" : 2, "title" : "t2" } { "_id" : 3, "title" : "t3" } { "_id" : 4, "title" : "t4", "size" : null } { "_id" : 5, "title" : "t5", "size" : "M" }
索引
創建一個一百萬文檔的集合
for(i=0; i<1000000; i++){ db.test_index.insert({name:'test'+i, rank:i}); }; WriteResult({ "nInserted" : 1 })
在沒有索引的情況下查找數據
db.test_index.find({name: 'test10000'}); { "_id" : ObjectId("5b937532dfee996b08be47c7"), "name" : "test10000", "rank" : 10000 } db.test_index.find({name: 'test10000'}).explain('executionStats'); // executionTimeMillis 值爲 416 表示執行 416 毫秒
創建索引
db.test_index.ensureIndex({name: 1}); { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
再次分析性能
db.test_index.find({name: 'test10000'}).explain('executionStats'); // executionTimeMillis 降爲 8 毫秒
用戶權限管理
- 創建超級管理員
- 修改配置文件,啓用身份驗證
- 重啓服務
- 使用超級管理員登錄
- 創建普通用戶
- 使用普通用戶登錄
創建用戶
db.createUser({ user:'admin', pwd:'admin123', roles:[{role:'root', db:'admin'}], passwordDigestor: 'server' })
修改配置
security: authorization: enabled
重啓服務
sudo brew services restart mongo
直接用mongo
登錄,無法使用show dbs
等命令
☁ ~ mongo MongoDB shell version v3.4.2 connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 4.0.2 WARNING: shell and server versions do not match > db test > show dbs; 2018-09-08T09:51:53.531+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "command listDatabases requires authentication", "code" : 13, "codeName" : "Unauthorized" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1 shellHelper.show@src/mongo/shell/utils.js:755:19 shellHelper@src/mongo/shell/utils.js:645:15 @(shellhelp2):1:1
使用超級管理員登錄
☁ ~ mongo -u admin -p --authenticationDatabase admin MongoDB shell version v3.4.2 Enter password: connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 4.0.2 WARNING: shell and server versions do not match Server has startup warnings: 2018-09-08T09:51:35.534+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended. 2018-09-08T09:51:35.534+0800 I CONTROL [initandlisten] > show dbs; admin 0.000GB config 0.000GB local 0.000GB py3 0.004GB
創建普通用戶
db.createUser({ user:'py3', pwd:'admin123', roles:[{role:'readWrite', db:'py3'}], passwordDigestor: 'server' })
使用普通管理員登錄
☁ ~ mongo -u py3 -p --authenticationDatabase py3 MongoDB shell version v3.4.2 Enter password: connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 4.0.2 WARNING: shell and server versions do not match > show dbs; py3 0.004GB
普通管理員只能在指定的數據庫,無法登錄其他數據庫
☁ ~ mongo -u py3 -p --authenticationDatabase admin MongoDB shell version v3.4.2 Enter password: connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 4.0.2 WARNING: shell and server versions do not match 2018-09-08T10:14:02.358+0800 E QUERY [thread1] Error: Authentication failed. : DB.prototype._authOrThrow@src/mongo/shell/db.js:1459:20 @(auth):6:1 @(auth):1:2 exception: login failed