热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

经验拾忆(纯手工)=>MongoDB与PyMongo语法对比解析

阅读须知由于是对比书写:M:代表Mongo原生语法P:代表PyMongo书写方法后面提到:”同上“字眼:意思就是Mongo和PyMongo语句是一模一样的,一个字都不差,复制上去,


阅读须知


由于是对比书写:
M: 代表 Mongo原生语法
P: 代表 PyMongo书写方法

后面提到:”同上“ 字眼:
意思就是 Mongo 和 PyMongo 语句是一模一样的, 一个字都不差,复制上去,可以直接运行
(也许你很好奇,为什么 一个是Python语言里的PyMongo,一个是Mongo)
他们的语句为什么可以做到一模一样 ??
答:因为 Mongo和Python都可以 给变量赋值, PyMongo的语法设计也是模仿Mongo的。
所以:我巧妙的 把二者的变量设为同一个,函数90%都一致, 所以整条语句就一模一样了!

主要语法区别:
1. 函数命名
Mongo 方法函数大都以 驼峰命名
PyMongo方法函数大都以 _ 下划线分割命名
2. 函数参数
Mongo : 基本都是 {} + [] 各组组合格式
PyMongo:同上, 但{}的 key需要使用字符串格式, 有些情况,还需要使用命名参数代替 {}
3. 空值 与 Bool
Mongo: null true false
PyMongo: None True False

前置安装配置环境




  • 客户端连接:


    pip install pymongo
    import pymongo

    M: Mongo
    P: cursor = pymongo.MongoClient('ip',port=27017)



  • 选择数据库:


    M: use test
    P: db = cursor['test'] # 记住这个db, 下面复用这个参数



  • 选择集合: (记住table变量名,下面就直接用他们了) 注意,注意,注意


    M: table = db.zhang
    P: table = db['zhang']
    注:选择库,选择集合的时候 注意事项:
    Mongo中: xx.xx 用 . 的语法
    PyMongo中:也可以 用 xx.xx 这样, 但是这样用在PyCharm中没有语法提示

    所以提倡 xx['xx'] 用索引的方式使用



  • Mongo 与 PyMongo 返回结果的游标比较


    Mongo中:
    大多数查询等结果返回都是游标对象
    如果不对游标遍历,那么Mongo的游标会默认为你取出 前 20 个 值
    当然,你也可以索引取值
    关闭操作: .close()
    PyMongo中:
    同样,大多数查询等结果返回都是游标对象(如果你学过ORM,可以理解游标就像 ORM的查询集)
    所以必须通过 list() 或 遍历 或 索引 等操作才能真正取出值
    关闭操作: .close() 或者 用 Python 的 with 上下文协议






  • save()


    M: table.save({}) # 估计要废弃了
    P: 将要被废弃 用insert_one代替它



  • insert()


    M: table.insert() # 包括上面两种,可以一个 {},可以多个 [{},{}]
    P: PyMongo源码明确说明,insert()语法将被废弃,请用 insert_one({}) 和 insert_many([])代替



  • insert_one() 和 insert_many()


    M:
    table.insertOne( {} ) # 驼峰
    table.insertMany([ {},{} ]) # 驼峰
    P:
    table.insert_one( {} ) # 下划线
    table.insert_many([ {},{} ]) # 下划线






  • remove()


    参数1:删除查询条件
    参数2:删除选项
    M: table.remove({'name':'zhangsan'}, {'justOne': true}) # 我更喜欢用delete的
    P: PyMongo中,此方法将被废弃。 将会被 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'})
    注意:
    不知道这两个函数是否让你想起了前面讲的 insertOne 和 insertMany,他们看起来很像,语法不同:
    insertMany([]) # 参数需要用 [] 包起来
    deleteMany({}) # 参数不需要
    注意2:
    table.deleteMany({}) # 空 {}, 代表删除所有文档 (慎行,慎行,慎行)



  • 删除整个集合:


    table.drop() # 删除集合(连同 所有文档, 连同 索引,全部删除)




"""
文档修改, 注意: _id 不可修改
"""



  • 三种更新方法:


    1. update(将要废弃,可跳过,直接看2,3点的方法)
    update({查询条件}, {更新操作符} , {更新选项})

    M: table.update({'name': {'$regex':'li'}},{'$set':{'name':'lin2'}}, {multi: true})
    P: table.update({'name': {'$regex': 'li'}}, {'$set': {'name': 'lin3'}},multi=True)

    注意1: 第三个参数 multi如果不设置,默认只更新一条文档,设置为 true ,就会更新多条文档
    注意2:
    Mongo写法: {multi: true} # Mongo 和往常一样,采用json格式, true小写
    Python写法: multi = True # python是采用命名参数来传递, True大写

    2. updateOne(更新一条)
    M: updateOne( {查询条件}, {更新操作符} )
    P: update_one
    3. updateMany(更新多条)
    M: updateMany( {查询条件}, {更新操作符} ) 其实参数是一模一样的,只不过方法名区分
    P: update_many


    注: 这三个方法的参数 是基本一模一样的
    所以下面讲具体 {查询条件}, {更新操作符} 时
    就统一用 update()来写了



  • 普通更新操作符:




  • $set(更新)


    # 注:规则就是:"有则改之, 无则添加"
    M: table.update({'5':5},{'$set': {'lin': [5,6,7,8]} })
    P: 同上
    微扩展(关于内嵌数组):
    table.update({'5':5},{'$set': {'lin.0': '呵呵' }) # lin.0代表数组的第一个元素
    当数组的索引越界,这个时候就视为数组的添加操作。
    eg: 假定我们给 lin.10 一个值,那么 中间空出的那么多索引,会自动填充 null



  • $unset(删除)


    # 注:删除的键对应的value可以随便写,写啥都会删除, 写 '' 只是为了语义明确(规范)
    M: table.update({'6':6}, {'$unset': {'6':''}}) # 把此条记录的 '6' 字段删除
    P: 同上

    微扩展(关于嵌套数组):
    table.update({'5':5}, {'$unset': {'lin.0':''}}) # lin.0同样代表数组第一个元素
    注:数组的删除 并不是真正的删除, 而是把值 用 null 替换



  • $rename(改名,替换)


    M: table.update({'name':'lin'}, {'$rename':{'name':'nick'}}) # name变成了nick
    P: 同上
    微扩展(文档嵌套):
    如果文档是嵌套的 eg: { a: {b:c} }
    M: table.update({'lin':'lin'}, {'$rename': {'a.b':'d'}})
    P: 同上
    结果 => {"a" : { }, "d" : "c" }
    解析:
    b 属于 子文档
    a.b 表示 通过父文档的a 来取出 子文档的b
    如果整体a.b被 rename为 d,那么 d会被安排到父文档的层级里,而a设为空。
    举个栗子:
    你有一个箱子,里面 有一个 儿子级别 和 孙子级别 的箱子 (共3层)
    现在你把 孙子级别的箱子 单独拿出来, 把整个箱子替换掉
    就是这种思想。。。自己体会吧

    (这种语法,好像Python列表的切片赋值。。形容可能不太恰当)



  • $inc:


    {$inc: { 'age': -2}} # 减少两岁,正数表示加法,负数表示减法,简单,不举例了
    特例:如果字段不存在,那么,此字段会被添加, 并且值就是你设定的值(0+n=n)



  • $mul:


    {$mul: { 'age': 0.5}} # 年龄除以2,整数表示乘法,小数表示除法,简单,不举例了
    特例:如果字段不存在,那么,此字段会被添加, 并且值为0 (0*n=0)



  • $min


    {$min: { 'age': 30}} # 30比原有值小:就替换, 30比原有值大,则不做任何操作



  • $max


    {$max: { 'age': 30}} # 30比原有值大:就替换, 30比原有值小,则不做任何操作
    特例:min和max特例相同,即如果字段不存在,那么,此字段会被添加, 并且值就是你设定的值



  • 数组更新操作符:


    """
    单数组: xx
    内嵌数组: xx.索引
    """



  • $addToSet(有序,无重复,尾部添加)


    原始数据: {'1':1}

    M: table.update({'1':1}, {'$addToSet':{'lin':[7,8]}})
    P: 同上
    结果 => {"1": 1,"lin": [ [7, 8 ] ]} # [7,8] 整体插入进来, 特别注意这是二级列表



  • $each ( 给[7,8]加个 $each,注意看结果变化 )


    M: table.update({'1': 1}, {'$addToSet': {'lin': {'$each':[7, 8]} }})
    P: 同上
    结果 => {"1": 1, "lin": [7,8]} # 7,8单独插入进来,参考python的 * 解构



  • $push(数据添加, 比$addToSet强大,可任意位置,可重复)


    """
    补充说明:
    $addToSet:添加数据有重复,会自动去重
    $push :添加数据有重复,不会去重,而是直接追加
    """
    原始数据: {'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
    }}}) # 这里为了清晰点,我就把所有括号折叠起来了
    P: 同上
    结果 => {"1" : 1, "lin" : [ { "a" : 7, "b" : 6 }, { "a" : 6, "b" : 7 } ] }
    终极解析:
    1. 添加数组: 先走 $sort => 根据a 逆序排列
    2. 再走 $position, 0表示:索引定位从0开始
    3. 再走 $slice, 2表示: 取2个
    4. 最后走 $each,把数组元素逐个放进另一个数组,说过的,相当于python的 * 解构操作,



  • $pop(只能 删除 头或尾 元素)


    M: table.update({'a': a}, {'$pop': {'lin': 1}}) # 删除最后一个
    P: 同上

    注1:$pop参数, 1代表最后一个, -1代表第一个。 这个是值得注意一下的,容易记反
    注2:如果全部删没了,那么会剩下空[], 而不是彻底删除字段



  • $pull (删除 任何位置 的 指定的元素)


    M: table.update({'1': 1},{'$pull':{ 'lin':[7,8]}}) # 删除数组中[7,8]这个内嵌数组
    P: 同上



  • $pullAll(基本和 $pull 一致)


    M: table.update({'1': 1},{'$pullAll':{ 'lin':[ [7,8] ]}}) # 同$pull,但多了个 []
    P: 同上
    注: $pull 和 $pullAll 针对于 内嵌文档 和 内嵌数组 有细小差别, 差别如下:
    内嵌数组:
    $pull 和 $pullAll 都严格要求内嵌数组的 排列顺序,顺序不一致,则不返回
    内嵌文档:
    $pullAll : 严格要求内嵌文档的顺序, 顺序不一致,则 不返回
    $pull : 不要求内嵌文档的循序, 顺序不一致,一样可以返回




"""
第一个参数的条件是 筛选出 数据的记录(文档)
第二个参数的条件是 筛选出 数据的记录中的 属性(字段),不配置 就是 默认 取出所有字段
find({查询条件}, {投影设置})
"""



  • 投影解释


    哪个字段 设置为 0, 此字段就不会被投影, 而其他字段全部被投影
    哪个字段 设置为 1, 此字段就会被单独投影, 其他字段不投影
    {'name': 0, 'age': 0} # 除了 name 和 age ,其他字段 都 投影
    {'name': 1, 'age': 1} # 只投影 name 和 age, 其他字段 不 投影,(_id除外)
    注意:所有字段必须满足如下要求:
    一: 你可以不设置,默认都会被投影
    二: 如果你设置了,就必须同为0,或者同为1,不允许0,1 混合设置(_id除外)
    三: _id虽然可以参与混合设置,但是它只可以设为0, 不可以设为1,因为1是它默认的
    通俗理解(0和1的设定):另一种理解思想 ====>
    设置为1: 就是 加入 白名单 机制
    设置为0, 就是 加入 黑名单 机制

    注: _id字段是 MongoDB的默认字段,它是会一直被投影的(默认白名单)
    但是,当你强制指定 {'_id': 0} ,强制把 _id指定为0,他就不会被投影了(变为黑名单)
    语法:
    M: queryset = table.find({}, {'name': 0})
    P: 同上



  • 投影-数组切片($slice)


    """针对投影时的value为数组的情况下,对此数组切片,然后再投影"""
    数据条件: {'arr1': [5,6,7,8,9] }
    整形参数:
    M: queryset = table.find({},{'arr1':{'$slice': 2}}) # 2表示前2个, -2表示后两个
    P: 同上,一模一样,一字不差
    结果: { 'arr1': [5,6] }
    数组参数: [skip, limit]
    M: queryset = table.find({},{'arr1':{'$slice': [2,3]}}) # 跳过前2个,取3个
    P: 同上,一模一样,一字不差
    输出结果 => { 'arr1': {7,8,9] }

    注: 这种数组参数,你可以用 skip+limit 方式理解
    也可以用, python的索引+切片方式理解 (skip开始查索引(0开始数), 然后取limit个)



  • 投影-数组过滤($elemMatch)


    """
    针对投影时 的value为数组的情况下,根据指定条件 对 数组 过滤,然后再投影
    注意这个过滤机制: 从前向后找,遇到一个符合条件的就立刻投影(类似 python正则的 search)
    """
    数据条件: {'arr1': [6,7,8,9]}
    M: queryset = table.find({}, {'arr1': {'$elemMatch': {'$gt':5}} })
    P: 同上
    输出结果 => "arr1" : [ 6 ]
    解析:(我自己总结的伪流程,可参考理解)
    1. 准备投影
    2. 发现数组,先处理数组,可看到数组中有 elemMatch条件
    elemMatch在投影中定义为:
    ”你给我一个条件,我把符合条件的 数组每个元素从前向后筛选
    遇到第一个符合条件的就返回, 剩下的都扔掉 (这里的返回你可以理解为 return)

    3. 把 2 步骤 返回的数据 投影



  • limit()


    limit: (只取前n条)
    M: queryset = table.find({'name':'lin'}).limit(n) # n就是取的条数
    P: 同上



  • skip()


    skip: (跳过n条,从第n+1条开始取)
    M: queryset = table.find({'name':'lin'}).skip(n) # 从0开始数
    P: 同上

    解释一下skip这个参数n:
    假如n等于2 ,就是从第三个(真实个数)开始取 => 你可以借鉴数组索引的思想 a[2]



  • count()


    count: (统计记录数)
    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 条数据
    skip(1) -> 跳过一条,就是从第二条开始取
    limit(1) -> 接着上面的来,从第二条开始取(算本身哦),取一个,实际上取的就是第二条
    count() -> 3 # 也许你很惊讶,按常理来说,结果应该为 1(看下面)

    count(applySkipLimit=false) # 这是 API原型,这个参数默认为False
    applySkipLimit: 看名字你就知道这函数作用了吧
    默认不写为 False: 不应用(忽略) skip(), limit() 来统计结果 ==> 上例结果为 3
    设为 True: 结合 skip(), limit() 来统计最终结果 ==> 上例结果为 1

    注: 对于 count() ,Mongo 和 PyMongo都有此方法,且用法是一模一样的。
    那为什么上面PyMongo中我却用了 count_documents() 而不是 count() ?????
    答:
    因为 运行 或者后 戳进PyMongo源码可清晰看见,未来版本 count() API将要废除。
    官方建议我们用 count_documents()
    它的好处是把 skip() 和 limit() 由两个函数调用 变为 2个参数传进去了。



  • sort()


    sort: 排序
    M: queryset = table.find({'name':'lin'}).sort({'_id': -1}) # 注意,参数是{} 对象
    P: queryset = table.find({'name':'lin'}).sort( '_id', -1 ) # 注意,这是2个参数
    第一个参数,代表 排序依据的字段属性
    第二个参数,代表 升/降
    1 : 升序 eg: 456
    -1: 降序 eg: 654
    特别注意: 3连招顺序(优先级要牢记) ()
    sort -> skip -> limit (排序 - 定位 - 挑选) 无论你代码什么顺序,它都会这个顺序执行
    eg: queryset = table.find({'name': 'lin'}).sort('_id', -1).skip(1).limit(1)
    也许你会有这样一个疑惑: 为什么 count_documents 没有放进连招里面?
    答:
    你仔细想想, 统计个数,和你排不排序有关系吗?
    没错,一点关系都没有。。。 sort() 和 count() 没有联系



  • 数组操作符


    已有数据条件: { name: ['张','李','王'] }
    $all:
    M: queryset = table.find({'name': {'$all': ['张','李']}}) # 数组值里必须包含 张和李
    P:同上,一模一样,一字不差
    $elemMatch:
    M: queryset = table.find({'name': {'$elemMatch': {'$eq':'张'} }}) # 数组值有张 就行
    P: 同上,一模一样,一字不差



  • 正则


    M: db.xx.find( {name: { $regex: /^a/, $options:'i' }} )
    P: queryset = db.xx.find({'name': {'$regex': 'LIN', '$options': 'i'}})
    PyMongo版的或者这样写->
    import re
    e1 = re.compile(r'LIN', re.I) # 把Python的正则对象 代替 Mongo语句
    queryset = db.xx.find({'name': {'$regex': re1 }})



聚合




  • 聚合表达式




  • 字段路径表达式:


    $name # 具体字段



  • 系统变量表达式:


    $$CURRENT # 表示管道中,当前操作的文档



  • 反转义表达式:


    $literal: '$name' # 此处 $name 原语法被破坏,现在它只是单纯的字符串



  • 聚合管道


    """
    单个管道,就像 Python中的 map等高阶函数原理, 分而治之。
    只不过,MongoDB善于将管道串联而已。
    .aggregate([ 里面写管道各种操作 ])
    """



  • $match(管道查询)


    M: queryset = table.aggregate([{'$match': {'name': 'zhangsan'}}])
    P: 同上



  • $project(管道投影)


    数据条件 =>
    [
    {"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: 同上

    结果 => [{'new': '5'}, {'new': '5'}, {'new': '5'}]
    注:'new'是在投影的时候新加的,会被投影。但是加了此新值,除了_id,其他属性默认都不会被投影了



  • $skip (管道跳过,原理同前面讲过skip() 略)




  • $limit(管道截取,原理同前面讲过的limit() )


    M: queryset = table.aggregate([{'$skip': 1},{'$limit':1}])
    P: 同上
    解释:
    一共三条文档, skip跳过了第一条,从第二条开始取,limit取一条,所以最终取的是第二条



  • $sort (管道排序,同上,不解释)


    M: queryset = table.aggregate([{'$sort':{'age':1}}])
    P: 同上



  • $unwind(管道展开数组, 相当于 数学的 分配律)


    数据条件 => {"name" : "Tom", "hobby" : [ "sing", "dance" ]}

    path小参数:
    M: table.aggregate([{'$unwind':{'path': '$hobby'}}]) # 注意 path是语法关键词
    P: 同上
    结果 =>
    { "_id" : xx, "name" : "Tom", "hobby" : "sing" }
    { "_id" : xx, "name" : "Tom", "hobby" : "dance" }
    形象例子:
    a * [b+c] => a*b + a*c

    includeArrayIndex小参数:
    M: queryset = table.aggregate([{'$unwind': {
    'path':'$hobby',
    'includeArrayIndex':'index' # 展开的同时会新增index字段记录原索引
    }}])
    P: 同上
    结果 =>
    {"name" : "Tom", "hobby" : "sing", "index" : NumberLong(0) }
    {"name" : "Tom", "hobby" : "dance", "index" : NumberLong(1) }

    注意:
    $unwind 上面有两种特殊情况:
    情况一:
    文档中无 hobby字段 或 hobby字段为 空数组[]
    那么该文档不参与unwind展开操作, 自然就不会显示结果。
    若想让这种文档也参与 unwind展开操作,那么需要追加小参数
    'preserveNullAndEmptyArrays':true # 与 path同级书写
    最终结果,这种字段的文档也会被展示出来,并且 index会被赋予一个 null值
    情况二:
    文档中有 hobby字段,但是该字段的值并不是数组
    那么该文档 会 参与 unwind展开操作,并且会显示出来, 同样 index 会被赋予一个 null值



  • $lookup(使用方式一)


    使用方式(一):集合关联 ===> 我的理解是,相当于关系型数据库的 多表查询机制
    集合 <=> 表 , 多表查询 <=> 多集合查询
    自身集合 与 外集合 根据我们指定的 关联字段 关联后, 如有关联,
    则新字段的值为 [外集合的关联文档, 。。。], 有几条文档关联,这个数组就会有几条
    废话不多说,先重新创建两个集合:
    db.user.insertOne({'name':'猫', 'country': ['China','USA']}) # 一条
    db.country.insertMany([{'name':'China'}, {'name':'USA'}]) # 两条

    table = db.user # 看好,我赋值了一下,下面直接写table就行了

    M: queryset = table.aggregate([{
    '$lookup': {
    'from': 'country', # 需要连接的另外一个集合的名称(外集合)
    'localField': 'country', # (主集合)连接的 依据 字段
    'foreignField': 'name', # (外集合)连接的 依据 字段
    'as': 'new_field' # 最终关联后查询出来的数据,生成新字段,as用来起名
    }
    }])
    P: 同上

    结果 =>
    {
    "_id" : ObjectId("5d2a6f4dee909cc7dc316bf1"),
    "name" : "猫",
    "country" : [
    "China",
    "USA"
    ], # 这行之前应该不用解释,这就是 user集合本身的数据,没变
    "new_field" : [ # 这行是新加的字段,后面解释
    {
    "_id" : ObjectId("5d2a6fcbee909cc7dc316bf2"),
    "name" : "China"
    },
    {
    "_id" : ObjectId("5d2a6fcbee909cc7dc316bf3"),
    "name" : "USA"
    }
    ]
    }
    解释:
    1. new_field是我们新添加的字段
    2. 因为user集合和country集合 我们给出了2个依据关联字段
    并且这两个关联字段 'China' 和 'USA' 的值都相等
    所以最终 user集合的new_field字段中 会添加 两条 country集合的文档 到 [] 中
    3. 如果无关联, 那么 new_field字段中的值 为 空[]



  • $lookup(使用方式二):


    使用方式二:不做集合的关联,而是直接把(外集合)经过条件筛选,作为新字段放到(主集合)中。

    M: queryset = table.aggregate([{
    '$lookup': {
    'from': 'country', # 外集合
    'let': {'coun': '$country'}, # 使(主集合)的变量 可以放在(外集合)使用
    'pipeline': [{ # 外集合的专属管道,里面只可以用外集合的属性
    '$match': { # 因为设置了 let,所以这里面可以用主集合变量
    '$expr': { # $expr使得$match里面可以使用 聚合操作
    '$and': [
    {'$eq': ['$name', 'China']}, # 注意,这是聚合的 $eq用法
    {'$eq': ['$$coun',['China', 'USA']]}
    ]
    }
    }
    }],
    'as': 'new_field'
    }
    }])
    P: 同上
    解释:
    把(外集合) pipeline里面按各种条件 查到的文档, 作为(主集合)new_field 的值。
    当然,如果不需要主集合中的属性,可以舍弃 let 字段



  • $group (分组--统计种类)


    用法1(分组--统计字段种类)
    M: queryset = table.aggregate([{'$group': {'_id': '$name'}}]) # _id是固定写法
    P: 同上
    结果 => [{'_id': '老鼠'}, {'_id': '狗'}, {'_id': '猫'}]

    用法2(分组--聚合)
    数据条件:
    { "name" : "猫", "country" : [ "China", "USA" ], "age" : 18 }
    { "name" : "狗", "country" : "Japna" }
    { "name" : "老鼠", "country" : "Korea", "age" : 12 }
    { "name" : "猫", "country" : "Japna" }

    M: queryset = table.aggregate([{
    '$group': {
    '_id': '$name', # 根据name字段分组
    'type_count': {'$sum': 1}, # 统计每个分类的 个数
    'ageCount': {'$sum': '$age'}, # 统计age字段的 数字和
    'ageAvg': {'$avg': '$age'}, # 统计age字段的 平均值
    'ageMin': {'$min': '$age'}, # 统计age字段的 最小值
    'ageMax': {'$max': '$age'}, # 统计age字段的 最大值
    }
    }])
    p: 同上

    结果:
    {
    "_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
    }
    注意:
    若想直接对整个集合的 做统计,而不是分组再统计
    把 _id改为 null即可 { _id: 'null' }
    # (或者随便写一个匹配不到的 字符串或数字都行,分不了组,就自动给你统计整个集合了)



  • $out (聚合操作后,将结果写入新集合)


    """
    我的理解是重定向 操作, 或者理解为 视图 操作
    写入的集合如果存在,那么会全部覆盖(但保留索引)
    聚合过程遇到错误,那么会自动执行 ’回滚’操作
    """
    M:
    table.aggregate([
    { '$group': {'_id': '$name'} },
    { '$out': 'newCollection' }
    ])
    P: 同上
    最后验证: db.newCollection.find() ,你就会看到新集合 及其 里面的内容
    聚合管道 ==> 第二个参数
    table.aggregate([之前说的都是这里面的参数], 下面说这个参数)

    allowDiskUse: true
    每个聚合管道占用内存需 <16M, 过大就会出问题
    allowDiskUse设置为true, 会将内存的 写入到临时文件中,减缓内存压力。
    官方文档:write data to the _tmp subdirectory in the dbPath directory
    Default: /data/db on Linux and macOS, \data\db on Windows
    它说: 默认在 dbPath配置变量下的 子目录_tmp下, dbPath默认为 : /data/db

    M:
    queryset = table.aggregate([{
    '$group': {'_id': '$name'}}],
    {'allowDiskUse': true}
    )
    P:
    queryset = table.aggregate([{
    '$group': {'_id': '$name'}}],
    allowDiskUse=True, # 注意,这里语法稍有不一样
    )



索引




  • 创建索引:




  • 单键索引


    M: table.createIndex({'name':1})
    P: table.create_index([('name',-1)]) # -1代表逆序索引,注意是元组



  • 联合索引


    索引命中:最左匹配原则 eg 1,2,3 这三个创建联合索引, 可命中索引为:【1,12,123】
    M: table.createIndex( {'name':1}, {}, {} ) # 多个{}
    P: table.create_index([ ('name',-1), (), () ]) # 多个元组



  • 多键索引


    多键是针对于数组来讲的,创建单键的字段 指定为 数组字段, 默认就会设置为多键索引



  • 唯一索引 (unique)


    '''注意: 如果集合中,不同文档的字段有重复,创建唯一索引的时候会报错'''
    M: table.createIndex({'name':1}, {'unique':true})
    P: table.create_index([('name', 1),('counrty',1)], unique=True)



  • 稀疏索引 (sparse)


    eg:
    一个集合中:
    给 name创建 唯一索引
    插入文档1: 有 name字段
    插入文档2: 无 name字段 (MongoDB会在索引库中,把没有的字段的 索引设为 {字段:null} )
    再插入文档3, 无name字段 --> 同样也会把索引库中 name设为 null
    但是就在这个时候,刚要把索引库中的 name字段设为 null的时候。。。

    唯一索引告诉你:” 我这里已经有了一个,{ name:null },请你滚 ”
    然后就无情的给你报错了(重复索引字段)
    那咋整啊, 别急,稀疏索引就是给你办这事的

    设置稀疏索引。 MongoDB就不会把 没有的字段 加入到索引库了
    所以,索引库里面就不会自动添加 {字段: null}
    重新再次插入文档3, 无name字段, 可成功插入,不存在null的重复问题了
    M: table.createIndex({'name':1}, {'unique':true, 'sparse':true})
    P: table.create_index([('name', 1),('counrty',1)], unique=True, sparse=True)



  • 查询索引


    M:queryset = table.getIndexes()
    P: queryset = table.list_indexes()



  • 删除索引


    方式1:
    M: table.dropIndex('索引名') # 索引名可通过 上面查询索引的指令查
    P: table.drop_index('索引名')
    方式2:
    M: table.dropIndexes() # 删除全部,_id除外, 想指定删除多个,可用列表列出
    P: table.drop_indexes()



  • 查看索引性能(是否有效)


    table.上面说过的任一函数().explain() # 链式调用 explain,表示列出此操作的性能
    eg:
    M: queryset = table.explain().find({'name':'猫'})
    P: 同上
    结果中找到:
    queryPlanner -> winningPlan -> inputStage -> stage # stage结果对应说明如下
    COLLSCAN # 未优化,还是搜的整个集合
    IXSCAN # 索引起到作用

    索引对投影的优化:
    queryPlanner -> winningPlan -> stage # stage结果对应说明如下
    FETCH # 索引 对投影 未优化
    PROJECTION # 索引 对投影 起到优化作用

    索引对排序的优化:
    同上 stage 最好 不是 sort
    按索引 正序(逆序) 取数据, 这样就有效避免了机械排序的过程





推荐阅读
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • Python操作MySQL(pymysql模块)详解及示例代码
    本文介绍了使用Python操作MySQL数据库的方法,详细讲解了pymysql模块的安装和连接MySQL数据库的步骤,并提供了示例代码。内容涵盖了创建表、插入数据、查询数据等操作,帮助读者快速掌握Python操作MySQL的技巧。 ... [详细]
  • python中安装并使用redis相关的知识
    本文介绍了在python中安装并使用redis的相关知识,包括redis的数据缓存系统和支持的数据类型,以及在pycharm中安装redis模块和常用的字符串操作。 ... [详细]
  • 【爬虫训练场】:分页爬虫案例设计Demo,Python Flask与MySQL的完美连接
    网站类应用,一定离不开MySQL,所以本案例将带着大家学习一下,通过Flask调用MySQL数据,并实现分页呈现。类被称作蓝图,它是一个存储操作方法的容器,Flask可以通过Blu ... [详细]
  • 1.淘宝模拟登录2.天猫商品数据爬虫3.爬取淘宝我已购买的宝贝数据4.每天不同时间段通过微信发消息提醒女友5.爬取5K分辨率超清唯美壁纸6.爬取豆瓣排行榜电影数据(含GUI界面版) ... [详细]
  • 今天我们学习,数据库mongodb的使用,最下面有mongodb的下载链接。pipinstallpymongo首先安装pymongo,然后在需要用到的地方importpymongo ... [详细]
  • mongoDB高可用集群环境搭建
    2019独角兽企业重金招聘Python工程师标准在生产环境下,部署一台mongodb服务的话,会存在以下问题:单点问题生产环境是一个 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • Python已成为全球最受欢迎的编程语言之一,然而Python程序的安全运行存在一定的风险。本文介绍了Python程序安全运行需要满足的三个条件,即系统路径上的每个条目都处于安全的位置、"主脚本"所在的目录始终位于系统路径中、若python命令使用-c和-m选项,调用程序的目录也必须是安全的。同时,文章还提出了一些预防措施,如避免将下载文件夹作为当前工作目录、使用pip所在路径而不是直接使用python命令等。对于初学Python的读者来说,这些内容将有所帮助。 ... [详细]
  • 关于python调试大法的信息
    本文目录一览:1、pdbpython调试怎么用 ... [详细]
author-avatar
静卍谧梁言_250
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有