數據庫,一個數據倉庫能夠包含多個集合html
集合,相似於關係數據庫中的表。一個集合能夠包含多個文檔。python
capped collection(限制集合):設定空間上線,循環寫入,新數據覆蓋舊數據git
文檔,一個文檔保存着一份數據記錄。github
一個集合下的文檔,不會默認要求它們具備相同的數據模式。也就是說,同一個集合下的多個文檔:1.字段能夠不一樣;2.同名字段的類型能夠不一樣;相反,若是對文檔數據類型有要求,能夠在建立集合時設置validator(例如使用JSON schema)來限制集合下文檔的數據類別。正則表達式
文檔使用BSON來存儲,BSON是JSON的二進制表現形式,所以能夠適應更多的數據類型。
存儲在文檔的數據格式,與JSON相似,以鍵值對的形式存儲。
默認主鍵_id,爲ObjectId類型。redis
使用insert_one()方法sql
book = { 'name': 'computer_science', 'page': 238, } result = db.books.insert_one(book) _id = result.inserted_id # 返回插入項的id,類型爲ObjectId
使用insert_many()方法mongodb
results = db.collection_name.insert_many([document1, document2, ...]) id_list = results.inserted_ids # 返回插入項的id列表,列表元素爲ObjectId
從pymongo3.0版本開始,已經不推薦使用insert()方法插入數據,雖然它能同時知足單條或多條數據的處理需求。
官方建議使用insert_one()和insert_many()來執行替代操做shell
假設預先執行了數據插入:數據庫
db.inventory.insert_many([ {"item": "journal", "qty": 25, "size": {"h": 14, "w": 21, "uom": "cm"}, "status": "A"}, {"item": "notebook", "qty": 50, "size": {"h": 8.5, "w": 11, "uom": "in"}, "status": "A"}, {"item": "paper", "qty": 100, "size": {"h": 8.5, "w": 11, "uom": "in"}, "status": "D"}, {"item": "planner", "qty": 75, "size": {"h": 22.85, "w": 30, "uom": "cm"}, "status": "D"}, {"item": "postcard", "qty": 45, "size": {"h": 10, "w": 15.25, "uom": "cm"}, "status": "A"}])
使用find()方法執行查詢,返回遊標cursor
查詢全部記錄時,find()內的filter參數爲空
cursor = db.inventory.find({})
上述查詢,相似於關係數據庫SQL語句:
SELECT * FROM inventory
cursor = db.inventory.find({"status": "D"})
上述查詢語句,相似於關係數據庫SQL語句:
SELECT * FROM inventory WHERE status = "D"
cursor = db.inventory.find({"status": {"$in": ["A", "D"]}})
上述查詢語句,相似於關係數據庫SQL語句:
SELECT * FROM inventory WHERE status in ("A", "D")
cursor = db.inventory.find({"status": "A", "qty": {"$lt": 30}})
上述查詢語句,相似於關係數據庫SQL語句:
SELECT * FROM inventory WHERE status = "A" AND qty < 30
cursor = db.inventory.find({"$or": [{"status": "A"}, {"qty": {"$lt": 30}}]})
上述查詢語句,相似於關係數據庫SQL語句:
SELECT * FROM inventory WHERE status = "A" OR qty < 30
cursor = db.inventory.find({ "status": "A", "$or": [{"qty": {"$lt": 30}}, {"item": {"$regex": "^p"}}]})
上述查詢語句,相似於關係數據庫SQL語句:
SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")
查詢操做符定義了查詢條件,如:大於、等於、小於等,如下是整理的查詢操做符及說明:
比較操做符 | 說明 |
---|---|
$eq | 等於 |
$gt | 大於 |
$gte | 大於等於 |
$in | 包含 |
$lt | 小於 |
$lte | 小於等於 |
$ne | 不等於 |
$nin | 不包含於 |
邏輯操做符 | 說明 |
---|---|
$and | 與 |
$not | 非 |
$nor | 或非 |
$or | 或 |
元素操做符 | 說明 |
---|---|
$exists | 指定field存在 |
$type | 指定field的type |
查看field的type類型說明
其餘操做符說明請見:Query and Projection Operators
對於文檔中存在的嵌套結構的查詢,能夠對文檔中的嵌套結構進行 匹配查詢 ,也能夠對嵌套內容中的某個字段進行 嵌套字段查詢
假設文檔數據以下:
from bson.son import SON db.inventory.insert_many([ {"item": "journal", # 物品名稱 "qty": 25, # 數量 "size": SON([("h", 14), ("w", 21), ("uom", "cm")]), # 嵌套結構(高度,寬度,度量單位) "status": "A"}, # 狀態 {"item": "notebook", "qty": 50, "size": SON([("h", 8.5), ("w", 11), ("uom", "in")]), "status": "A"}, {"item": "paper", "qty": 100, "size": SON([("h", 8.5), ("w", 11), ("uom", "in")]), "status": "D"}, {"item": "planner", "qty": 75, "size": SON([("h", 22.85), ("w", 30), ("uom", "cm")]), "status": "D"}, {"item": "postcard", "qty": 45, "size": SON([("h", 10), ("w", 15.25), ("uom", "cm")]), "status": "A"}])
from bson.son import SON cursor = db.inventory.find( {"size": SON([("h", 14), ("w", 21), ("uom", "cm")])}) # 上述查詢語句中的filter條件,須要徹底匹配嵌套文檔中的內容,不然沒法查詢到相關記錄。
mongo使用點表示法指定文檔中的嵌套字段:"field.nested_field"
# 查詢嵌套字段uom的值爲cm的記錄 cursor = db.inventory.find({"size.uom": "cm"}) # 使用操做符查詢高度大於10的記錄 cursor = db.inventory.find({"size.h": {"$gt": 10}}) # 多個字段的複合查詢 cursor = db.inventory.find( {"size.h": {"$lt": 15}, "size.uom": "in", "status": "D"})
假設向collection中插入以下數據
db.inventory.insert_many([ {"item": "journal", "qty": 25, "tags": ["blank", "red"], "dim_cm": [14, 21]}, {"item": "notebook", "qty": 50, "tags": ["red", "blank"], "dim_cm": [14, 21]}, {"item": "paper", "qty": 100, "tags": ["red", "blank", "plain"], "dim_cm": [14, 21]}, {"item": "planner", "qty": 75, "tags": ["blank", "red"], "dim_cm": [22.85, 30]}, {"item": "postcard", "qty": 45, "tags": ["blue"], "dim_cm": [10, 15.25]}])
與2.2.3中嵌套文檔查詢相似,能夠對整個數組進行 匹配查詢 , 也能夠對數組中的某個元素進行查詢
cursor = db.inventory.find({"tags": ["red", "blank"]})
# 全部tags中包含red元素的數組都會被查詢到 cursor = db.inventory.find({"tags": "red"}) # 對數組中某個元素進行條件查詢 # dim_cm中任意一個元素大於25的記錄查詢 cursor = db.inventory.find({"dim_cm": {"$gt": 25}})
查詢dim_cm中的某個元素能同時知足大於15小於20的查詢條件的記錄
或者 dim_cm中的一個元素大於15,而且存在另外一個元素小於20的記錄
# 這種查詢是不限制單個數組元素的,多個數組元素分別知足查詢條件亦可 cursor = db.inventory.find({"dim_cm": {"$gt": 15, "$lt": 20}})
若要指定數組中某一個元素知足多個查詢條件,須要使用 __$elemMatch__操做符來進行查詢
# 查詢數組中存在某一個元素同時知足大於22而且小於30 cursor = db.inventory.find( {"dim_cm": {"$elemMatch": {"$gt": 22, "$lt": 30}}})
查詢dim_cm的第一個元素大於25的記錄
cursor = db.inventory.find({"dim_cm.1": {"$gt": 25}})
查詢數組長度是否符合查詢條件,須要使用 $size 操做符
查詢全部tags長度等於3的的記錄
cursor = db.inventory.find({"tags": {"$size": 3}})
假設向collection中插入以下數據:
from bson.son import SON db.inventory.insert_many([ {"item": "journal", "instock": [ SON([("warehouse", "A"), ("qty", 5)]), SON([("warehouse", "C"), ("qty", 15)])]}, {"item": "notebook", "instock": [ SON([("warehouse", "C"), ("qty", 5)])]}, {"item": "paper", "instock": [ SON([("warehouse", "A"), ("qty", 60)]), SON([("warehouse", "B"), ("qty", 15)])]}, {"item": "planner", "instock": [ SON([("warehouse", "A"), ("qty", 40)]), SON([("warehouse", "B"), ("qty", 5)])]}, {"item": "postcard", "instock": [ SON([("warehouse", "B"), ("qty", 15)]), SON([("warehouse", "C"), ("qty", 35)])]}])
能夠看到instock數組內部每個元素都是一個嵌套文檔。對這類數據的查詢方法,是2.2.3嵌套文檔查詢和2.2.4數組類型查詢的結合。
# 匹配查詢對於嵌套文檔內的field順序有要求, # 查詢結果只展現與查詢條件中field排列順序相同的記錄。 cursor = db.inventory.find( {"instock": SON([("warehouse", "A"), ("qty", 5)])})
# 查詢全部文檔中,instock數組中至少有一個元素的qty值大於20的記錄 cursor = db.inventory.find({'instock.qty': {"$lte": 20}})
# 查詢全部文檔中,instock數組的第0個嵌套文檔元素中,qty的值小於等於20的全部記錄 cursor = db.inventory.find({'instock.0.qty': {"$lte": 20}})
# 數組內的某個文檔同時知足qty=5而且warehouse值爲A的查詢條件 cursor = db.inventory.find( {"instock": {"$elemMatch": {"qty": 5, "warehouse": "A"}}}) # 數組內的某個文檔的qty值大於10而且小於等於20 cursor = db.inventory.find( {"instock": {"$elemMatch": {"qty": {"$gt": 10, "$lte": 20}}}})
MonogDB返回的查詢結果,默認包含文檔中全部的field,使用者能夠經過讓mongo返回指定的field,來限制返回內容的數量。
查詢表達式以下:
# 返回指定的field cursor = db.inventory.find( {"status": "A"}, {"item": 1, "status": 1, "size.uom": 1}) # 不返回指定的field cursor = db.inventory.find({"status": "A"}, {"size.uom": 0, "status": 0}) # 對於數組形式的field,指定只返回最後一個元素(使用$slice操做符) cursor = db.inventory.find( {"status": "A"}, {"instock": {"$slice": -1}})
cursor = db.inventory.find({"item": None})
# type值爲10時表示的是null類型 cursor = db.inventory.find({"item": {"$type": 10}})
# 查詢全部文檔中,沒有item字段的記錄 cursor = db.inventory.find({"item": {"$exists": False}})
MongoDB提供了一系列的操做符來幫助完成文檔數據更新,具體說明可查看連接:https://docs.mongodb.com/manu...
使用pymongo的update_one方法
db.inventory.update_one( {"item": "paper"}, # filter篩選條件, 只更新符合該條件的第一條數據 {"$set": {"size.uom": "cm", "status": "P"}, "$currentDate": {"lastModified": True}}) # 數據更新表達式,使用$set操做符來更新數據
使用pymongo的update_many()方法
db.inventory.update_many( {"qty": {"$lt": 50}}, # filter篩選條件,更新符合該條件的全部數據 {"$set": {"size.uom": "in", "status": "P"}, "$currentDate": {"lastModified": True}})# 數據更新表達式,一樣使用$set操做符來更新數據
使用pymongo的replace()方法
注:替換方法只替換除_id之外的其餘字段
db.inventory.replace_one( {"item": "paper"}, # filter篩選條件,替換符合該條件的第一條數據 {"item": "paper", # 替換後的文檔數據 "instock": [ {"warehouse": "A", "qty": 60}, {"warehouse": "B", "qty": 40}]})
不管是update_one()方法仍是update_many()方法,亦或是replace_one()方法,都包含upsert:bool 選項,當upsert爲True時,這些方法將具有在filter未篩選到文檔時,執行文檔插入的能力。
pymongo提供了delete_one()和delete_many()兩種方法執行刪除操做。其中,delete_one()方法一次執行一條文檔的刪除任務,delete_manyI()可執行多條文檔刪除任務。
db.inventory.delete_one({"status": "D"}) # 刪除符合status值爲D的第一條數據 db.inventory.delete_many({"status": "A"}) # 刪除符合status值爲A的全部數據
值得一提的是,刪除操做並不會改變collection的索引設置,即使刪除了這個collection下的全部文檔。
pymongo提供了批量寫入方法:bulk_write(),相似於redis中的pipe_line,它能夠將多個寫入操做做爲一個list參數傳入,而後一塊兒執行。它支持insert、update、replace、delete的多種方法,如下是官方文檔提供的示例:
try { db.characters.bulkWrite( [ { insertOne : { "document" : { "_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4 } } }, { insertOne : { "document" : { "_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3 } } }, { updateOne : { "filter" : { "char" : "Eldon" }, "update" : { $set : { "status" : "Critical Injury" } } } }, { deleteOne : { "filter" : { "char" : "Brisbane"} } }, { replaceOne : { "filter" : { "char" : "Meldane" }, "replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 } } } ] ); } catch (e) { print(e); }
1)同一個集合下,不一樣文檔的字段能夠不一致;同一個集合下,不一樣文檔的相同字段,類型能夠不一致;
2)能夠經過對一個文檔的字段進行增刪改操做,或是變動字段類型,來改變文檔的結構。
1) __嵌入式文檔結構__,即在一個文檔內,能夠嵌套子文檔內容,實現邏輯相關的數據結構嵌套組合。嵌套式文檔結構以下:
# 用戶信息表 { _id: ObjectId_1, username: "youjia", sex: "man", age: 29, contact: { # 嵌入式文檔 phone: 18195181469, email: "jia.you@shunwang.com", } }
2) __引用式__,不一樣類型數據使用id引用進行關聯,上例可變爲:
# 用戶表 { _id: ObjectId_1, username: "youjia", sex: "man", age: 29, } # 聯繫信息表(原嵌入式文檔) { _id: ObjectId_2, user_id: ObjectId_1, # 對應用戶表_id phone: 18195181469, email: "jia.you@shunwang.com", }
1) 對一個文檔的寫入操做是原子性的,即便這個寫入操做包含了對嵌套文檔的數據寫入。
2) 因爲對嵌套文檔的寫入動做是原子性的,所以嵌套式的文檔結構設計,更加促進了寫入操做原子化,提升了寫入效率和數據一致性。
3) 當執行相似updateMany等操做時,雖然只執行了一條指令,但其內部執行過程實際上包含了對多個文檔的原子操做。所以這類批量執行指令是非原子性的。
4) 因爲對多個文檔的批量指令執行是非原子性的,所以在對多個文檔進行寫入操做時,寫入任務可能與其餘批量寫入任務交叉。
5) 從MongoDB4.0開始,爲了保證多文檔寫入/讀取數據的一致性,加入了多表操做事務
6) 多表操做事務相比單表操做,會形成大的多的性能消耗,所以官方仍然認爲,在多數狀況下 __嵌入式文檔結構是更好的選擇__。
官方提供了多種數據模型校驗的方法,包括:1. JSON Schema校驗,2. 查詢表達式校驗。官方推薦使用前者。
一個典型的JSON Schema語法示例:
db.createCollection("students", { validator: { $jsonSchema: { bsonType: "object", required: [ "name", "year", "major", "gpa" ], properties: { name: { bsonType: "string", description: "must be a string and is required" }, gender: { bsonType: "string", description: "must be a string and is not required" }, year: { bsonType: "int", minimum: 2017, maximum: 3017, exclusiveMaximum: false, description: "must be an integer in [ 2017, 3017 ] and is required" }, major: { enum: [ "Math", "English", "Computer Science", "History", null ], description: "can only be one of the enum values and is required" }, gpa: { bsonType: [ "double" ], minimum: 0, description: "must be a double and is required" } } } } })
關於JSON Schema的更詳細信息,可參照網上教程:https://spacetelescope.github...
3.1.1中介紹了模型的兩種設計結構:__嵌入式文檔結構__,和 引用式 文檔結構。在設計數據模型時,要考慮根據不一樣狀況選擇適合的文檔結構進行設計。
如下狀況下適合使用嵌入式文檔結構:
1) 兩類數據是一對一而且具備包含關係。例如:用戶我的信息-用戶聯繫信息
2) 兩類數據時一對多關係,可是在應用過程當中一般兩類數據須要聯合查詢使用,使用「一」時一般會查詢「多」。
嵌套式文檔結構的優勢:
注意:MongoDB默認限制單個文檔大小最大爲16MB,所以單個文檔大小不能無限擴大。Mongo提供了其餘大致量數據的存儲方式:GridFS
如下狀況適合使用引用式文檔結構:
1) 當採用嵌套式文檔結構時,被嵌套的數據會有大量重複,而且大量重複數據形成影響大於嵌套文檔的優點時,選擇引用式文檔結構時更好的選擇。
2) 要設計「多對多」關係時。
3) 爲大型分層數據集建模時。
引用式文檔的優勢:
對於查詢需求較多的文檔,能夠經過在適合的字段創建索引來提升查詢效率。可是對文檔創建過多的索引,會影響寫入效率,增長磁盤和內存的空間使用率。
對文檔內單個字段創建索引,稱爲單字段索引。適合對文檔內單個字段有頻繁查詢請求的場景。
db.person.createIndex( {id_no: 1} )
{id_no: 1}表明升序索引,{id_no: -1}表明降序索引,在單字段索引類型下,升序與降序沒有區別。
複合索引是對多個字段聯合建立一個索引。適合對文檔內某些字段有頻繁查詢請求,以及查詢與排序請求並存的業務場景。
db.person.createIndex( {age: 1, name: 1} )
建立符合索引時,field的順序是有關係的。索引將按照第一個field進行升序/降序排列,在此基礎上,再對第二個field進行升序/降序排列,以此類推。
當索引的字段爲數組時,建立出的索引稱爲多key索引,多key索引會爲數組的每一個元素創建一條索引,好比person表加入一個habbit字段(數組)用於描述興趣愛好,須要查詢有相同興趣愛好的人就能夠利用habbit字段的多key索引。
//文檔格式 {"name" : "jack", "age" : 19, habbit: ["football, runnning"]} // 自動建立多key索引 db.person.createIndex( {habbit: 1} ) db.person.find( {habbit: "football"} )
保證索引對應的字段不會出現相同的值,文檔主鍵_id的索引,就是惟一索引。
能夠針對某個時間字段,指定文檔的過時時間(通過指定時間後過時 或 在某個時間點過時)
只針對符合某個特定條件的文檔創建索引,好比某字段值大於5,或者某字段值符合某正則表達式,才創建索引,注意:3.2版本才支持該特性.
只針對存在索引字段的文檔創建索引,可看作是部分索引的一種特殊狀況
單字段索引
複合索引
# 建立索引 db.collection.create_index( { "x" : 1, "y" : -1 } ) # 支持下列查詢/排序 db.collection.find().sort( { "x": 1, "y": -1 } ) db.collection.find().sort( { "x": -1, "y": 1 } ) # 對下列查詢/排序,索引不生效 db.collection.find().sort( { "x": 1, "y": 1 } )
# 建立索引 db.collection.create_index( { "x" : 1, "y" : 1 , "z": 1} ) # 支持對下列查詢/排序場景 db.collection.find().sort({"x": 1}) db.collection.find().sort({"x": 1, "y": 1}) db.collection.find().sort({"x": 1, "z": 1}) # 效率較低 db.collection.find().sort({"x": 1, "y": 1, "z": 1}) # 不支持下列查詢/排序場景 db.collection.find().sort({"y": 1}) db.collection.find().sort({"z": 1}) db.collection.find().sort({"y": 1, "z": 1})
多key索引
# 建立複合索引 db.collection.create_index( { "x" : 1, "y" : 1 } ) # 索引支持的文檔 { "x" : 1, "y" : [1,2,3] } {"x": [1,2,3], "y": 1} # 不支持的狀況 {"x": [1,2,3], "y": [1,2,3]}
# 文檔結構 { "x": "test", "y": 1, "z": [ {"a": 1, "b": "test"}, {"a": 2, "b": "some"}, ... ], } # 創建嵌入式文檔的多key索引 db.collection_name.create_index({"z.a": 1, "z.b": -1})