成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

經(jīng)驗(yàn)拾憶(純手工)=> MongoDB與PyMongo語(yǔ)法對(duì)比解析

mo0n1andin / 3584人閱讀

摘要:舉個(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

相關(guān)文章

  • 經(jīng)驗(yàn)拾憶手工)=&gt; Python基本數(shù)據(jù)類(lèi)型

    摘要:不要疑惑,告訴你答案這個(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ú)法增刪改, 只可查 升操: + 與...

    Andrman 評(píng)論0 收藏0
  • 經(jīng)驗(yàn)拾憶手工)=&gt; Python正則全解詳解

    預(yù)編譯 import re re1 = re.compile(r元字符 組成的正則規(guī)則) # 元字符下面會(huì)說(shuō) re1.方法() # 方法下邊也會(huì)說(shuō) 元字符: 表示普通字符: . # 除了 外 都可以匹配的到 d # 只匹配 純數(shù)字 0-9 D # 和 d相反, 除了數(shù)字全都匹配 ...

    Luosunce 評(píng)論0 收藏0
  • 經(jīng)驗(yàn)拾憶手工)=&gt; Python高階函數(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] 解...

    Elle 評(píng)論0 收藏0
  • 經(jīng)驗(yàn)拾憶手工)=&gt; Python三程

    摘要:多線程對(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而言,...

    Snailclimb 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<