一口(很長的)氣掌握mongodb基本操作nosql介紹安裝mongodb庫操作集合操作文檔操作數據類型查詢進階聚合索引用戶權限管理

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 毫秒

用戶權限管理

  1. 創建超級管理員
  2. 修改配置文件,啓用身份驗證
  3. 重啓服務
  4. 使用超級管理員登錄
  5. 創建普通用戶
  6. 使用普通用戶登錄

創建用戶

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章