快速掌握mongoDB(一)——mongoDB安裝部署和常用shell命令

撈月亮的猴子發表於2019-07-16

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:{$gt:25}},
  { name:1 }

)

查詢age>25的文件的name

結果:

  [{ "_id" : 4, "name" : "趙六" },

   { "_id" : 5, "name" : "田七" }]

 $lt($lte)  小於(小於等於)  db.userinfos.find(

  { age:{$lt:25}},
  { name:1 }

)

查詢age<25的文件的name

結果: 

  [ { "_id" : 1, "name" : "張三" },

  { "_id" : 2, "name" : "李四" } ]

 $eq  等於  db.userinfos.find(

  { age:{$eq:25}},
  { name:1 }

)

查詢age=25的文件的name

 結果:

  [ { "_id" : 3, "name" : "王五" } ]

 $ne  不等於

db.userinfos.find(
  { age:{$ne:25}},
  { name:1 }
)

 查詢age!=25的文件的name

結果:

  [{"_id" : 1,"name" : "張三"},
  {"_id" : 2,"name" : "李四"},
  {"_id" : 4,"name" : "趙六"},
  {"_id" : 5,"name" : "田七"}]

 $in  包含  db.userinfos.find(

  { age:{$in:[24,25]}},
  { name:1 }
)

查詢age在[24,25]中的文件的name

結果:

   [ { "_id" : 2, "name" : "李四" },

  { "_id" : 3, "name" : "王五" } ]

 $nin  不包含 db.userinfos.find(

  { age:{$nin:[24,25]}},
  { name:1 }
)

查詢age不在[24,25]中的文件的name

結果:

  [{"_id" : 1,"name" : "張三"},
  {"_id" : 4,"name" : "趙六"},
  {"_id" : 5,"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]}}},
  {name:1}
)

查詢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" : "王五"},
   {"_id" : 4,"name" : "趙六"},
   {"_id" : 5,"name" : "田七"} ]

 評估運算子 $mod 取餘

db.userinfos.find(

  {age:{$mod:[10,3]}},
  {name:1}
)

查詢name%10=3的文件

結果:

[ { "_id" : 1, "name" : "張三" } ]

$regex  正則  

db.userinfos.find(

  {name:{$regex:/^張/i}},
  {name:1}
)

查詢name以張開頭的文件

結果:

[ { "_id" : 1, "name" : "張三" } ]

db.userinfos.find(

  {name:{$in:[/^張/,/四$/]}},
  {name:1}
)

查詢name以張開頭或者以四結尾的文件

結果:

[ { "_id" : 1, "name" : "張三" },

{ "_id" : 2, "name" : "李四" } ]

$where where過濾

db.userinfos.find(
  {$where :function(){return this.name=='張三';}},
  {name:1}

)

查詢名字為張三的記錄

結果:

  [ { "_id" : 1, "name" : "張三" } ]

注意:where可以實現所有的過濾,但是效率不高。

這是因為where採用逐行判斷而不使用索引

 $expr  表示式過濾

db.userinfos.find(
  { $expr:{$lt:["$age","$level"]}},
  { name:1 }

)

 查詢age<level的記錄

結果:

[ { "_id" : 3, "name" : "王五" },

{ "_id" : 4, "name" : "趙六" } ]

 

元素運算子   $exists field是否存在

db.userinfos.find(
  { address:{$exists:true}},
  { name:1 }

)

查詢存在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" : "張三"},
  {"_id" : 2,"name" : "李四"},
  {"_id" : 3,"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(
  {name:'張三'},
  {$currentDate:{

    createtime:{$type:'timestamp'}}

  }
)

將張三的createtime欄位值修改為當前時間

  格式為時間戳("createtime" : Timestamp(1560663270, 1))

補充:如果不設定$type,預設的格式為date

  格式為date("createtime" : ISODate("2019-06-16T05:38:21.119Z"))

  

$set

修改值

db.userinfos.update(
  {name:'張三'},
  {$set:

    {level:20}

  }
)

 將張三的level修改為20。如果要修改的field不存在,不會新增新的field。
$setOnInsert 

只有在新增document時進行賦值,

一定要設定upsert:true

 

db.userinfos.update(
  {name:'張三'},
  {$setOnInsert:

    {level:30}

  },
  {upsert:true}
)

 因為已經有name=張三的document,所以不做任何操作

db.userinfos.update(
  {name:'吳九'},
  {$setOnInsert:{level:30}},
  {upsert:true}
)

 新增一個name=吳九的document,並設定level為30
$inc 自增

db.userinfos.update(
  {name:'張三'},
  {$inc:

    {age:10}

  }
)

張三的age自增10,age修改為23+10=33
$mul 自乘

db.userinfos.update(
  {name:'張三'},
  {$mul:

    {age:2}

  }
)

張三的age自乘2,age修改為23*2=46
$min 取小

db.userinfos.update(
  {name:'張三'},
  {$min:

    {age:13}

  }
)

張三的age取小值,因為23>13,所以修改age為13。如果修改的值比23大,那麼不做操作。
$max 取大

db.userinfos.update(
  {name:'張三'},
  {$max:

    {age:33}

  }
)

張三的age取大值,因為23<33,所以修改age為33。如果修改的值比23小,那麼不做操作。
欄位操作運算子   $rename  修改filed的名字  db.userinfos.update(

  {name:'張三'},
  {$rename:

    {age:'年齡'}

  }
)

 將張三的age欄位名改成年齡,值不變,年齡=23
$unset  刪除field  db.userinfos.update(

  {name:'張三'},
  {$unset:

    {level:''}

  }
)

 將張三的level欄位刪除

 

   陣列相關的update運算子:

測試資料:

   db.students.insertMany(
        [{ "_id" : 1, "grades" : [ 85, 80, 80 ] },
        { "_id" : 2, "grades" : [ 88, 90, 92 ] }]
   )
 陣列運算子 $ 單個佔位符

db.students.updateOne(
  { _id: 1, grades: 80 },
  { $set: { "grades.$" : 82 } }
)

修改_id=1的文件graders中第一個值為80的元素,值改成82

結果:

  [{ "_id" : 1, "grades" : [ 85, 82, 80 ] },
  { "_id" : 2, "grades" : [ 88, 90, 92 ] }]

 $[]  所有元素佔位符  

db.students.updateOne(
  { _id: 1},
  { $set: { "grades.$[]" : 100 } }
)

 

修改_id=1的文件graders中所有元素,值改成100

結果:

  [{ "_id" : 1, "grades" : [ 100, 100, 100 ] },
  { "_id" : 2, "grades" : [ 88, 90, 92 ] }]

 $[<identifier>]  符合arrayFilter過濾條件的元素佔位符  

db.students.updateOne(
  { _id: 2},
  { $set: { "grades.$[element]" : 100 } },
  { arrayFilters: [ { "element": { $gte: 90 } } ]}
)

 

修改_id=2的文件graders中大於等於90的元素,值改成100

結果:

  [{ "_id" : 1, "grades" : [  85, 82, 80  ] },
  { "_id" : 2, "grades" : [ 88, 100, 100 ] }]

 $push  新增元素  

db.students.updateOne(
  { _id: 1 },
  { $push: { "grades" : 98 } }
)

 

在_id=1的文件graders中新增元素

結果:

  [{ "_id" : 1, "grades" : [  85, 80, 80 ,98 ] },
  { "_id" : 2, "grades" : [ 88, 90, 92 ] }]

$addToSet  新增不存在的元素,如果元素已經存在則無操作  db.students.updateOne(
  { _id: 1 },
  { $addToSet: { "grades" : 100 } }
)
 

在_id=1的文件graders中新增元素

結果:

  [{ "_id" : 1, "grades" : [  85, 80, 80 ,98,100 ] },
  { "_id" : 2, "grades" : [ 88, 90, 92 ] }]

補充:如果新增的元素是85,因為85已經存在,所以不執行操作

 $pop  彈出(移除)元素  

db.students.updateOne(
  { _id: 1 },
  { $pop: { "grades" : 1 } }
)

 移除最後一個元素

結果:

  [{ "_id" : 1, "grades" : [  85, 80 ] },
  { "_id" : 2, "grades" : [ 88, 90, 92 ] }]

補充:如果{ $pop: { "grades" : -1 } }表示從前邊彈出,移除第一個元素

$pullAll 根據值移除陣列中的元素

db.students.update(
  {_id:2},
  {$pullAll:{"grades":[88,90]}}
)

移除_id=2的文件的graders中值為88,90的所有元素

結果:

  [{ "_id" : 1, "grades" : [  85, 80, 80  ] },
  { "_id" : 2, "grades" : [ 92 ] }]

 $pull  移除符合條件的元素  

db.students.update(
  {_id:2},
  {$pull:{"grades":{$gt:90}}}
)

 

移除_id=2的文件的graders中大於90的所有元素

結果:

  [{ "_id" : 1, "grades" : [  85, 80, 80  ] },
  { "_id" : 2, "grades" : [ 88,90 ] }]

  陣列批量新增

相關運算子

 $each  和$push,$addToSet配合使用,用於批量新增元素  

db.students.update(
  {_id:1},
  {$push:

    {grades:{$each:[99,100]}}

  }

)

在_id=1的文件graders中新增元素[99,100]

結果:

  [{ "_id" : 1, "grades" : [  85, 80, 80 ,99,100 ] },
  { "_id" : 2, "grades" : [ 88,90 ,92] }]

 $slice  和$push,$each配合使用,用於限制元素個數  

db.students.update(
  {_id:1},
  {$push:

    {grades:{$each:[99,100],$slice:4}}

  }
)

 在_id=1的文件graders中新增元素[99,100]

結果:

  [{ "_id" : 1, "grades" : [  85, 80, 80 ,99] },
  { "_id" : 2, "grades" : [ 88,90 ,92] }]

如果使用$slice:-4,則保留後4個元素

$sort 和$push,$each配合使用,在新增元素後進行排序

db.students.update(
  {_id:1},
  {$push:

    {grades:{$each:[70,100],$sort:1}}

  }
)

在_id=1的文件graders中新增元素[99,100],並排序

結果:

  [{ "_id" : 1, "grades" : [70,80,80,85,100]},
  { "_id" : 2, "grades" : [ 88,90 ,92] }]

如果使用$sort:-1,則倒序排序

$position 和$push,$each配合使用,指定插入元素的位置

db.students.update(
  {_id:1},
  {$push:

    {grades:{$each:[70,100],$position:1}}

  }
)

 在_id=1的文件graders中,從索引1開始插入元素[99,100]

結果:

  [{ "_id" : 1, "grades" : [85,70,100,80,80]},
  { "_id" : 2, "grades" : [ 88,90 ,92] }]

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

 

相關文章