• MongoDB常用脚本汇总


    概述

    本文汇总记录日常工作中常用的MongoDB查询脚本。

    本文使用的MongoDB版本为4.2.13:db.version();

    实战

    新增

    新增集合:

    db.getSiblingDB("corpus").createCollection('message');
    
    • 1

    删除

    删除一条数据:

    db.getSiblingDB("cx_user").userAccount.deleteOne({_id: ObjectId('628720aa454b9b0008ca218f')});
    
    • 1

    批量删除多条数据:

    db.getSiblingDB("cx_user").userAccount.deleteMany({_id: {$in: [ObjectId('645af98020506e0008f61268'), ObjectId('6467100e3743bf00088c18c9')]}})
    
    • 1

    删除集合:

    db.getSiblingDB("cx_user").getCollection("msg").drop();
    
    • 1

    getSiblingDB指定数据库后,可以使用getCollection定位到具体的集合,也可以直接使用.指定集合。

    更新

    通过$set唯一更新未指定的新数据:

    db.getSiblingDB("corpus").getCollection("risk_control").updateOne({_id: new ObjectId("6502be1b36b36e0008e7888e")}, {"$set": {mobile: "189*****725"}})
    
    • 1

    $set指定查询条件后批量更新:

    db.getSiblingDB("corpus").getCollection("risk_control").updateMany({'channel': '77'}, {"$set": {'operator': "johnny"}})
    
    • 1

    指定多个查询条件:

    db.getSiblingDB("corpus").getCollection("risk_control").updateMany({'channel': '77', 'type': 1}, {"$set": {'operator': "johnny"}})
    
    • 1

    查询

    考虑到工作中绝大多数场景都是在查询数据,调整一下标题层级。

    版本

    查询Server版本:

    db.version();
    
    • 1

    distinct

    distinct查询:

    db.getSiblingDB("medhub").getCollection("case").distinct('finished');
    db.getSiblingDB("cx_user").userAccount.distinct('profiles.channel');
    
    • 1
    • 2

    where

    官方文档,可以在$where里直接给出String表达式,或JS函数,可使用thisobj来引用文档。

    查询userAccount集合里source字段和channel字段不相同的数据:

    db.getSiblingDB("cx_user").getCollection("userAccount")
    .find({source: {$exists: true}, channel: {$nin:['pdwjk_screen','xhwjk_screen','232']}, $where: 'this.source != obj.channel'})
    .sort({registerTime: -1}).limit(1);
    
    • 1
    • 2
    • 3

    如果是4.4以后的版本:

    For a mongos instance, see security.javascriptEnabled configuration option or the --noscripting command-line option starting in MongoDB 4.4.

    In earlier versions, MongoDB does not allow JavaScript execution on mongos instances.

    还可以查询JS函数:

    db.getSiblingDB("cx_user").getCollection("userAccount").findOne({$where: function() {return (this.mobile == "1WGCkMgePvun0Bhc1Jjz5Q==")}});
    
    • 1

    MongoDB 4.4以前的版本,执行报错Query failed with error code 139 with name 'JSInterpreterFailure' and error message 'SyntaxError: unexpected token: keyword 'function'' on server。参考mongo-jsinterpreter-syntaxerror-unexpected-token-keyword-function

    表达式

    $expr表达式查询。

    需求,查询某个字段长度为18的所有记录。

    在DataGrip的filter输入框里输入:{$expr: {$eq: [{$strLenCP: "$profileKey"}, 18]}},即等价于如下查询语句:

    db.getSiblingDB("medhub").getCollection("userCase")
              .find({$expr: {$eq: [{$strLenCP: "$profileKey"}, 18]}})
    
    • 1
    • 2

    参考string-field-value-length-in-mongodb

    exists

    查询某集合下某个JSON Array字段满足Array个数大于等于5的数据:

    db.getSiblingDB("cx_user").userAccount.find({
    	'profiles.5': {$exists: true}
    });
    
    • 1
    • 2
    • 3

    注:profiles是一个JSON Array字段
    参考stackoverflow——query-for-documents-where-array-size-is-greater-than-1

    findOne

    sort

    Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting.

    模糊

    countDocuments

    非常常用的方法,必须要掌握的入门API。

    使用countDocuments查询符合某一个限制条件的集合总数:

    db.getSiblingDB("medhub").getCollection("report").countDocuments({
    	'translatedReports.zh-CN.profileKey': '64abd052b99c8f0008338990'
    });
    
    • 1
    • 2
    • 3

    注:上面的translatedReports字段是一个大JSON列,通过.连字符以类似于JsonPath的方式取JSON里的Key字段。

    使用countDocuments查询符合多个限制条件的集合总数:

    db.getSiblingDB("medhub").getCollection("report").countDocuments({
    	'translatedReports.zh-CN.profileKey': '64abd052b99c8f0008338990',
    	'isDelete': false
    });
    
    • 1
    • 2
    • 3
    • 4

    使用countDocuments模糊查询符合限制条件的集合总数:

    db.getSiblingDB("medhub").getCollection("report").countDocuments({
    	'translatedReports.zh-CN.conditionReports.key': /::finding/,
    });
    
    • 1
    • 2
    • 3

    注:使用/关键词字段/表示模糊查询,支持中英文字符。后面介绍的$match也支持模糊查询。

    MongoDB不是传统的关系型数据库。如果是传统的关系型数据库,如MySQL,新增业务场景或需求调整,则往往需要在应用层PO里新增一个字段,对应的数据库也必须新增一个字段。如果应用新增字段后,生产环境的数据库未同步新增字段,则大概率会出现问题。这也就是我们所熟悉的数据库发布系统。

    使用MongoDB,则无此担扰。应用发布后触发特定业务逻辑时,数据库会自动新增对应的字段。

    使用countDocuments查询不存在某字段的数据总数:

    db.getSiblingDB("medhub").getCollection("case").countDocuments({
    	'alias': null,
    });
    
    • 1
    • 2
    • 3

    注:上述查询表示早期业务里并没有alias字段,后面业务才有此字段。等价于:

    db.getSiblingDB("medhub").getCollection("case").countDocuments({
    	'alias': {$exists: 0},
    });
    
    • 1
    • 2
    • 3

    aggregate

    聚合查询,功能非常强大,学习门槛稍微有点高。

    不带任何限制条件查询某个集合的总数:

    db.getSiblingDB("sms").sms_history.aggregate([{$count: "total"}]);
    
    • 1

    aggregate使用$project查询指定字段:

    db.getSiblingDB("corpus").getCollection("mds_findings").aggregate([
    	{$project: {_id: 0, commonName: 1, key:1, }},
    ]);
    
    • 1
    • 2
    • 3

    注:1表示查询某字段,0表示不查询某字段,_id: 0,,默认会查询主键_id字段,如果不想查询此字段,则需要显式设置为0。

    aggregate使用$project查询指定字段,并加上$match过滤符合条件的数据:

    db.getSiblingDB("corpus").getCollection("mds_findings").aggregate([
    	{$project: {_id: 0, key:1, minAge: 1, maxAge: 1}},
        {
          $match: {
            minAge: {$lt: 378691200,},
            maxAge: {$gt: 378691200,}
          },
        },
    ]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    aggregate使用$match过滤符合条件,使用$count查询总数:

    db.getSiblingDB("corpus").getCollection("mds_findings").aggregate([
    //  {$project: {_id: 0, commonName: 1, key:1, minAge:1, maxAge:1}},
        {
          $match: {
            minAge: {$lt: 378691200,},
            maxAge: {$gt: 378691200,}
          },
        },
        {$count: "total"}
    ]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注:$count优先级大于同级$project,也就是如果两者并列时,只会得到符合条件的总数,$project不生效,并不会给指定的字段。

    JOIN

    关系型数据库中非常常见的查询类型。在非关系型数据库MongoDB里也会经常遇到。

    现有两个集合userAccount以及unionUserAccount,都有一个outUserId字段。现在需要join查询某个用户同时在两个集合里存在。MongoDB初学者想要写出MongoDB查询脚本还是有一定门槛的。

    所以此时需要用好工具,博主鄙人是一个不折不扣的工具拥趸,从我的博客专栏Tool可见一斑。工欲善其事,必先利其器。

    好在我使用DataGrip,有限支持以MySQL语法查询MongoDB!!!

    之所以说是有限支持:

    1. 不是所有场景都可以使用MySQL类似语法
    2. 有时候查询结果会有问题(虽然执行不报错)

    上面的需求很容易就可以写出查询SQL:

    select a.outUserId from cx_user.userAccount a left join cx_user.unionUserAccount b on a.outUserId = b.outUserId;
    
    • 1

    给出一大堆查询结果,以第一条数据aaaaa来验证,发现被join查询的集合unionUserAccount里并没有outUserId等于aaaaa的数据。

    事实上执行:

    select count(*) from cx_user.userAccount a left join cx_user.unionUserAccount b on a.outUserId = b.outUserId;
    
    • 1

    给出79887条数据。根据业务常识来判断,肯定没有这么多数据。进一步执行:

    select count(*) from cx_user.userAccount a left join cx_user.unionUserAccount b on a.outUserId = b.outUserId where a.channel = 'tong_app';
    
    • 1

    数据量还是不对。进一步执行:

    select count(*) from cx_user.userAccount a left join cx_user.unionUserAccount b on a.outUserId = b.outUserId where a.channel = 'tong_app' and b.channel = 'tong_app';
    
    • 1

    数据量级差不多。

    所以说【有限支持】。如果不会使用MongoDB语法,可以考虑使用MySQL语法,但是一定要验证一下!!

    这就是完了吗?不!

    DataGrip的强大之处在于它会将上面的MySQL脚步转换为MongoDB语法脚本,这样我们可以学习怎么来写MongoDB脚本!

    上面最后一条SQL在DataGrip Output里打印如下:

    db.getSiblingDB("cx_user").getCollection("userAccount").aggregate([
    {
     $project: {"a": "$$ROOT", "_id": 0}
    },
    {
     $lookup: {
       localField: "a.outUserId",
       from: "unionUserAccount",
       foreignField: "outUserId",
       as: "b"
     }
    },
    {
     $unwind: {
       path: "$b",
       preserveNullAndEmptyArrays: true
     }
    },
    {
     $match: {$and: [{"a.channel": {$eq: 'tong_app'}}, {"b.channel": {$eq: 'tong_app'}}]}
    },
    {
     $project: {"outUserId": "$a.outUserId", "_id": 0}
    }
    ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    注意看上面有个preserveNullAndEmptyArrays关键词。

    我使用的DataGrip版本:

    DataGrip 2022.1.5
    Build #DB-221.5787.39, built on June 6, 2022
    Licensed to Hunan Institute of Science and Technology / henu itstaff
    Subscription is active until February 9, 2024.
    Runtime version: 11.0.15+10-b2043.56 aarch64
    VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
    macOS 13.0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

        参考

      • 相关阅读:
        MySql字符串拆分实现split功能(字段分割转列、转行)
        Flink Table API & SQL
        SPRINGBOOT03_自动配置原理入门、Lombok、dev-tools、快速初始化boot项目
        538页21万字数字政府大数据云平台项目建设方案
        Python爬虫抓取经过JS加密的API数据的实现步骤
        zookeeper源码(09)follower处理客户端请求
        计算机网络自顶向下实例
        使用Kubernetes部署Kubernetes集群
        Linux拔网线后网卡仍然处于激活状态
        异常
      • 原文地址:https://blog.csdn.net/lonelymanontheway/article/details/122441489