mongodb資料庫操作

jycg789發表於2020-11-16

### 安裝在Ubuntu16.04:


```bash
# 建立資料庫資料夾,這個是預設存放資料的地方,但MongoDB不會自動建立
mkdir -p /data/db
# 增加許可權
chown -R $USER:$USER /data/db
wget -qO - https://www.mongodb.org/static/pgp/server-4.0.asc | sudo apt-key add -  # 匯入公鑰
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list  # 為MongoDB建立列表檔案
sudo apt-get update  # 重新載入本地資料包
sudo apt-get install -y mongodb-org=4.0.12 mongodb-org-server=4.0.12 mongodb-org-shell=4.0.12 mongodb-org-mongos=4.0.12 mongodb-org-tools=4.0.12  # 安裝軟體包
echo "mongodb-org hold" | sudo dpkg --set-selections
echo "mongodb-org-server hold" | sudo dpkg --set-selections
echo "mongodb-org-shell hold" | sudo dpkg --set-selections
echo "mongodb-org-mongos hold" | sudo dpkg --set-selections
echo "mongodb-org-tools hold" | sudo dpkg --set-selections  # 保持目前的版本,防止自動更新
# 啟動
sudo service mongodb start #或者
systemctl start mongodb
```


### 解除安裝


```bash
sudo apt-get purge mongodb-org*
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb
```


### 通過systemctl start mongodb 啟動時的錯誤解決


報錯如下:


Failed to start mongodb.service: Unit mongodb.service not found.


解決:


```bash
sudo nano /etc/systemd/system/mongdb.service  # 建立服務檔案
```


```bash
[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target

[Service]
User=mongodb
ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf
# 制定需要執行的命令的位置,                                #  制定專案執行所需要的配置檔案位置
[Install]
WantedBy=multi-user.target
```


- 按ctrl + x 儲存退出
- systemctl  start mongodb
- 永久有效: systemctl enable mongodb


### 1、文件中值的資料型別


在mongodb中資料是以文件的形式存在的


1、每個文件相當於一條記錄


2、多個文件組合在一起,就是集合,一個集合就相當於一張資料表


3、多個集合組合在一起,就是資料庫,每個資料庫就是一個資料夾


- Object ID: ⽂檔ID
- String: 字串, 最常⽤, 必須是有效的UTF-8
- Boolean: 儲存⼀個布林值, true或false
- Integer: 整數可以是32位或64位, 這取決於伺服器
- Double: 儲存浮點值
- Arrays: 陣列或列表, 多個值儲存到⼀個鍵
- Object: ⽤於嵌⼊式的⽂檔, 即⼀個值為⼀個⽂檔
- Null: 儲存Null值
- Timestamp: 時間戳, 表示從1970-1-1到現在的總秒數
- Date: 儲存當前⽇期或時間的UNIX時間格式


### 2、Mongo  shell


- 使用mongo命令自動啟動shell,並連線資料庫伺服器,該shell能夠解析JavaScript的語法
- 通過shell可以完成一些列的增刪改查工作


### 3、文件的基本增刪改查


- 增加:使用insert()函式,將要插入的資料放到集合中


  - 比如增加一個文件,首先先建立文件變數


    post = {"title": "my first mongo"}


  - 插入文件到blog集合中


    db.blog.insert(post)


  - 批量插入:同樣適用inset方法,將你需要插入的文件全部存放到一個陣列中


  - 當我們連線到伺服器之後,**db**會自動指向正在使用的資料庫,blog為集合名,沒有會自動建立


- 查詢:使用find()或者findOne()


  - find()預設顯示20條資料


    db.blog.find()


  - findOne()只顯示一條資料


    db.blog.findOne()


- 更新:使用**update()**方法, 該方法接收兩個引數,第一個為需要修改的文件,第二個修改過的文件


  **update()方法除了接收前兩個引數外,還可以接收另外兩個引數,並且這兩個引數都是布林值**


  **例如:db.blog.update({"title": "My Blog Post"}, {"title": "My Blog Get"}, true, true)**


  **第三個參數列示upsert,能夠保證操作的原子性,同時在沒有查詢到制定的集合情況下,會以查詢條件和更新文件為基礎新建一個文件,第四個引數預設是true,表示更新所有符合條件的集合,若改為false,則只更新第一個。**


  - 第一:先更改文件物件的屬性,也就是鍵值對,比如前文使用到的post變數


    給他增加一個屬性:post.comments = []


  - 找到舊文件,替換為新的文件,根具給出的一個引數中的具體資訊可以找到舊文件


    db.blog.update({"title":"my first mongo"}, post)


- 刪除:remove(),在不給出引數的情況下,刪除集合中的所有文件,給出引數後,會刪除對應的文件


  - db.blog.remove({"title":"my first mongo"})


### 4、基於shell的其他命令,利用help檢視


```shell
> help
```


| db.help()                  | help on db methods                                        |
| -------------------------- | --------------------------------------------------------- |
| db.mycoll.help()           | help on collection methods                                |
| sh.help()                  | sharding helpers                                          |
| rs.help()                  | replica set helpers                                       |
| help admin                 | administrative help                                       |
| help keys                  | key shortcuts                                             |
| help connect               | connecting to a db help                                   |
| help misc                  | misc things to know                                       |
| help mr                    | mapreduce                                                 |
| show dbs                   | show database names                                       |
| show collections           | show collections in current database                      |
| show users                 | show users in current database                            |
| show profile               | show most recent system.profile entries with time >= 1ms  |
| show logs                  | show the accessible logger names                          |
| use <db_name>              | set current database                                      |
| db.foo.find()              | list objects in collection foo                            |
| db.foo.find( { a : 1 } )   | list objects in foo where a == 1                          |
| it                         | result of the last line evaluated; use to further iterate |
| DBQuery.shellBatchSize = x | set default number of items to display on shell           |
| exit                       | quit the mongo shell                                      |


### 5、訪問集合


1、當集合名與內建的函式名相同時,是沒有辦法通過**db.集合名**來訪問的


2、當集合名名包含**-**時,比如:**six-four**,同樣沒有辦法通過正常的方法訪問集合,引文shell對其的解釋是兩個變數的相減


**最終的解決辦法是通過__getCollection()__來訪問,**例如:db.getCollection("version"),db.getCollection("six-four")這樣才能正常訪問


### 6、save shell


save是一個shell函式,可以在文件不存在的時候插入,存在的時候更新,它只有一個引數,即文件本身


save會呼叫upsert,即實現文件的自動建立和更新


var x = db.foo.findOne()


x.num = 12


db.foo.save(x)


### 7、修改器,以$號開頭的特殊字串


| 修改器 | 用法 | 作用 |
| ---- | :--- | :--- |
| $inc | db.x.update({"a":1}, {$inc: {"b":1}}) |在原有b的基礎上加1|
| $set | db.x.update({"a":1}, {$set: {"b":"a"}}) |將原有b的資料修改為a,若b不存在,則建立一個|
| $unset | db.x.update({"a":1}, {$unset: {"b":1}}) |撤銷修改,此時的數值1,表示條件為真的意思|
| $push | db.x.update({"a":1}, {$push:{"comments": {"name":"json"}}}) |該修改器只作用於陣列,可以向文件中已有陣列中新增值,當然陣列不存在時,建立一個陣列並新增對應內容|
| $pop | db.x.update({"a":1}, {$pop:{"comments": 1}})                                      db.x.update({"a":1}, {$pop:{"comments": -1}}) |基於陣列位置的刪除,1表示從陣列的尾部刪除一個元素,-1表示從陣列的頭部刪除一個元素|
| $addToSet | db.x.update({"a":1}, {$addToSet:{"email": "aa@163.com"}) |該修改器只作用於陣列,可以向陣列中新增一個值,若陣列中已經有了該值,則新增失敗|
| $each | db.x.update({"a":1}, {$addToSet:{"email":  {$each: ["1", "2", "3"]}}) |可以遍歷出陣列中的每個值,通常與$addToSet搭配使用,完成多個值得新增,如果存在不新增|
| $pull | db.x.update({"a":1}, {$pull:{"email":  "1"}}) |指定陣列中的具體元素進行刪除,若匹配到多個值,匹配多少刪除多少|
| $陣列定位符 | db.x.update({"comments.author": "jhon"}, {$set:{"comments.$.author": 'lili'}}) |定位符只匹配第一個匹配到的文件|


### 8、查詢


#### 8.1、指定鍵返回查詢


- 查詢使用find函式或者findOne函式
- 查詢返回指定欄位,也就是查詢指定鍵
- db.blog.find({"a":2}, {"ss":1, "wo": 0}),當匹配到包含{"a":2}的文件後,只返回"_id" 和"ss"兩個鍵,"_id"是預設返回的,其中1表示真,0表示假


#### 8.2、條件查詢


| 命令    | 用法                                                        | 作用                                                         |
| ------- | ----------------------------------------------------------- | ------------------------------------------------------------ |
| $gt     | db.users.find({"age": {"$gt": 20}})                         | 查詢users集合中年齡大於20的文件                              |
| $gte    | db.users.find({"age": {"$gte": 20}})                        | 查詢users集合中年齡大於等於20的文件                          |
| $lt     | db.users.find({"age": {"$lt": 20}})                         | 查詢users集合中年齡小於20的文件                              |
| $lte    | db.users.find({"age": {"$lte": 20}})                        | 查詢users集合中年齡小於等於20的文件                          |
| $ne     | db.users.find({"username": {"$ne": "andy"}})                | 查詢users集合中使用者名稱不等於andy的文件                        |
| $or     | db.users.find({"$or": [{"age": 20},{"username": "andy"} ]}) | $or 鍵對應的值是一個陣列,滿足陣列中任何一個條件的文件都會被查詢出來,可作用於不同的鍵 |
| $in     | db.users.find({"age": {"$in": [12 ,10, 22]}})               | $in鍵對應的值是一個陣列,滿足陣列中任何一個條件的文件都會被查詢出來,但是它只作用於同一個鍵 |
| $nin    | db.users.find({"age": {"$nin": [12 ,10, 22]}})              | $nin鍵對應的值是一個陣列,不滿足陣列中任何一個條件的文件都會被查詢出來,它也只作用於同一個鍵 |
| $mod    | db.users.find({"age": {"$mod": [5, 1]}})                    | $mod表示取模運算,運算完成後在作為查詢條件,它對應的值是一個陣列,該陣列只有兩個值且有順序,第一個表示除數,第二個表示餘數,在這裡表達的意思是,找出年齡除以5後餘數為1的文件 |
| $not    | db.users.find({"$not":{"age": {"$mod": [5, 1]}}})           | $not是元條件查詢,即作用其他條件查詢之上,例如他與$mod取模運算結合使用,表示取非 |
| $existe | db.users.find({"x":{"$existe": true}})                      | $existe判斷該鍵是否真實存在,存在則顯示出來                  |
| /joy/i  | db.users.find({"username": /joy/i})                         | 這裡是利用正規表示式去匹配所查詢文件的某個鍵對應的值,i表示不區分大小寫 |


$where 自定義查詢


```js
db.stu.find({
    $where:function() {
        return this.age>30;}  // this 代表當前查詢的文件,找出年齡大於30歲的文件
})
```


#### 8.3、陣列查詢


##### 8.3.1、$all


- 建立如下集合並加入三條文件:


  db.fruit.insert({"_id": 1, "fruits": ["banana", "peach", "apple"]})


  db.fruit.insert({"_id": 2, "fruits": ["orange", "kumquat", "apple"]})


  db.fruit.insert({"_id": 3, "fruits": ["banana", "cherry", "apple"]})


- {"$all": ["banana", "apple"]},作用於陣列,


  使用db.fruit.find({"fruits": {"$all" : ["banana", "apple"]}}),執行結果是數值fruits中只要包含有"banana"和"apple"元素的文件,全部返回,忽略陣列中元素的順序


- 特殊用法


  {"fruits": {"$all" : ["banana"]}}和{"fruits":"banana"},這兩條查詢效果是一樣的


- 基於陣列下標的查詢


  比如:db.fruit.find({"fruits.2":"apple"}),只要陣列中指定位置的元素匹配正確,就會返回結果


##### 8.3.2、$size


- 查詢固定陣列長度的文件


  - 用法:db.fruit.find({"fruits": {"$size": 3}})
  - 不能配合其他條件命令使用,例如$gt,而實際生產中,總是需要我們去查詢陣列範圍的文件


- 在文件中建立一個size的欄位來專門描述陣列的長度,變相的通過size的值來範圍查詢


  - 例如上文:增加一個size欄位,db.fruit.update({"_id": 1}, {"$inc": {"size": 3}})


  - 增加陣列的內容時,同時增加size的值


    db.fruit.update({"_id": 1}, {"$push" : {"fruits": "pear"}, "$inc" : {"size": 1}})


  - 最後就可以進行範圍查詢了


    db.fruit.find({"size": {"$gt": 3}})


##### 8.3.3、$slice


- 對查詢到的陣列進行切片處理,並返回新的切片後的文件


  - 使用$slice必須配合find函式的第二個引數使用,find的第二引數是指定文件中的鍵返回的,未提及或者對應值為0,將不會被返回,當使用$slice後,即使不提及文件中的其他鍵同樣會返回


    ```mongo
    db.fruit.find({"_id" : 1}, {"fruits": 1})
    >{ "_id" : 1, "fruits" : [ "banana", "peach", "apple", "pear"]}  # 這裡_id預設返回
    # 如果將_id的值變為0,則不會被返回
    db.fruit.find({"_id" : 1}, {"fruits": 1, "_id": 0})  # 數字代表真假
    >{"fruits" : [ "banana", "peach", "apple", "pear"]}
    # 當對返回的鍵使用切片後$slice,文件中所有的鍵預設都會返回
    db.fruit.find({"_id": 1}, {"fruit": {"$slice": 3}})
    >{ "_id" : 1, "fruits" : [ "banana", "peach", "apple" ], "size" : 4 }
    ```


- 對陣列正向切割


  db.fruit.find({"_id": 1}, {"fruit": {"$slice": 3}}), $slcie對應的值是正數即可,得到陣列的前三個元素


- 對陣列反向切割


  db.fruit.find({"_id": 1}, {"fruit": {"$slice": -3}}), $slcie對應的值是負數即可,得到陣列的後三個元素


- 對陣列指定範圍切片


  db.fruit.find({"_id": 1}, {"fruit": {"$slice": [1, 3]}}),$slice對應的值為陣列,該陣列表示你要操作的陣列下標


  ,得到結果:**{ "_id" : 1, "fruits" : [ "peach", "apple", "pear" ], "size" : 4 }**,包含起始和結束下標


#### 8.4、查詢內嵌文件


- 在mongodb中文件的表現形式,像極了javascript中的物件,所以可以通過**物件.屬性**的方法,來訪問MongoDB中的內嵌文件,但也可以匹配整個內嵌文件


- 方法一:如有文件


  {"name": {"first": "Joe", "last": "Schmoe"}, "age": 27},在集合people中


  db.people.find({"name": {"first": "Joe", "last": "Schmoe"}}),這裡可以找到有這個內嵌文件的文件,但是有一個缺點,就是查詢條件必須匹配整個內嵌文件,否則就會匹配失敗,同時順序也不能夠錯,也就是要求精確匹配


- 方法二:通過**物件.屬性**的方法來訪問,就不存在限制了


  db.people.find({"name.first": "Joe", "name.last": "Schmoe"}),這裡就要求精確匹配了


- $elemMatch


  將限定條件進行分組,僅當需要對一個內嵌文件的多個鍵操作時才會用到


  db.blog.find({"comments": {"$elemMatch": {"author": "joe", "score": {"$gte": 5}}}})


### 9、排序、分頁和跳過


#### 9.1、排序sort


- 使用方法:db.collection.find().sort({"username": 1})


- 返回結果按升序排列,同時可以設定多個鍵排序


  db.collection.find().sort({"username":1, "age": -1})


  先以username升序排列,若username相同,則按照age降序排列


#### 9.2、分頁limit


- 使用方法:


  db.collection.find().limit(20),表示每頁返回20條資料


#### 9.3、跳過skip


- db.collection.find().skip(20),跳過前20個資料


**組合實現分頁查詢**


- db.collection.find().limit(20).sort({"age": -1})


  按照年齡從大到小取出前二十條文件


- db.collection.find().limit(20).skip(20).sort({"age": -1})


  跳過前20條,取後20條的結果


### 10、建立索引


- 方法:db.users.ensureindex({"username": 1})


### 11、遊標物件


使用find函式返回的物件可以賦值給一個變數,這個變數就可以理解為是一個遊標物件


**遊標物件屬性** :可迭代(iterable)


**遊標物件方法** :hasNext()判斷是否還有下一條資料


next()獲取下一條資料


### 12、聚合函式(aggregate)


#### 12.1、count函式


db.stu.count()  # 返回fruit集合中的總文件數


現有如下集合mycol:


```javascript
{ "_id" : 1, "name" : "tom", "sex" : "男", "score" : 100, "age" : 34 }
{ "_id" : 2, "name" : "jeke", "sex" : "男", "score" : 90, "age" : 24 }
{ "_id" : 3, "name" : "kite", "sex" : "女", "score" : 40, "age" : 36 }
{ "_id" : 4, "name" : "herry", "sex" : "男", "score" : 90, "age" : 56 }
{ "_id" : 5, "name" : "marry", "sex" : "女", "score" : 70, "age" : 18 }
{ "_id" : 6, "name" : "john", "sex" : "男", "score" : 100, "age" : 31 }
```


#### 12.2、$group,$sum


db.stu.aggregate({$group:{_id: "$sex", Count: {"$sum": 1}}})


#### 12.3、$group,$push


```json
db.stu.aggregate(
     {$group:
         {
            _id:"$gender",
             name:{$push:"$name"}
         }
     }
)  把所有的資料放到一起
$$ROOT, 把整個文件放到一個陣列中
db.stu.aggregate(
     {$group:
         {
             _id:null,
             name:{$push:"$$ROOT"}
         }
     }
)
```


#### 12.4、$match`


```json
db.stu.aggregate(
     {$match:{age:{$gt:20}}
     )
```


#### 12.5、$project


```json
db.stu.aggregate(
     {$project:{_id:0,name:1,age:1}}
     ) //控制顯示的文件鍵
```


#### 12.6、$sort


```json
db.stu.aggregate(
     {$group:{_id:"$gender",counter:{$sum:1}}},
     {$sort:{counter:-1}}
)
```


#### 12.7、$limit,$skip


```json
db.stu.aggregate(
     {$group:{_id:"$gender",counter:{$sum:1}}},
     {$sort:{counter:-1}},
     {$skip:1},
     {$limit:1}
)
```


### 13、資料庫命令(runCommand)


#### 13.1、distinct函式


db.runCommand({"distinct": "fruit" , "key": "fruits"})


runCommand指令表示執行命令,括號內的引數的第一個鍵為具體的指令,指令所對應的的值是要操作的集合,key對應的是要操作的鍵。


distinct所表達的意思是找出集合中某個鍵對應多少不同的值,也就是去重。


#### 13.2、group函式


- 對文件進行分組,stocks集合中有以下文件:


  db.stock.insert({"day" : "2010/10/03", "time": "10/03/2010 03:57:01 GMT-400", "price": "4.23"})


  db.stock.insert({"day" : "2010/10/04", "time": "10/04/2010 11:28:39 GMT-400", "price": "4.27"})


  db.stock.insert({"day" : "2010/10/03", "time": "10/03/2010 05:00:22 GMT-400", "price": "4.10"})


  db.stock.insert({"day" : "2010/10/06", "time": "10/06/2010 05:27:58 GMT-400", "price": "4.30"})


  db.stock.insert({"day" : "2010/10/04", "time": "10/04/2010 08:34:50 GMT-400", "price": "4.01"})


  獲取到每天最新的股票交易價格


- 使用資料庫命令的模式來執行group命令:


  ```javascript
  db.runCommand({"group":{
      "ns": "stock",
      "key": {"day": true},  // 程式執行到這裡之後,就已經對集合分組完畢了
      "$keyf": function(X){
          return X.toLowerCase() // 將需要排序的鍵進行轉換,比如進行大小寫轉換
      },
      "initial":{"time":0},  // 每組文件遍歷時候的初始值
      "$reduce":function(doc, prev){
          if(doc.time>prev.time){  // 判斷時間大小,更新要顯示的最近時間
              prev.time = doc.time;
              prev.price= doc.price;
          }
      },
      "condition": {"day": {"$gt": "2010/10/03"}},// 對分組進行條件限制,必須大於條件時間
      "finalize":function(prev){  //對返回結果做最後的修改,和限定
          ...
      }
  }})
  ```


  **引數解析**


  group:對集合執行的操作,即分組


  ns:需要操作的資料集


  key:以文件中的哪個鍵進行分組


  $keyf: 對需要分組的鍵進行條件轉換


  initial: 每組文件的初始化值,也就是最終分完組後顯示的欄位的初始化值


  $reduce: 對分組後的顯示文件做最後的操作,在上述案例中,就是找到那一組文件中時間最新的文件,然後把他的時間和股票交易價格顯示到我們可以看到的結果


  condition: 對分組進行條件限制


  finalize: 對返回的結果做最後的修改


  #### 13.3、findAndModify


  db.runCommand({"findAndModify": "stock",  # 操作集合stock


  ​        "query": {"day":{"$gt": "2010/09/30"}},    # 查詢條件


  ​        "sort": {"day": -1},  # 排序鍵


  ​        "remove": true  # 是否刪除文件true


  })


  #### 13.4、建立固定大小的集合


  db.creatCollection("my_collection",{"capped": true, "size": 100000})


  **引數解析**


  my_collection:集合名


  capped:表示是否限值集合的大小,true表示限值,false表示不限制


  size:表示集合大小,這裡表示100000個位元組

相關文章