03-MongoDB

一、MongoDB簡介

1.概述

? MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫。旨在為WEB應用提供可擴展的高性能數據存儲解決方案。

MongoDB介于關系型數據和非關系型數據庫之間,是非關系數據庫當中功能最豐富,最像關系數據庫的。他支持的數據結構非常松散,類似json格式,因此可以存儲比較復雜的數據類型。

MongoDB最大的特點是他支持的查詢語言非常強大,其語法有點類似于面向對象的查詢語言,幾乎可以實現類似關系數據庫表單查詢的絕大部分功能,而且還支持對數據建立索引。

2.和MySQL之間的區別

2.1MySQL

關系型數據庫。
查詢語句是使用傳統的sql語句,擁有較為成熟的體系,成熟度很高。
關系型數據庫遵循ACID規則
開源數據庫的份額在不斷增加,mysql的份額頁在持續增長。
缺點:在海量數據處理的時候效率會顯著變慢。

2.2MongoDB

非關系型數據庫(nosql ),屬于文檔型數據庫。先解釋一下文檔的數據庫,即可以存放xml、json、bson類型系那個的數據。這些數據具備自述性(self-describing),呈現分層的樹狀數據結構。數據結構由鍵值(key=>value)對組成。
存儲方式:虛擬內存+持久化。
查詢語句:是獨特的Mongodb的查詢方式。
適合場景:事件的記錄,內容管理或者博客平臺等等。
數據處理:數據是存儲在硬盤上的,只不過需要經常讀取的數據會被加載到內存中,將數據存儲在物理內存中,從而達到高速讀寫。
成熟度與廣泛度:新興數據庫,成熟度較低,Nosql數據庫中最為接近關系型數據庫,比較完善的DB之一,適用人群不斷在增長。
優勢:
快速!在適量級的內存的Mongodb的性能是非常迅速的,它將熱數據存儲在物理內存中,使得熱數據的讀寫變得十分快,
高擴展!
自身的Failover機制!
json的存儲格式!

2.3二者之間的區別
MySQL MongoDB 解釋
database database 數據庫
table collection 數據表/集合
row【一條記錄,實體】 document 行/文檔
column field 列/字段或者屬性
table join 不支持 表連接
primary key primary key 主鍵

解釋

數據庫:容器,不管是mysql還是mongodb,一個單一的服務器都可以管理多個數據庫

集合:是一組mongodb的文件,等價于mysql中的table,集合中文檔可以有不同的字段,也可以有不同的數據類型

文檔:一組鍵值對,具有動態模式【不同的數據可以是不同的格式】

二、MongoDB安裝和卸載

1.卸載

執行命令:
sudo apt-get autoremove mongodb
sudo apt-get autoclean mongodb

#清除殘留數據
dpkg -l |grep ^rc|awk '{print $2}' |tr ["\n"] [" "]|sudo xargs dpkg -P 

2.安裝

第1步 – 導入公鑰**

Ubuntu軟件包管理器apt(高級軟件包工具)需要軟件分銷商的GPG密鑰來確保軟件包的一致性和真實性。 執行此下面的命令將MongoDB密鑰導入到您的服務器:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5

第2步 – 創建源列表文件MongoDB

檢查URL http://repo.mongodb.org/apt/ubuntu/dists/。 如果您在該網頁上看到一個目錄“bionic”,則將下述命令中的單詞“xenial”替換為“bionic”一詞,【原因:MongoDB尚未發布Bionic Beaver軟件包,但Xenial軟件包在Ubuntu 18.04 LTS上運行良好】

執行以下命令在/etc/apt/sources.list.d/中創建一個MongoDB列表文件:

echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list

第3步 – 更新存儲庫

使用apt命令更新存儲庫:

sudo apt-get update

說明:執行完會提示一些失敗,不用在意

第4步 – 安裝MongoDB

執行以下命令來安裝MongoDB:

sudo apt-get install -y mongodb

第5步:啟動MongoDB

執行以下命令啟動MongoDB并將其添加為在啟動時啟動的服務

systemctl start mongodb

如果執行完這一步終端沒有任何輸出,則說明是正確的
如果啟動的時候提示:Failed to start mongod.service: Unit mongodb.service not found.
解決辦法如下:
  1創建配置文件:
      在/etc/systemd/system/下
      sudo vim mongodb.service

  2.在里面追加文本:
      [Unit]
      Description=High-performance, schema-free document-oriented database
      After=network.target
   
      [Service]
      User=mongodb
      ExecStart=/usr/bin/mongod --quiet --config /etc/mongodb.conf
   
      [Install]
      WantedBy=multi-user.target
  3.按ctrl+X退出
  4.啟動服務
      sudo systemctl start mongodb
      sudo systemctl status mongodb
  5.讓它永久啟動
      sudo systemctl enable mongodb
第6步:檢查MongoDB是否已經啟動在27017端口號上

執行下面的命令:

netstat -plntu

第7步:登錄MongoDB

mongo

如果出現錯誤全局初始化失敗:BadValue無效或無用戶區域設置。 請確保LANG和/或LC_ *環境變量設置正確,請嘗試命令:
  export LC_ALL=C
  mongo

補充:ubuntu下如何查看軟件安裝目錄以及安裝版本

1.查詢版本
aptitude show 軟件名  或者 dpkg -l軟件名
2.查詢安裝路徑
dpkg -L 軟件名  或者 whereis 軟件名

三、MongoDB使用

1.創建和刪除數據庫

1.1創建數據庫

語法:

use DATABASE_NAME
注意:如果指定的數據庫DATABASE_NAME不存在,則該命令將創建一個新的數據庫,否則返回現有的數據庫

#mysql中
創建數據庫:create database basename;
切換數據庫:use basename;

數據庫命名:

? a.不能是空字符串

? b.不能包含特殊符號

? c.最好全部小寫

? d.有一些數據庫名是保留的,可以直接訪問特殊數據庫

? admin:從權限的角度來說,是root的數據庫

? local:本地數據

? config:配置,用于保存MongoDB的配置信息

演示:

#登錄MongoDB                                                       rock@rockrong:~$ mongo
MongoDB shell version: 2.6.10
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
  http://docs.mongodb.org/
Questions? Try the support group
  http://groups.google.com/group/mongodb-user
#創建數據庫并切換到該數據庫下
> use mydb1   
switched to db mydb1

其他命令:

#檢查當前選擇的數據庫
> db
mydb1

#檢查已經創建好的數據庫列表
> show dbs
admin  (empty)
local  0.078GB

#創建的數據庫(newdb)不在列表中。要顯示數據庫,需要至少插入一個文檔,空的數據庫是不顯示出來的
> db.mydb1.insert({'name':'yangyang'})
WriteResult({ "nInserted" : 1 })
> show dbs
admin  (empty)
local  0.078GB
mydb1  0.078GB

#退出MongoDB
> exit
bye
rock@rockrong:~$ mongo
MongoDB shell version: 2.6.10
#默認的數據庫test
connecting to: test

#如果數據庫已經存在,use表示切換數據庫
> use mydb1
switched to db mydb1
1.2刪除數據庫

語法:

db.dropDatabase()

#注意:默認刪除當前正在工作的數據庫,如果沒有通過use命令切換數據庫,則刪除的是test

演示:

#刪除當前正在工作的數據庫
> db.dropDatabase()
{ "dropped" : "mydb1", "ok" : 1 }
> show dbs
admin  (empty)
local  0.078GB

2.創建和刪除集合

類似于MySQL中的表

集合存在于數據庫中,集合沒有固定的結構,意味著可以對集合插入不同格式和不同類型的數據,但是盡量插入集合的時候保證數據的關聯性

集合名的規范:

? a.不能空字符串

? b.集合名不能含有\0【空字符】,表示集合名的結尾

? c.集合名不能以"system."開頭,為系統集合保留的關鍵字

? d.不能含有保留字符,千萬不能含有$

2.1創建集合

語法:

db.createCollection(name, options)

#注意
name的類型為String,是要創建的集合的名稱
options的類型是Document,是一個文檔,指定相應的大小和索引,是可選參數

下面是可以使用的選項列表:

在插入文檔時,MongoDB首先檢查上限集合capped字段的大小,然后檢查max字段。

字段 類型 描述
capped Boolean (可選)如果為true,則啟用封閉的集合。上限集合是固定大小的集合,它在達到其最大大小時自動覆蓋其最舊的條目。 如果指定true,則還需要指定size參數。
autoIndexId Boolean (可選)如果為true,則在_id字段上自動創建索引。默認值為false
size 數字 (可選)指定上限集合的最大大小(以字節為單位)。 如果cappedtrue,那么還需要指定此字段的值。
max 數字 (可選)指定上限集合中允許的最大文檔數。

演示:

> use test
switched to db test

#沒有options選項的集合的創建
> db.createCollection("myCollection")
{ "ok" : 1 }

#顯示當前數據庫下的集合列表
> show collections
myCollection
system.indexes

#有options選項的集合的創建
> db.createCollection("mycol",{capped:true,autoIndexId:true,size:1024,max:10000})
{ "ok" : 1 }
> show collections
myCollection
mycol
system.indexes

#如果一個集合不存在,直接向其中插入數據,會自動創建
> db.newcollection.insert({'name':'zhangsan'})
WriteResult({ "nInserted" : 1 })
> show collections
myCollection
mycol
newcollection
system.indexes
2.2刪除集合

語法:

db.COLLECTION_NAME.drop()

注意:如果選定的集合成功刪除,drop()方法將返回true,否則返回false

演示:

> db.newcollection.drop()
true
> show collections
myCollection
mycol

3.文檔操作

文檔:相當表中的一條記錄【實體】

是一組鍵值對,文檔不需要設置相同的字段,并且相同的字段不需要相同的數據類型

注意:

? a.文檔中的鍵值對是有序的

? b.文檔中值除了字符串之外,還可以是其他數據類型【嵌套一個文檔】

? c.嚴格區分大小寫和數據類型的,mycol myCol

? d.文檔中不能有重復的鍵

? e.文檔中的鍵基本都是用字符串表示的

文檔中鍵的命名:

? a.鍵不能包含\0

? b.$和.有特殊含義

? c.以下劃線開頭的鍵是保留的,盡量不要使用下劃線開頭

3.1插入文檔

語法:

db.COLLECTION_NAME.insert(document)

注意:在插入的文檔中,如果不指定_id參數,那么 MongoDB 會為此文檔分配一個唯一的ObjectId

_id為集合中的每個文檔唯一的12個字節的十六進制數。 12字節劃分如下

_id: ObjectId(4 bytes timestamp, 3 bytes machine id, 2 bytes process id, 
   3 bytes incrementer)

演示:

#插入單條文檔
> db.mycol.insert({
   id:100,
   name:'zhangsan',
  age:18,
  hobby:'sing',
  })
WriteResult({ "nInserted" : 1 })

#查看已經插入的文檔
> db.mycol.find()
{ "_id" : ObjectId("5b3d8c919e00ffe8882d9705"), "id" : 100, "name" : "zhangsan", "age" : 18, "hobby" : "sing" }
> db.mycol.find().pretty()
{
  "_id" : ObjectId("5b3d8c919e00ffe8882d9705"),
  "id" : 100,
  "name" : "zhangsan",
  "age" : 18,
  "hobby" : "sing"
}

#批量插入文檔
#直接寫文檔,只能插入一條
> db.mycol.insert({ id:101, name:"lisi", age:20, hobby:'dance', },{ id:102, name:'jack', age:15, hobby:'write', })
WriteResult({ "nInserted" : 1 })

#用數組可以插入多條
> db.check.insert([
      {
         _id: 101,
         title: 'MongoDB Guide', 
         description: 'MongoDB is no sql database',
         by: 'yiibai tutorials',
         url: 'http://www.yiibai.com',
         tags: ['mongodb', 'database', 'NoSQL'],
         likes: 100
      },
  
      {
        _id: 102,
         title: 'NoSQL Database', 
         description: "NoSQL database doesn't have tables",
         by: 'yiibai tutorials',
         url: 'http://www.yiibai.com',
         tags: ['mongodb', 'database', 'NoSQL'],
         likes: 210, 
         comments: [
            {
               user:'user1',
               message: 'My first comment',
               dateCreated: new Date(2017,11,10,2,35),
               like: 0 
            }
         ]
      },
      {
         _id: 104,
         title: 'Python Quick Guide', 
         description: "Python Quick start ",
         by: 'yiibai tutorials',
         url: 'http://www.yiibai.com',
         tags: ['Python', 'database', 'NoSQL'],
         likes: 30, 
         comments: [
            {
               user:'user1',
               message: 'My first comment',
               dateCreated: new Date(2018,11,10,2,35),
               like: 590 
            }
         ]
     }
  ])
BulkWriteResult({
  "writeErrors" : [ ],
  "writeConcernErrors" : [ ],
  "nInserted" : 3,
  "nUpserted" : 0,
  "nMatched" : 0,
  "nModified" : 0,
  "nRemoved" : 0,
  "upserted" : [ ]
})
> db.check.find().pretty()
{
  "_id" : 101,
  "title" : "MongoDB Guide",
  "description" : "MongoDB is no sql database",
  "by" : "yiibai tutorials",
  "url" : "http://www.yiibai.com",
  "tags" : [
      "mongodb",
      "database",
      "NoSQL"
  ],
  "likes" : 100
}
{
  "_id" : 102,
  "title" : "NoSQL Database",
  "description" : "NoSQL database doesn't have tables",
  "by" : "yiibai tutorials",
  "url" : "http://www.yiibai.com",
  "tags" : [
      "mongodb",
      "database",
      "NoSQL"
  ],
  "likes" : 210,
  "comments" : [
      {
          "user" : "user1",
          "message" : "My first comment",
          "dateCreated" : ISODate("2017-12-09T18:35:00Z"),
          "like" : 0
      }
  ]
}
{
  "_id" : 104,
  "title" : "Python Quick Guide",
  "description" : "Python Quick start ",
  "by" : "yiibai tutorials",
  "url" : "http://www.yiibai.com",
  "tags" : [
      "Python",
      "database",
      "NoSQL"
  ],
  "likes" : 30,
  "comments" : [
      {
          "user" : "user1",
          "message" : "My first comment",
          "dateCreated" : ISODate("2018-12-09T18:35:00Z"),
          "like" : 590
      }
  ]
}
>
其它插入文檔的方法【作為了解】

db.collection.insertOne():插入單個文檔

db.collection.insertMany():插入多個文檔

a.db.collection.insertOne()方法

db.collection.insertOne()方法將單個文檔插入到集合中。 如果文檔沒有指定_id字段,MongoDB會自動將_id字段與ObjectId`值添加到新文檔

演示:

#以下示例將新文檔插入到庫存集合中
db.invent.insertOne({ 
       item: "canvas", 
       num: 100, 
       tags: ["cotton"], 
       size: { 
            h: 20,
            w: 30, 
        } 
})


#db.collection.insertOne()方法返回包含新插入的文檔的`_id```字段值的文檔

執行結果如下:
> db.inventory.insertOne(
      { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
   )
{
        "acknowledged" : true,
        "insertedId" : ObjectId("5955220846be576f199feb55")
}
>

b.db.collection.insertMany()方法

db.collection.insertMany()方法將多個文檔插入到集合中,可將一系列文檔傳遞給db.collection.insertMany()方法。以下示例將三個新文檔插入到庫存集合中。如果文檔沒有指定_id字段,MongoDB會向每個文檔添加一個ObjectId值的_id`字段

演示:

db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])

#insertMany()返回包含新插入的文檔_id字段值的文檔。執行結果如下:
> db.inventory.insertMany([
      { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
      { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
      { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
   ])
{
        "acknowledged" : true,
        "insertedIds" : [
                ObjectId("59552c1c46be576f199feb56"),
                ObjectId("59552c1c46be576f199feb57"),
                ObjectId("59552c1c46be576f199feb58")
        ]
}
3.2查詢文檔

語法:

db.COLLECTION_NAME.find(document)

注意:
find()將以非結構化的方式返回查詢結果

要以格式化的方式返回查詢結果,可以結合pretty函數使用
db.COLLECTION_NAME.find(document).pretty()

findOne():返回一個文檔
MongoDB 與 RDBMS的等效MySQL子句

要在一些條件的基礎上查詢文檔,可以使用以下操作:

操作 語法 示例 MySQL等效語句
相等 {<key>:<value>} db.mycol.find({"by":"yiibai"}).pretty() where by = 'yiibai'
小于 {<key>:{$lt:<value>}} db.mycol.find({"likes":{$lt:50}}).pretty() where likes < 50
小于等于 {<key>:{$lte:<value>}} db.mycol.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.mycol.find({"likes":{$gt:50}}).pretty() where likes > 50
大于等于 {<key>:{$gte:<value>}} db.mycol.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.mycol.find({"likes":{$ne:50}}).pretty() where likes != 50

演示:

a.MongoDB中的AND操作符

語法:

#在find()方法中,如果通過使用’,‘將它們分開傳遞多個鍵,則 MongoDB 將其視為AND條件。 以下是AND的基本語法
>db.mycol.find(
   {
      $and: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

演示:

#第一種表示and:  $and[{鍵值對},{鍵值對}]
db.check.find({$and:[{'by':'yiibai tutorials'},{'title':'Python Quick Guide'}]}).pretty()
{
  "_id" : 104,
  "title" : "Python Quick Guide",
  "description" : "Python Quick start ",
  "by" : "yiibai tutorials",
  "url" : "http://www.yiibai.com",
  "tags" : [
      "Python",
      "database",
      "NoSQL"
  ],
  "likes" : 30,
  "comments" : [
      {
          "user" : "user1",
          "message" : "My first comment",
          "dateCreated" : ISODate("2018-12-09T18:35:00Z"),
          "like" : 590
      }
  ]
}
> 

#第二種表示and:{鍵值對,鍵值對}
 db.check.find({'by':'yiibai tutorials','title':'Python Quick Guide'}).pretty()

{
  "_id" : 104,
  "title" : "Python Quick Guide",
  "description" : "Python Quick start ",
  "by" : "yiibai tutorials",
  "url" : "http://www.yiibai.com",
  "tags" : [
      "Python",
      "database",
      "NoSQL"
  ],
  "likes" : 30,
  "comments" : [
      {
          "user" : "user1",
          "message" : "My first comment",
          "dateCreated" : ISODate("2018-12-09T18:35:00Z"),
          "like" : 590
      }
  ]
}

#等效的SQL的語句
select * from check where by="" and title="";

b.MongoDB中的OR操作符

語法:

#在要根據OR條件查詢文檔,需要使用$or關鍵字。 以下是OR條件的基本語法
>db.mycol.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

演示:

 db.check.find({$or:[{'by':'yiibai tutorials'},{'title':'Python Quick Guide'}]}).pretty()

c.使用 AND 和 OR 聯合使用

演示:

> db.check.find({'likes':{$gt:100},$or:[{'title':'NoSQL Database'},{'by':'yiibai tutorials'}]}).pretty()
{
  "_id" : 102,
  "title" : "NoSQL Database",
  "description" : "NoSQL database doesn't have tables",
  "by" : "yiibai tutorials",
  "url" : "http://www.yiibai.com",
  "tags" : [
      "mongodb",
      "database",
      "NoSQL"
  ],
  "likes" : 210,
  "comments" : [
      {
          "user" : "user1",
          "message" : "My first comment",
          "dateCreated" : ISODate("2017-12-09T18:35:00Z"),
          "like" : 0
      }
  ]
}
> 
select * from check where likes>100 and (title='' or by='')
3.3更新文檔

1>update():更新現有文檔中的值

criteria:用于指定一個查詢,查詢選擇將要更新的目標記錄

action:用于指定更新信息,也可以使用操作符完成

options:

語法:

db.COLLECTION_NAME.update(SELECTION_CRITERIA, UPDATED_DATA)

演示:

#find():查詢指定列的數據
#{'_id':1,'title':1}:表示要檢索的字段列表
#注意:當執行find函數的時候,它默認將所有的文檔顯示,為了限制顯示的字段,需要將字段列表的值設置為1,如果不顯示可以設置為0
> db.check.find({'title':'MongoDB Guide'},{'_id':1,'title':1})
{ "_id" : 101, "title" : "MongoDB Guide" }

#注意:update默認只更新一個文檔,如果要更新多個文檔,則添加參數{multi:true})
db.check.update({'title':'MongoDB Guide'},{$set:{'title':'aaaaaa'}},{multi:true})

2>save():使用save()方法中傳遞的文檔數據替換現有文檔

語法:

>db.COLLECTION_NAME.save({_id:ObjectId(),NEW_DATA})

演示:

> use mydb1
switched to db mydb1
> db.check.save({'_id':102,'titlt':'bbbb','by':'hello'})
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 102 })
> db.check.find({'_id':102},{'_id':1,'title':1,'by':1})
{ "_id" : 102, "by" : "hello" }
3.4刪除文檔

MongoDB中的 remove()方法用于從集合中刪除文檔。 remove()方法接受兩個參數。 一個是刪除條件,第二個是標志:justOne

? criteria - (可選)符合刪除條件的集合將被刪除。

? justOne - (可選)如果設置為true1,則只刪除一個文檔

語法:

>db.COLLECTION_NAME.remove(DELLETION_CRITTERIA)

演示:

> db.check.remove({'_id':100})
WriteResult({ "nRemoved" : 0 })
> db.check.find({},{'_id':1,'title':1})
{ "_id" : 102 }
> db.check.remove()
2018-07-05T15:02:29.068+0800 remove needs a query at src/mongo/shell/collection.js:299
> db.check.find({},{'_id':1,'title':1})
{ "_id" : 102 }
> db.check.find().pretty()

4.查詢

4.1投影

投影:查詢過程中,只顯示指定的字段

語法:

>db.COLLECTION_NAME.find({},{KEY:1})

演示:

#在查詢文檔時只顯示文檔的標題
> db.mycol.find({}, {'title':1,'_id':0})

##注意,在執行find()方法時,始終都會顯示_id字段,如果不想要此字段,則需要將其設置為0
#只能為全1或全0,但是對于_id,可以不受此限制
4.2限制篩選記錄

1>limit方法

作用:限制MongoDB要返回的記錄數

根據指定的參數返回記錄數

語法:

> db.COLLECTION_NAME.find().limit(NUMBER)

演示:

#在查詢文檔時僅顯示兩個文檔
> db.mycol.find({},{"title":1,_id:0}).limit(2)

2>skip方法

語法:

>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

演示:

> db.mycol.find({},{"title":1,_id:0}).limit(1).skip(2)
#先跳過2條,再總共顯示1條
#注意:skip()方法中的默認值為0。
4.3對查詢記錄排序

語法:

>db.COLLECTION_NAME.find().sort({KEY:1})

注意:使用指定順序進行排序,1表示升序,-1表示降序

演示:

> db.mycol.find({},{'_id':1, 'title':1})
# 按`title`降序排序
> db.mycol.find({},{"title":1,_id:0}).sort({"title":-1})
# 按`title`降序排序
> db.mycol.find({},{"title":1,_id:0}).sort({"title":-1})
4.4分組與聚合函數查詢

1>aggregate()方法

語法:

>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

以下是可用聚合表達式的列表。

表達式 描述 示例
$sum 從集合中的所有文檔中求出定義的值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
$avg 計算集合中所有文檔的所有給定值的平均值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min 從集合中的所有文檔獲取相應值的最小值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max 從集合中的所有文檔獲取相應值的最大值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
$push 將值插入到生成的文檔中的數組中。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet 將值插入生成的文檔中的數組,但不會創建重復項。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根據分組從源文檔獲取第一個文檔。 通常情況下,這只適用于以前應用的“$sort”階段。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last 根據分組從源文檔獲取最后一個文檔。通常情況下,這只適用于以前應用的“$sort”階段。 db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])

演示:

 db.article.aggregate([{$group:{'_id':'$by_user','num_tutorial':{$sum:1}}}])
{ "_id" : "Curry", "num_tutorial" : 1 }
{ "_id" : "Kuber", "num_tutorial" : 1 }
{ "_id" : "Maxsu", "num_tutorial" : 2 }

#等效的sql語句
select  by_user, count(*) as num_tutorial from article group by by_user

管道:

每一組的輸出可以作為另一組的輸入,并且生成一組新的文檔

$sort

$limit

$skip

$group

5.MongoDB關聯關系

MongoDB中的關系表示各個文檔在邏輯上的相互關聯。關系可以通過嵌入式和引用方法建模。 這種關系可以是1:11:NN:1N:N

假設有一種情況:要存儲用戶的地址。一個用戶可以擁有多個地址,這就是1:N關系。

以下是用戶(user)文檔示例的文檔結構

{
   "_id":10999110,
   "name": "Maxsu",
   "contact": "13888990021",
   "dob": "1992-10-11"
}

以下是地址(address)文檔的示例文檔結構 -

{
   "_id":12200,
   "building": "Hainan Building NO.2100",
   "pincode": 571100,
   "city": "Haikou",
   "province": "Hainan"
}
5.1嵌入式關系建模

在嵌入式方法中,我們將地址(address)文檔嵌入到用戶(user)文檔中。

{
   "_id": 21000100,
   "contact": "13800138000",
   "dob": "1991-11-11",
   "name": "Maxsu",
   "address": [
      {
         "building": "Hainan Building NO.2100",
         "pincode": 571100,
         "city": "Haikou",
         "province": "Hainan"
      },
      {
         "building": "Sanya Building NO.2100",
         "pincode": 572200,
         "city": "Sanya",
         "province": "Hainan"
      },
   ]
}

該方法將所有相關數據保存在單個文檔中,這使得檢索和維護更容易。可以使用單個查詢來在整個文檔檢索,例如 -

> db.users.findOne({"name":"Maxsu"},{"address":1, "name":1})

請注意,在上述查詢中,dbusers分別是數據庫和集合。缺點是如果嵌入式文檔的大小如果不斷增長,可能會影響讀/寫性能。

5.2建模參考關系

這是設計規范化關系的方法。 在這種方法中,用戶和地址文件將分別維護,但用戶文檔將包含一個將引用地址文檔的id字段的字段。

{
   "_id":ObjectId("52ffc33321332111sdfaf"),
   "contact": "13800138000",
   "dob": "1991-11-11",
   "name": "Maxsu",
   "address_ids": [
      ObjectId("123123"),
      ObjectId("123412")
   ]
}

如上所示,用戶文檔包含對應地址的ObjectId的數組字段address_ids。 使用這些ObjectIds,我們可以從那里查詢地址文件并獲取地址詳細信息。 使用這種方法,需要兩個查詢:首先從用戶文檔獲取address_ids字段,然后從地址集中獲取這些地址。

>var result = db.users.findOne({"name":"Maxsu"},{"address_ids":1})
>var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})

6.MongoDB-update多層嵌套數組解決辦法

{ "_id" : 1
  "user_id": 1,
  "message" : "Yes"
  "translations" : [
    { 
      "destination" : "fr",
      "text": "Oui"
    },
    { 
      "destination" : "bf",
      "text": "uid"
    },
  ]
}

如果是一層數組,可以用如下語句更新數組里指定key的value

db.getCollection('message').update({
    '_id': 1,
    'translations.destination': 'fr'   
},
{
    '$set': {
        "translations.$.text": "asd"
    }
}

如果再加一層嵌套

{ "_id" : 1
  "user_id": 1,
  "message" : "Yes"
  "translations" : [
    { 
      "destination" : "fr",
      "text": "Oui",
      "rating" : [
        { "user_id" : 1,
          "rating" : 1
        },
        { "user_id" : 2,
          "rating" : 1
        }
      ]
    }
  ]
}

使用這個更新語句就會報錯了

db.getCollection('message').update({
    '_id': 1,
    'translations.destination': 'fr',
    'translations.rating.user_id':'1'
},
{
    '$set': {
        "translations.$.rating.$.rating": 5
    }
}

目前mongodb不支持多個$占位符,推薦的方法的修改數據結構,把數據格式改成這樣,把數組元素改成key-value形式

{ "_id" : 1
  "user_id": 1,
  "message" : "Yes"
  "translations" : {
    "fr": { 
      "destination" : "fr",
      "text": "Oui",
      "rating" : [
        { "user_id" : 1,
          "rating" : 1
        },
        { "user_id" : 2,
          "rating" : 1
        }
      }
    },
    "en": {...}
  ]
}

執行如下更新語句

db.getCollection('message').update({
    '_id': 1,
    'translations.destination': 'fr',
    'translations.rating.user_id':'1'
},
{
    '$set': {
       translations.fr.rating.$.rating”: 5
    }
}

ps:只能使用一個$占位符的問題在社區里已經提了很久了,一直沒有得到解決,希望下個版本可以解決。

補充:MongoDB與Python的交互

1.安裝

pip3 install pymongo

2.使用

演示代碼:

import pymongo
from pymongo import  MongoClient
from bson.objectid import ObjectId

#1.建立連接
#創建MongoClient的對象
#方式一
#特點:可以連接默認的主機和端口號
#client = MongoClient()
#方式二
#明確指明主機和端口號
#client = MongoClient('localhost',27017)
#client = MongoClient(host='localhost',port=27017)
#方式三
#使用MongoDB URI的
client = MongoClient('mongodb://localhost:27017')

#2.獲取數據庫
#MongoDB的一個實例可以支持多個獨立的數據庫
#可以通過MongoClient的對象的屬性來訪問數據庫
#方式一
db = client.test
print(db)
#方式二
#db = client['test']

#3.獲取集合
#集合是存儲在MongoDb中的一組文檔,可以類似于MySQl中的表
#方式一
collection = db.stuents
#方式二
#collection = db['students']
"""
注意:
MongoDB中關于數據庫和集合的創建都是懶創建
以上的操作在MongoDB的服務端沒有做任何操作
當第一個文檔被插入集合的時候才會創建數據庫和集合
"""

#4.文檔
#在pymongo中使用字典來表示文檔
student1 = {
    'id':'20180101',
    'name':'jack',
    'age':20,
    'gender':'male'
}

#5.插入文檔
#5.1insert()
#插入單條數據
#注意:MongoDb會自動生成一個ObjectId,insert函數的返回值為objectid
result = collection.insert(student1)
print(result)

#插入多條數據
student2 = {
    'id':'20180530',
    'name':'tom',
    'age':30,
    'gender':'male'
}
student3 = {
    'id':'20180101',
    'name':'bob',
    'age':18,
    'gender':'male'
}
#result = collection.insert([student2,student3])

#5.2insert_one()
student4 = {
    'id':'20180101',
    'name':'rose',
    'age':25,
    'gender':'female'
}
#result = collection.insert_one(student4)
#print(result)           #InsertOneResult
#print(result.inserted_id)

#5.3insert_many()
#result = collection.insert_many([student2,student3]);
#print(result)           #InsertOneResult
#print(result.inserted_ids)

#6.查詢文檔
#6.1
#find_one()
result = collection.find_one({'name':'jack'})
print(type(result))    #<class 'dict'>
print(result)

#6.2通過objectid查詢
#5b3ed21f2e1016e9ad2dc7b7
#注意:導入模塊
result = collection.find_one({'_id':ObjectId('5b3ed21f2e1016e9ad2dc7b7')})
print(result)
#查詢不到結果則返回None

#6.3find()
#需求:查詢年齡為20的數據
results = collection.find({'age':20})
print(results)
#Cursor相當于是一個生成器,只能通過遍歷的方式獲取其中的數據
for r in results:
    print(r)

#需求:查詢年齡大于20的數據
results = collection.find({'age':{'$gt':20}})

#6.4其他用法
#a.count()
#統計所有數據的條數
count1 = collection.find().count()
#統計制定條件的數據條數
count1 = collection.find({'age':20}).count()

#b.sort()
r0 = collection.find().sort('name',pymongo.ASCENDING)

#c.limit(),skip()
r0 = collection.find().sort('name',pymongo.ASCENDING).skip(2)
r0 = collection.find().sort('name',pymongo.ASCENDING).skip(2).limit(5)
#注意事項:在數據庫數量非常龐大的情況下,最好不要使用大的 的偏移量,很可能會導致內存溢出


#7.更新文檔
#7.1update()
conditon = {'name':'jack'}
student = collection.find_one(conditon);
student['age'] = 30
result = collection.update(conditon,student)

#7.2update_one()
conditon = {'name':'jack'}
student = collection.find_one(conditon);
student['age'] = 30
result = collection.update_one(conditon,{'$set':student})
print(result.matched_count,result.modified_count)

#7.2update_many()
#查詢年齡大于20的數據,然后講年齡增加1
conditon = {'age':{'$gt':20}}
result = collection.update_one(conditon,{'$inc':{'age':1}})
print(result.matched_count,result.modified_count)

#8.刪除文檔
#8.1remove()
#將符合條件的所有的數據全部刪除
result = collection.remove({'name':'rose'})

#8.2delete_one()
result = collection.delete_one({'name':'rose'})

#8.3delete_many()
result = collection.delete_many({'name':'rose'})
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容

  • 關于Mongodb的全面總結 MongoDB的內部構造《MongoDB The Definitive Guide》...
    中v中閱讀 31,986評論 2 89
  • 一、MongoDB簡介 概述MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫。旨在為WEB應用提供...
    EndEvent閱讀 1,164評論 1 4
  • 一、MongoDB簡介 概述MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫。旨在為WEB應用提供...
    王梓懿_1fbc閱讀 499評論 0 3
  • 一、MongoDB簡介 概述MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫。旨在為WEB應用提供...
    慕楊_閱讀 564評論 0 4
  • 一、MongoDB簡介 概述MongoDB是一個基于分布式文件存儲的數據庫,由C++語言編寫。旨在為WEB應用提供...
    fly5閱讀 293評論 0 0