摘要:舉個(gè)栗子你有一個(gè)箱子,里面有一個(gè)兒子級(jí)別和孫子級(jí)別的箱子共層現(xiàn)在你把孫子級(jí)別的箱子多帶帶拿出來(lái),把整個(gè)箱子替換掉就是這種思想。。。自己體會(huì)吧這種語(yǔ)法,好像列表的切片賦值。。官方建議我們用它的好處是把和由兩個(gè)函數(shù)調(diào)用變?yōu)閭€(gè)參數(shù)傳進(jìn)去了。
閱讀須知
由于是對(duì)比書(shū)寫(xiě): M: 代表 Mongo原生語(yǔ)法 P: 代表 PyMongo書(shū)寫(xiě)方法 后面提到:”同上“ 字眼: 意思就是 Mongo 和 PyMongo 語(yǔ)句是一模一樣的, 一個(gè)字都不差,復(fù)制上去,可以直接運(yùn)行 (也許你很好奇,為什么 一個(gè)是Python語(yǔ)言里的PyMongo,一個(gè)是Mongo) 他們的語(yǔ)句為什么可以做到一模一樣 ?? 答:因?yàn)?Mongo和Python都可以 給變量賦值, PyMongo的語(yǔ)法設(shè)計(jì)也是模仿Mongo的。 所以:我巧妙的 把二者的變量設(shè)為同一個(gè),函數(shù)90%都一致, 所以整條語(yǔ)句就一模一樣了! 主要語(yǔ)法區(qū)別: 1. 函數(shù)命名 Mongo 方法函數(shù)大都以 駝峰命名 PyMongo方法函數(shù)大都以 _ 下劃線分割命名 2. 函數(shù)參數(shù) Mongo : 基本都是 {} + [] 各組組合格式 PyMongo:同上, 但{}的 key需要使用字符串格式, 有些情況,還需要使用命名參數(shù)代替 {} 3. 空值 與 Bool Mongo: null true false PyMongo: None True False前置安裝配置環(huán)境
客戶(hù)端連接:
pip install pymongo import pymongo M: Mongo P: cursor = pymongo.MongoClient("ip",port=27017)
選擇數(shù)據(jù)庫(kù):
M: use test P: db = cursor["test"] # 記住這個(gè)db, 下面復(fù)用這個(gè)參數(shù)
選擇集合: (記住table變量名,下面就直接用他們了) 注意,注意,注意
M: table = db.zhang P: table = db["zhang"] 注:選擇庫(kù),選擇集合的時(shí)候 注意事項(xiàng): Mongo中: xx.xx 用 . 的語(yǔ)法 PyMongo中:也可以 用 xx.xx 這樣, 但是這樣用在PyCharm中沒(méi)有語(yǔ)法提示 所以提倡 xx["xx"] 用索引的方式使用
Mongo 與 PyMongo 返回結(jié)果的游標(biāo)比較
Mongo中: 大多數(shù)查詢(xún)等結(jié)果返回都是游標(biāo)對(duì)象 如果不對(duì)游標(biāo)遍歷,那么Mongo的游標(biāo)會(huì)默認(rèn)為你取出 前 20 個(gè) 值 當(dāng)然,你也可以索引取值 關(guān)閉操作: .close() PyMongo中: 同樣,大多數(shù)查詢(xún)等結(jié)果返回都是游標(biāo)對(duì)象(如果你學(xué)過(guò)ORM,可以理解游標(biāo)就像 ORM的查詢(xún)集) 所以必須通過(guò) list() 或 遍歷 或 索引 等操作才能真正取出值 關(guān)閉操作: .close() 或者 用 Python 的 with 上下文協(xié)議增
save()
M: table.save({}) # 估計(jì)要廢棄了 P: 將要被廢棄 用insert_one代替它
insert()
M: table.insert() # 包括上面兩種,可以一個(gè) {},可以多個(gè) [{},{}] P: PyMongo源碼明確說(shuō)明,insert()語(yǔ)法將被廢棄,請(qǐng)用 insert_one({}) 和 insert_many([])代替
insert_one() 和 insert_many()
M: table.insertOne( {} ) # 駝峰 table.insertMany([ {},{} ]) # 駝峰 P: table.insert_one( {} ) # 下劃線 table.insert_many([ {},{} ]) # 下劃線刪
remove()
參數(shù)1:刪除查詢(xún)條件 參數(shù)2:刪除選項(xiàng) M: table.remove({"name":"zhangsan"}, {"justOne": true}) # 我更喜歡用delete的 P: PyMongo中,此方法將被廢棄。 將會(huì)被 delete_one() 和 delete_many() 代替
deleteOne() # 只刪除一條
M: table.deleteOne({"name": "lin3"}) P: table.delete_one({"name": "lin3"}) #
deleteMany() # 刪除多條
M: table.deleteMany({"name": "lin3"}) P: table.delete_many({"name": "lin3"}) 注意: 不知道這兩個(gè)函數(shù)是否讓你想起了前面講的 insertOne 和 insertMany,他們看起來(lái)很像,語(yǔ)法不同: insertMany([]) # 參數(shù)需要用 [] 包起來(lái) deleteMany({}) # 參數(shù)不需要 注意2: table.deleteMany({}) # 空 {}, 代表刪除所有文檔 (慎行,慎行,慎行)
刪除整個(gè)集合:
table.drop() # 刪除集合(連同 所有文檔, 連同 索引,全部刪除)改
""" 文檔修改, 注意: _id 不可修改 """
三種更新方法:
1. update(將要廢棄,可跳過(guò),直接看2,3點(diǎn)的方法) update({查詢(xún)條件}, {更新操作符} , {更新選項(xiàng)}) M: table.update({"name": {"$regex":"li"}},{"$set":{"name":"lin2"}}, {multi: true}) P: table.update({"name": {"$regex": "li"}}, {"$set": {"name": "lin3"}},multi=True) 注意1: 第三個(gè)參數(shù) multi如果不設(shè)置,默認(rèn)只更新一條文檔,設(shè)置為 true ,就會(huì)更新多條文檔 注意2: Mongo寫(xiě)法: {multi: true} # Mongo 和往常一樣,采用json格式, true小寫(xiě) Python寫(xiě)法: multi = True # python是采用命名參數(shù)來(lái)傳遞, True大寫(xiě) 2. updateOne(更新一條) M: updateOne( {查詢(xún)條件}, {更新操作符} ) P: update_one 3. updateMany(更新多條) M: updateMany( {查詢(xún)條件}, {更新操作符} ) 其實(shí)參數(shù)是一模一樣的,只不過(guò)方法名區(qū)分 P: update_many 注: 這三個(gè)方法的參數(shù) 是基本一模一樣的 所以下面講具體 {查詢(xún)條件}, {更新操作符} 時(shí) 就統(tǒng)一用 update()來(lái)寫(xiě)了
普通更新操作符:
$set(更新)
# 注:規(guī)則就是:"有則改之, 無(wú)則添加" M: table.update({"5":5},{"$set": {"lin": [5,6,7,8]} }) P: 同上 微擴(kuò)展(關(guān)于內(nèi)嵌數(shù)組): table.update({"5":5},{"$set": {"lin.0": "呵呵" }) # lin.0代表數(shù)組的第一個(gè)元素 當(dāng)數(shù)組的索引越界,這個(gè)時(shí)候就視為數(shù)組的添加操作。 eg: 假定我們給 lin.10 一個(gè)值,那么 中間空出的那么多索引,會(huì)自動(dòng)填充 null
$unset(刪除)
# 注:刪除的鍵對(duì)應(yīng)的value可以隨便寫(xiě),寫(xiě)啥都會(huì)刪除, 寫(xiě) "" 只是為了語(yǔ)義明確(規(guī)范) M: table.update({"6":6}, {"$unset": {"6":""}}) # 把此條記錄的 "6" 字段刪除 P: 同上 微擴(kuò)展(關(guān)于嵌套數(shù)組): table.update({"5":5}, {"$unset": {"lin.0":""}}) # lin.0同樣代表數(shù)組第一個(gè)元素 注:數(shù)組的刪除 并不是真正的刪除, 而是把值 用 null 替換
$rename(改名,替換)
M: table.update({"name":"lin"}, {"$rename":{"name":"nick"}}) # name變成了nick P: 同上 微擴(kuò)展(文檔嵌套): 如果文檔是嵌套的 eg: { a: {b:c} } M: table.update({"lin":"lin"}, {"$rename": {"a.b":"d"}}) P: 同上 結(jié)果 => {"a" : { }, "d" : "c" } 解析: b 屬于 子文檔 a.b 表示 通過(guò)父文檔的a 來(lái)取出 子文檔的b 如果整體a.b被 rename為 d,那么 d會(huì)被安排到父文檔的層級(jí)里,而a設(shè)為空。 舉個(gè)栗子: 你有一個(gè)箱子,里面 有一個(gè) 兒子級(jí)別 和 孫子級(jí)別 的箱子 (共3層) 現(xiàn)在你把 孫子級(jí)別的箱子 多帶帶拿出來(lái), 把整個(gè)箱子替換掉 就是這種思想。。。自己體會(huì)吧 (這種語(yǔ)法,好像Python列表的切片賦值。。形容可能不太恰當(dāng))
$inc:
{$inc: { "age": -2}} # 減少兩歲,正數(shù)表示加法,負(fù)數(shù)表示減法,簡(jiǎn)單,不舉例了 特例:如果字段不存在,那么,此字段會(huì)被添加, 并且值就是你設(shè)定的值(0+n=n)
$mul:
{$mul: { "age": 0.5}} # 年齡除以2,整數(shù)表示乘法,小數(shù)表示除法,簡(jiǎn)單,不舉例了 特例:如果字段不存在,那么,此字段會(huì)被添加, 并且值為0 (0*n=0)
$min
{$min: { "age": 30}} # 30比原有值?。壕吞鎿Q, 30比原有值大,則不做任何操作
$max
{$max: { "age": 30}} # 30比原有值大:就替換, 30比原有值小,則不做任何操作 特例:min和max特例相同,即如果字段不存在,那么,此字段會(huì)被添加, 并且值就是你設(shè)定的值
數(shù)組更新操作符:
""" 單數(shù)組: xx 內(nèi)嵌數(shù)組: xx.索引 """
$addToSet(有序,無(wú)重復(fù),尾部添加)
原始數(shù)據(jù): {"1":1} M: table.update({"1":1}, {"$addToSet":{"lin":[7,8]}}) P: 同上 結(jié)果 => {"1": 1,"lin": [ [7, 8 ] ]} # [7,8] 整體插入進(jìn)來(lái), 特別注意這是二級(jí)列表
$each ( 給[7,8]加個(gè) $each,注意看結(jié)果變化 )
M: table.update({"1": 1}, {"$addToSet": {"lin": {"$each":[7, 8]} }}) P: 同上 結(jié)果 => {"1": 1, "lin": [7,8]} # 7,8多帶帶插入進(jìn)來(lái),參考python的 * 解構(gòu)
$push(數(shù)據(jù)添加, 比$addToSet強(qiáng)大,可任意位置,可重復(fù))
""" 補(bǔ)充說(shuō)明: $addToSet:添加數(shù)據(jù)有重復(fù),會(huì)自動(dòng)去重 $push :添加數(shù)據(jù)有重復(fù),不會(huì)去重,而是直接追加 """ 原始數(shù)據(jù): {"1":1} M: table.update( { "1": 1 }, { "$push": { "lin": { "$each": [ {"a": 5, "b": 8 }, { "a": 6, "b": 7 }, {"a": 7, "b": 6 } ], "$sort": { "a": -1 }, "$position": 0, "$slice": 2 }}}) # 這里為了清晰點(diǎn),我就把所有括號(hào)折疊起來(lái)了 P: 同上 結(jié)果 => {"1" : 1, "lin" : [ { "a" : 7, "b" : 6 }, { "a" : 6, "b" : 7 } ] } 終極解析: 1. 添加數(shù)組: 先走 $sort => 根據(jù)a 逆序排列 2. 再走 $position, 0表示:索引定位從0開(kāi)始 3. 再走 $slice, 2表示: 取2個(gè) 4. 最后走 $each,把數(shù)組元素逐個(gè)放進(jìn)另一個(gè)數(shù)組,說(shuō)過(guò)的,相當(dāng)于python的 * 解構(gòu)操作,
$pop(只能 刪除 頭或尾 元素)
M: table.update({"a": a}, {"$pop": {"lin": 1}}) # 刪除最后一個(gè) P: 同上 注1:$pop參數(shù), 1代表最后一個(gè), -1代表第一個(gè)。 這個(gè)是值得注意一下的,容易記反 注2:如果全部刪沒(méi)了,那么會(huì)剩下空[], 而不是徹底刪除字段
$pull (刪除 任何位置 的 指定的元素)
M: table.update({"1": 1},{"$pull":{ "lin":[7,8]}}) # 刪除數(shù)組中[7,8]這個(gè)內(nèi)嵌數(shù)組 P: 同上
$pullAll(基本和 $pull 一致)
M: table.update({"1": 1},{"$pullAll":{ "lin":[ [7,8] ]}}) # 同$pull,但多了個(gè) [] P: 同上 注: $pull 和 $pullAll 針對(duì)于 內(nèi)嵌文檔 和 內(nèi)嵌數(shù)組 有細(xì)小差別, 差別如下: 內(nèi)嵌數(shù)組: $pull 和 $pullAll 都嚴(yán)格要求內(nèi)嵌數(shù)組的 排列順序,順序不一致,則不返回 內(nèi)嵌文檔: $pullAll : 嚴(yán)格要求內(nèi)嵌文檔的順序, 順序不一致,則 不返回 $pull : 不要求內(nèi)嵌文檔的循序, 順序不一致,一樣可以返回查
""" 第一個(gè)參數(shù)的條件是 篩選出 數(shù)據(jù)的記錄(文檔) 第二個(gè)參數(shù)的條件是 篩選出 數(shù)據(jù)的記錄中的 屬性(字段),不配置 就是 默認(rèn) 取出所有字段 find({查詢(xún)條件}, {投影設(shè)置}) """
投影解釋
哪個(gè)字段 設(shè)置為 0, 此字段就不會(huì)被投影, 而其他字段全部被投影 哪個(gè)字段 設(shè)置為 1, 此字段就會(huì)被多帶帶投影, 其他字段不投影 {"name": 0, "age": 0} # 除了 name 和 age ,其他字段 都 投影 {"name": 1, "age": 1} # 只投影 name 和 age, 其他字段 不 投影,(_id除外) 注意:所有字段必須滿(mǎn)足如下要求: 一: 你可以不設(shè)置,默認(rèn)都會(huì)被投影 二: 如果你設(shè)置了,就必須同為0,或者同為1,不允許0,1 混合設(shè)置(_id除外) 三: _id雖然可以參與混合設(shè)置,但是它只可以設(shè)為0, 不可以設(shè)為1,因?yàn)?是它默認(rèn)的 通俗理解(0和1的設(shè)定):另一種理解思想 ====> 設(shè)置為1: 就是 加入 白名單 機(jī)制 設(shè)置為0, 就是 加入 黑名單 機(jī)制 注: _id字段是 MongoDB的默認(rèn)字段,它是會(huì)一直被投影的(默認(rèn)白名單) 但是,當(dāng)你強(qiáng)制指定 {"_id": 0} ,強(qiáng)制把 _id指定為0,他就不會(huì)被投影了(變?yōu)楹诿麊危? 語(yǔ)法: M: queryset = table.find({}, {"name": 0}) P: 同上
投影-數(shù)組切片($slice)
"""針對(duì)投影時(shí)的value為數(shù)組的情況下,對(duì)此數(shù)組切片,然后再投影""" 數(shù)據(jù)條件: {"arr1": [5,6,7,8,9] } 整形參數(shù): M: queryset = table.find({},{"arr1":{"$slice": 2}}) # 2表示前2個(gè), -2表示后兩個(gè) P: 同上,一模一樣,一字不差 結(jié)果: { "arr1": [5,6] } 數(shù)組參數(shù): [skip, limit] M: queryset = table.find({},{"arr1":{"$slice": [2,3]}}) # 跳過(guò)前2個(gè),取3個(gè) P: 同上,一模一樣,一字不差 輸出結(jié)果 => { "arr1": {7,8,9] } 注: 這種數(shù)組參數(shù),你可以用 skip+limit 方式理解 也可以用, python的索引+切片方式理解 (skip開(kāi)始查索引(0開(kāi)始數(shù)), 然后取limit個(gè))
投影-數(shù)組過(guò)濾($elemMatch)
""" 針對(duì)投影時(shí) 的value為數(shù)組的情況下,根據(jù)指定條件 對(duì) 數(shù)組 過(guò)濾,然后再投影 注意這個(gè)過(guò)濾機(jī)制: 從前向后找,遇到一個(gè)符合條件的就立刻投影(類(lèi)似 python正則的 search) """ 數(shù)據(jù)條件: {"arr1": [6,7,8,9]} M: queryset = table.find({}, {"arr1": {"$elemMatch": {"$gt":5}} }) P: 同上 輸出結(jié)果 => "arr1" : [ 6 ] 解析:(我自己總結(jié)的偽流程,可參考理解) 1. 準(zhǔn)備投影 2. 發(fā)現(xiàn)數(shù)組,先處理數(shù)組,可看到數(shù)組中有 elemMatch條件 elemMatch在投影中定義為: ”你給我一個(gè)條件,我把符合條件的 數(shù)組每個(gè)元素從前向后篩選 遇到第一個(gè)符合條件的就返回, 剩下的都扔掉 (這里的返回你可以理解為 return) “ 3. 把 2 步驟 返回的數(shù)據(jù) 投影
limit()
limit: (只取前n條) M: queryset = table.find({"name":"lin"}).limit(n) # n就是取的條數(shù) P: 同上
skip()
skip: (跳過(guò)n條,從第n+1條開(kāi)始?。? M: queryset = table.find({"name":"lin"}).skip(n) # 從0開(kāi)始數(shù) P: 同上 解釋一下skip這個(gè)參數(shù)n: 假如n等于2 ,就是從第三個(gè)(真實(shí)個(gè)數(shù))開(kāi)始取 => 你可以借鑒數(shù)組索引的思想 a[2]
count()
count: (統(tǒng)計(jì)記錄數(shù)) M: count_num = table.find({"name":"lin"}).skip(1).limit(1).count() P: count_num = table.count_documents(filter={"name":"lin"}, skip=1, limit=1) 分析: find() -> 查出 3 條數(shù)據(jù) skip(1) -> 跳過(guò)一條,就是從第二條開(kāi)始取 limit(1) -> 接著上面的來(lái),從第二條開(kāi)始?。ㄋ惚旧砼叮∫粋€(gè),實(shí)際上取的就是第二條 count() -> 3 # 也許你很驚訝,按常理來(lái)說(shuō),結(jié)果應(yīng)該為 1(看下面) count(applySkipLimit=false) # 這是 API原型,這個(gè)參數(shù)默認(rèn)為False applySkipLimit: 看名字你就知道這函數(shù)作用了吧 默認(rèn)不寫(xiě)為 False: 不應(yīng)用(忽略) skip(), limit() 來(lái)統(tǒng)計(jì)結(jié)果 ==> 上例結(jié)果為 3 設(shè)為 True: 結(jié)合 skip(), limit() 來(lái)統(tǒng)計(jì)最終結(jié)果 ==> 上例結(jié)果為 1 注: 對(duì)于 count() ,Mongo 和 PyMongo都有此方法,且用法是一模一樣的。 那為什么上面PyMongo中我卻用了 count_documents() 而不是 count() ????? 答: 因?yàn)?運(yùn)行 或者后 戳進(jìn)PyMongo源碼可清晰看見(jiàn),未來(lái)版本 count() API將要廢除。 官方建議我們用 count_documents() 它的好處是把 skip() 和 limit() 由兩個(gè)函數(shù)調(diào)用 變?yōu)?2個(gè)參數(shù)傳進(jìn)去了。
sort()
sort: 排序 M: queryset = table.find({"name":"lin"}).sort({"_id": -1}) # 注意,參數(shù)是{} 對(duì)象 P: queryset = table.find({"name":"lin"}).sort( "_id", -1 ) # 注意,這是2個(gè)參數(shù) 第一個(gè)參數(shù),代表 排序依據(jù)的字段屬性 第二個(gè)參數(shù),代表 升/降 1 : 升序 eg: 456 -1: 降序 eg: 654 特別注意: 3連招順序(優(yōu)先級(jí)要牢記) () sort -> skip -> limit (排序 - 定位 - 挑選) 無(wú)論你代碼什么順序,它都會(huì)這個(gè)順序執(zhí)行 eg: queryset = table.find({"name": "lin"}).sort("_id", -1).skip(1).limit(1) 也許你會(huì)有這樣一個(gè)疑惑: 為什么 count_documents 沒(méi)有放進(jìn)連招里面? 答: 你仔細(xì)想想, 統(tǒng)計(jì)個(gè)數(shù),和你排不排序有關(guān)系嗎? 沒(méi)錯(cuò),一點(diǎn)關(guān)系都沒(méi)有。。。 sort() 和 count() 沒(méi)有聯(lián)系
數(shù)組操作符
已有數(shù)據(jù)條件: { name: ["張","李","王"] } $all: M: queryset = table.find({"name": {"$all": ["張","李"]}}) # 數(shù)組值里必須包含 張和李 P:同上,一模一樣,一字不差 $elemMatch: M: queryset = table.find({"name": {"$elemMatch": {"$eq":"張"} }}) # 數(shù)組值有張 就行 P: 同上,一模一樣,一字不差
正則
M: db.xx.find( {name: { $regex: /^a/, $options:"i" }} ) P: queryset = db.xx.find({"name": {"$regex": "LIN", "$options": "i"}}) PyMongo版的或者這樣寫(xiě)-> import re e1 = re.compile(r"LIN", re.I) # 把Python的正則對(duì)象 代替 Mongo語(yǔ)句 queryset = db.xx.find({"name": {"$regex": re1 }})聚合
聚合表達(dá)式
字段路徑表達(dá)式:
$name # 具體字段
系統(tǒng)變量表達(dá)式:
$$CURRENT # 表示管道中,當(dāng)前操作的文檔
反轉(zhuǎn)義表達(dá)式:
$literal: "$name" # 此處 $name 原語(yǔ)法被破壞,現(xiàn)在它只是單純的字符串
聚合管道
""" 單個(gè)管道,就像 Python中的 map等高階函數(shù)原理, 分而治之。 只不過(guò),MongoDB善于將管道串聯(lián)而已。 .aggregate([ 里面寫(xiě)管道各種操作 ]) """
$match(管道查詢(xún))
M: queryset = table.aggregate([{"$match": {"name": "zhangsan"}}]) P: 同上
$project(管道投影)
數(shù)據(jù)條件 => [ {"id":"xxx", "name" : "zhangsan", "age" : 15 }, {"id":"xxx", "name" : "lisi", "age" : 18 }, {"id":"xxx", "name" : "wangwu", "age" : 16 } ] M: queryset = table.aggregate([{"$project": {"_id": 0,"new":"5"}}]) P: 同上 結(jié)果 => [{"new": "5"}, {"new": "5"}, {"new": "5"}] 注:"new"是在投影的時(shí)候新加的,會(huì)被投影。但是加了此新值,除了_id,其他屬性默認(rèn)都不會(huì)被投影了
$skip (管道跳過(guò),原理同前面講過(guò)skip() 略)
$limit(管道截取,原理同前面講過(guò)的limit() )
M: queryset = table.aggregate([{"$skip": 1},{"$limit":1}]) P: 同上 解釋?zhuān)? 一共三條文檔, skip跳過(guò)了第一條,從第二條開(kāi)始取,limit取一條,所以最終取的是第二條
$sort (管道排序,同上,不解釋)
M: queryset = table.aggregate([{"$sort":{"age":1}}]) P: 同上
$unwind(管道展開(kāi)數(shù)組, 相當(dāng)于 數(shù)學(xué)的 分配律)
數(shù)據(jù)條件 => {"name" : "Tom", "hobby" : [ "sing", "dance" ]} path小參數(shù): M: table.aggregate([{"$unwind":{"path": "$hobby"}}]) # 注意 path是語(yǔ)法關(guān)鍵詞 P: 同上 結(jié)果 => { "_id" : xx, "name" : "Tom", "hobby" : "sing" } { "_id" : xx, "name" : "Tom", "hobby" : "dance" } 形象例子: a * [b+c] => a*b + a*c includeArrayIndex小參數(shù): M: queryset = table.aggregate([{"$unwind": { "path":"$hobby", "includeArrayIndex":"index" # 展開(kāi)的同時(shí)會(huì)新增index字段記錄原索引 }}]) P: 同上 結(jié)果 => {"name" : "Tom", "hobby" : "sing", "index" : NumberLong(0) } {"name" : "Tom", "hobby" : "dance", "index" : NumberLong(1) } 注意: $unwind 上面有兩種特殊情況: 情況一: 文檔中無(wú) hobby字段 或 hobby字段為 空數(shù)組[] 那么該文檔不參與unwind展開(kāi)操作, 自然就不會(huì)顯示結(jié)果。 若想讓這種文檔也參與 unwind展開(kāi)操作,那么需要追加小參數(shù) "preserveNullAndEmptyArrays":true # 與 path同級(jí)書(shū)寫(xiě) 最終結(jié)果,這種字段的文檔也會(huì)被展示出來(lái),并且 index會(huì)被賦予一個(gè) null值 情況二: 文檔中有 hobby字段,但是該字段的值并不是數(shù)組 那么該文檔 會(huì) 參與 unwind展開(kāi)操作,并且會(huì)顯示出來(lái), 同樣 index 會(huì)被賦予一個(gè) null值
$lookup(使用方式一)
使用方式(一):集合關(guān)聯(lián) ===> 我的理解是,相當(dāng)于關(guān)系型數(shù)據(jù)庫(kù)的 多表查詢(xún)機(jī)制 集合 <=> 表 , 多表查詢(xún) <=> 多集合查詢(xún) 自身集合 與 外集合 根據(jù)我們指定的 關(guān)聯(lián)字段 關(guān)聯(lián)后, 如有關(guān)聯(lián), 則新字段的值為 [外集合的關(guān)聯(lián)文檔, 。。。], 有幾條文檔關(guān)聯(lián),這個(gè)數(shù)組就會(huì)有幾條 廢話不多說(shuō),先重新創(chuàng)建兩個(gè)集合: db.user.insertOne({"name":"貓", "country": ["China","USA"]}) # 一條 db.country.insertMany([{"name":"China"}, {"name":"USA"}]) # 兩條 table = db.user # 看好,我賦值了一下,下面直接寫(xiě)table就行了 M: queryset = table.aggregate([{ "$lookup": { "from": "country", # 需要連接的另外一個(gè)集合的名稱(chēng)(外集合) "localField": "country", # (主集合)連接的 依據(jù) 字段 "foreignField": "name", # (外集合)連接的 依據(jù) 字段 "as": "new_field" # 最終關(guān)聯(lián)后查詢(xún)出來(lái)的數(shù)據(jù),生成新字段,as用來(lái)起名 } }]) P: 同上 結(jié)果 => { "_id" : ObjectId("5d2a6f4dee909cc7dc316bf1"), "name" : "貓", "country" : [ "China", "USA" ], # 這行之前應(yīng)該不用解釋?zhuān)@就是 user集合本身的數(shù)據(jù),沒(méi)變 "new_field" : [ # 這行是新加的字段,后面解釋 { "_id" : ObjectId("5d2a6fcbee909cc7dc316bf2"), "name" : "China" }, { "_id" : ObjectId("5d2a6fcbee909cc7dc316bf3"), "name" : "USA" } ] } 解釋?zhuān)? 1. new_field是我們新添加的字段 2. 因?yàn)閡ser集合和country集合 我們給出了2個(gè)依據(jù)關(guān)聯(lián)字段 并且這兩個(gè)關(guān)聯(lián)字段 "China" 和 "USA" 的值都相等 所以最終 user集合的new_field字段中 會(huì)添加 兩條 country集合的文檔 到 [] 中 3. 如果無(wú)關(guān)聯(lián), 那么 new_field字段中的值 為 空[]
$lookup(使用方式二):
使用方式二:不做集合的關(guān)聯(lián),而是直接把(外集合)經(jīng)過(guò)條件篩選,作為新字段放到(主集合)中。 M: queryset = table.aggregate([{ "$lookup": { "from": "country", # 外集合 "let": {"coun": "$country"}, # 使(主集合)的變量 可以放在(外集合)使用 "pipeline": [{ # 外集合的專(zhuān)屬管道,里面只可以用外集合的屬性 "$match": { # 因?yàn)樵O(shè)置了 let,所以這里面可以用主集合變量 "$expr": { # $expr使得$match里面可以使用 聚合操作 "$and": [ {"$eq": ["$name", "China"]}, # 注意,這是聚合的 $eq用法 {"$eq": ["$$coun",["China", "USA"]]} ] } } }], "as": "new_field" } }]) P: 同上 解釋?zhuān)? 把(外集合) pipeline里面按各種條件 查到的文檔, 作為(主集合)new_field 的值。 當(dāng)然,如果不需要主集合中的屬性,可以舍棄 let 字段
$group (分組--統(tǒng)計(jì)種類(lèi))
用法1(分組--統(tǒng)計(jì)字段種類(lèi)) M: queryset = table.aggregate([{"$group": {"_id": "$name"}}]) # _id是固定寫(xiě)法 P: 同上 結(jié)果 => [{"_id": "老鼠"}, {"_id": "狗"}, {"_id": "貓"}] 用法2(分組--聚合) 數(shù)據(jù)條件: { "name" : "貓", "country" : [ "China", "USA" ], "age" : 18 } { "name" : "狗", "country" : "Japna" } { "name" : "老鼠", "country" : "Korea", "age" : 12 } { "name" : "貓", "country" : "Japna" } M: queryset = table.aggregate([{ "$group": { "_id": "$name", # 根據(jù)name字段分組 "type_count": {"$sum": 1}, # 統(tǒng)計(jì)每個(gè)分類(lèi)的 個(gè)數(shù) "ageCount": {"$sum": "$age"}, # 統(tǒng)計(jì)age字段的 數(shù)字和 "ageAvg": {"$avg": "$age"}, # 統(tǒng)計(jì)age字段的 平均值 "ageMin": {"$min": "$age"}, # 統(tǒng)計(jì)age字段的 最小值 "ageMax": {"$max": "$age"}, # 統(tǒng)計(jì)age字段的 最大值 } }]) p: 同上 結(jié)果: { "_id" : "老鼠", "type_count" : 1, "ageCount" : 12, "ageAvg" : 12, "ageMin" : 12, "ageMax" : 12 } { "_id" : "狗", "type_count" : 1, "ageCount" : 0, "ageAvg" : null, "ageMin" : null, "ageMax" : null } { "_id" : "貓", "type_count" : 2, "ageCount" : 18, "ageAvg" : 18, "ageMin" : 18, "ageMax" : 18 } 注意: 若想直接對(duì)整個(gè)集合的 做統(tǒng)計(jì),而不是分組再統(tǒng)計(jì) 把 _id改為 null即可 { _id: "null" } # (或者隨便寫(xiě)一個(gè)匹配不到的 字符串或數(shù)字都行,分不了組,就自動(dòng)給你統(tǒng)計(jì)整個(gè)集合了)
$out (聚合操作后,將結(jié)果寫(xiě)入新集合)
""" 我的理解是重定向 操作, 或者理解為 視圖 操作 寫(xiě)入的集合如果存在,那么會(huì)全部覆蓋(但保留索引) 聚合過(guò)程遇到錯(cuò)誤,那么會(huì)自動(dòng)執(zhí)行 ’回滾’操作 """ M: table.aggregate([ { "$group": {"_id": "$name"} }, { "$out": "newCollection" } ]) P: 同上 最后驗(yàn)證: db.newCollection.find() ,你就會(huì)看到新集合 及其 里面的內(nèi)容 聚合管道 ==> 第二個(gè)參數(shù) table.aggregate([之前說(shuō)的都是這里面的參數(shù)], 下面說(shuō)這個(gè)參數(shù)) allowDiskUse: true 每個(gè)聚合管道占用內(nèi)存需 < 16M, 過(guò)大就會(huì)出問(wèn)題 allowDiskUse設(shè)置為true, 會(huì)將內(nèi)存的 寫(xiě)入到臨時(shí)文件中,減緩內(nèi)存壓力。 官方文檔:write data to the _tmp subdirectory in the dbPath directory Default: /data/db on Linux and macOS, datadb on Windows 它說(shuō): 默認(rèn)在 dbPath配置變量下的 子目錄_tmp下, dbPath默認(rèn)為 : /data/db M: queryset = table.aggregate([{ "$group": {"_id": "$name"}}], {"allowDiskUse": true} ) P: queryset = table.aggregate([{ "$group": {"_id": "$name"}}], allowDiskUse=True, # 注意,這里語(yǔ)法稍有不一樣 )索引
創(chuàng)建索引:
單鍵索引
M: table.createIndex({"name":1}) P: table.create_index([("name",-1)]) # -1代表逆序索引,注意是元組
聯(lián)合索引
索引命中:最左匹配原則 eg 1,2,3 這三個(gè)創(chuàng)建聯(lián)合索引, 可命中索引為:【1,12,123】 M: table.createIndex( {"name":1}, {}, {} ) # 多個(gè){} P: table.create_index([ ("name",-1), (), () ]) # 多個(gè)元組
多鍵索引
多鍵是針對(duì)于數(shù)組來(lái)講的,創(chuàng)建單鍵的字段 指定為 數(shù)組字段, 默認(rèn)就會(huì)設(shè)置為多鍵索引
唯一索引 (unique)
"""注意: 如果集合中,不同文檔的字段有重復(fù),創(chuàng)建唯一索引的時(shí)候會(huì)報(bào)錯(cuò)""" M: table.createIndex({"name":1}, {"unique":true}) P: table.create_index([("name", 1),("counrty",1)], unique=True)
稀疏索引 (sparse)
eg: 一個(gè)集合中: 給 name創(chuàng)建 唯一索引 插入文檔1: 有 name字段 插入文檔2: 無(wú) name字段 (MongoDB會(huì)在索引庫(kù)中,把沒(méi)有的字段的 索引設(shè)為 {字段:null} ) 再插入文檔3, 無(wú)name字段 --> 同樣也會(huì)把索引庫(kù)中 name設(shè)為 null 但是就在這個(gè)時(shí)候,剛要把索引庫(kù)中的 name字段設(shè)為 null的時(shí)候。。。 唯一索引告訴你:” 我這里已經(jīng)有了一個(gè),{ name:null },請(qǐng)你滾 ” 然后就無(wú)情的給你報(bào)錯(cuò)了(重復(fù)索引字段) 那咋整啊, 別急,稀疏索引就是給你辦這事的 設(shè)置稀疏索引。 MongoDB就不會(huì)把 沒(méi)有的字段 加入到索引庫(kù)了 所以,索引庫(kù)里面就不會(huì)自動(dòng)添加 {字段: null} 重新再次插入文檔3, 無(wú)name字段, 可成功插入,不存在null的重復(fù)問(wèn)題了 M: table.createIndex({"name":1}, {"unique":true, "sparse":true}) P: table.create_index([("name", 1),("counrty",1)], unique=True, sparse=True)
查詢(xún)索引
M:queryset = table.getIndexes() P: queryset = table.list_indexes()
刪除索引
方式1: M: table.dropIndex("索引名") # 索引名可通過(guò) 上面查詢(xún)索引的指令查 P: table.drop_index("索引名") 方式2: M: table.dropIndexes() # 刪除全部,_id除外, 想指定刪除多個(gè),可用列表列出 P: table.drop_indexes()
查看索引性能(是否有效)
table.上面說(shuō)過(guò)的任一函數(shù)().explain() # 鏈?zhǔn)秸{(diào)用 explain,表示列出此操作的性能 eg: M: queryset = table.explain().find({"name":"貓"}) P: 同上 結(jié)果中找到: queryPlanner -> winningPlan -> inputStage -> stage # stage結(jié)果對(duì)應(yīng)說(shuō)明如下 COLLSCAN # 未優(yōu)化,還是搜的整個(gè)集合 IXSCAN # 索引起到作用 索引對(duì)投影的優(yōu)化: queryPlanner -> winningPlan -> stage # stage結(jié)果對(duì)應(yīng)說(shuō)明如下 FETCH # 索引 對(duì)投影 未優(yōu)化 PROJECTION # 索引 對(duì)投影 起到優(yōu)化作用 索引對(duì)排序的優(yōu)化: 同上 stage 最好 不是 sort 按索引 正序(逆序) 取數(shù)據(jù), 這樣就有效避免了機(jī)械排序的過(guò)程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/45195.html
摘要:不要疑惑,告訴你答案這個(gè)代表正負(fù)號(hào)的正。雖然一點(diǎn)技術(shù)含量沒(méi)有,但是你要懂序列也許叫可迭代對(duì)象更為合適,但是我喜歡叫序列。 數(shù)據(jù)結(jié)構(gòu) 可變類(lèi)型與不可變類(lèi)型(重頭戲) 基操: 可變類(lèi)型:[], {} # 可增刪改 查 不可變類(lèi)型: int float str () # 無(wú)法增刪改, 只可查 升操: + 與...
預(yù)編譯 import re re1 = re.compile(r元字符 組成的正則規(guī)則) # 元字符下面會(huì)說(shuō) re1.方法() # 方法下邊也會(huì)說(shuō) 元字符: 表示普通字符: . # 除了 外 都可以匹配的到 d # 只匹配 純數(shù)字 0-9 D # 和 d相反, 除了數(shù)字全都匹配 ...
摘要:解釋就相當(dāng)于把每個(gè)序列元素的每一個(gè)單獨(dú)用一個(gè)管道函數(shù)處理,再把他們按順序組合成一個(gè)新可迭代對(duì)象注意這個(gè)管道函數(shù)只能是單參數(shù)函數(shù),如果想傳遞多個(gè)參數(shù)怎么辦使用偏函數(shù)怕有些人看不懂,這里就不用了,而是用普通函數(shù)定義方式固定值固定值固定值固定值固 map In [25]: list(map(lambda a:a**2, [1,2,3,4])) Out[25]: [1, 4, 9, 16] 解...
摘要:多線程對(duì)于爬蟲(chóng)方面也可以表現(xiàn)出較好的性能。計(jì)算密集型就別想多線程了,一律多進(jìn)程。所以同一時(shí)刻最大的并行線程數(shù)進(jìn)程數(shù)的核數(shù)這條我的個(gè)人理解很模糊,參考吧多線程多線程有種通過(guò)的那種方式,非常普遍,此處就不寫(xiě)了。 GIL的理解 GIL這個(gè)話題至今也是個(gè)爭(zhēng)議較多的,對(duì)于不用應(yīng)用場(chǎng)景對(duì)線程的需求也就不同,說(shuō)下我聽(tīng)過(guò)的優(yōu)點(diǎn): 1. 我沒(méi)有用過(guò)其他語(yǔ)言的多線程,所以無(wú)法比較什么,但是對(duì)于I/O而言,...
閱讀 2896·2021-09-28 09:36
閱讀 3655·2021-09-27 13:59
閱讀 2499·2021-08-31 09:44
閱讀 2288·2019-08-30 15:54
閱讀 2361·2019-08-30 15:44
閱讀 1196·2019-08-30 13:45
閱讀 1232·2019-08-29 18:38
閱讀 1221·2019-08-29 18:37