MongoDB 聚合操作、子文档操作、AND与OR操作、性能优化

正则匹配

比如要查询包含 /^.*怒江.*$/ 的字段可以这样查询
.find({'name': /^.*怒江.*$/})

聚合查询

聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最大值、最小值、平均值,求和等操作。聚合操作还能够对记录进行复杂的操作,主要用于数理统计和数据挖掘。在 MongoDB 中,聚合操作的输入是集合中的文档,输出可以是一个文档,也可以是多条文档。

三种方式如下

  • 聚合管道
  • 单目聚合
  • MapReduce编程模型

聚合管道

简介

MongoDB 2.2 版本之后的功能,其作用是对文档进行过滤查询出复合条件的文档,并且对文档进行转换,改变文档的输出形式。

构建管道的语法如下
db.COLLECTION_NAME.aggregate([{<stage>},...])

解释: $match 用于获取 status = "A" 的记录,然后将符合条件的记录送到下一阶段 $group 中进行分组求和计算,最后返回 Results。其中,$match$group 都是阶段操作符,而阶段 $group 中用到的 $sum 是表达式操作符。

阶段操作符

语法作用
$project修改文档的结构,可以用来重命名、增加或删除文档中的字段
$match用于过滤文档。用法类似于 find() 方法中的参数
$group将集合中的文档进行分组,可用于统计结果
$sort将集合中的文档进行排序
$limit限制返回的文档数量
$skip跳过指定数量的文档,并返回余下的文档
$unwind将文档中数组类型的字段拆分成多条,每条文档包含数组中的一个值

$project

修改文档结构,可以用来重命名、增删文档中字段等操作。

只返回 title 和 author 字段
_id: 0 来禁止显示 _id

db.article.aggregate([{$project:{_id:0, title:1, author:1 }}])

pages字段值加 10 并重命名

db.article.aggregate(
   [
      {
          $project:{
               _id:0,
               title:1,
               author:1,
               newPages: {$add:["$Pages",10]}
         }
      }
   ]
)

$match

过滤文档,用法类似find()
  • $match 中不能使用 $where 表达式操作符
  • 如果 $match 位于管道的第一个阶段,可以利用索引来提高查询效率
  • $match 中使用 $text 操作符的话,只能位于管道的第一阶段
  • $match 尽量出现在管道的最前面,过滤出需要的数据,在后续的阶段中可以提高效率。

查询文档 pages 字段大于 5 的

db.article.aggregate(
    [
         {
              $match: {"pages": {$gte: 5}}
         }
    ]
   ).pretty()

$group

分组, 类似 mysql 的 group。

从 article 中得到每个 author 的文章数,并输入 author 和对应的文章数

db.article.aggregate(
    [
         {
              $group: {_id: "$author", total: {$sum: 1}}
         }
    ]
   )

$sort

文档进行排序。升序 1, 倒序 -1

让集合 article 以 pages 升序排列

db.article.aggregate([{$sort: {"pages": 1}}]).pretty()

$limit

限制返回的文档数量

返回集合 article 中前两条文档

db.article.aggregate([{$limit: 2}]).pretty()

$skip

跳过指定数量的文档,并返回余下的文档。

跳过集合 article 中一条文档,输出剩下的文档

db.article.aggregate([{$skip: 1}]).pretty()

$unwind

将文档中数组类型的字段拆分成多条,每条文档包含数组中的一个值。
  • $unwind 所作的修改,只用于输出,不能改变原文档
  • $unwind 参数数组字段为空或不存在时,待处理的文档将会被忽略,该文档将不会有任何输出
  • $unwind 参数不是一个数组类型时,将会抛出异常

把集合 article 中 title="MongoDB Aggregate" 的 tags 字段拆分

db.article.aggregate(
    [
         {
              $match: {"title": "MongoDB Aggregate"}
         },
         {
              $unwind: "$tags"
         }
    ]
   ).pretty()

表达式操作符

表达式操作符有很多操作类型,其中最常用的有布尔管道聚合操作、集合操作、比较聚合操作、算术聚合操作、字符串聚合操作、数组聚合操作、日期聚合操作、条件聚合操作、数据类型聚合操作等。每种类型都有很多用法举例一些常用的。
  • 布尔聚合
  • 集合聚合
  • 比较性聚合
  • 算数聚合
  • 字符串聚合
  • 时间聚合
  • 数组聚合
  • 条件聚合
  • 数据类型聚合
类型语法
布尔$and
布尔$or
布尔$not
集合操作$setEquals
集合操作$setIntersection
集合操作$setUnion
集合操作$setDifference
集合操作$setIsSubset
集合操作$anyElementTrue
集合操作$allElementsTrue
比较$cmp
比较$eq
比较$gt
比较$gte
比较$lt
比较$lte
比较$ne
比较$ne
算术聚合$abs
算术聚合$add
算术聚合$ceil
算术聚合$divide
算术聚合$floor
算术聚合$ln
算术聚合$log
算术聚合$log10
字符串聚合$concat
字符串聚合$split

聚合管道优化

  • $match$sort 放到管道的前面,可以给集合建立索引,来提高处理数据的效率。
  • 可以用 $match$limit$skip 对文档进行提前过滤,以减少后续处理文档的数量。
  • $sort + $match 顺序优化 如果 $match 出现在 $sort 之后,优化器会自动把 $match 放到 $sort 前面

聚合管道限制

对聚合管道的限制主要是对 返回结果大小 和 内存 的限制。

返回结果大小

聚合结果返回的是一个文档,不能超过 16M,从 MongoDB 2.6版本以后,返回的结果可以是一个游标或者存储到集合中,返回的结果不受 16M 的限制。

内存

聚合管道的每个阶段最多只能用 100M 的内存,如果超过100M,会报错,如果需要处理大数据,可以使用 allowDiskUse 选项,存储到磁盘上。

AND与OR操作

AND

AND有2种写法
  1. 也就是我们平时用的隐形写法

    db.getCollection('play_info').find({'字段1': '值1','字段2':'值2'})
  2. 显示AND方法

    db.getCollection('play_info').find(
        {'$and': [{'字段1': '值1'},{'字段2':'值2'}]}
    )
  3. 必须使用显示AND的情况

    在嵌套or的时候

    db.getCollection('play_info').find(
        '$and':[
            {'$or': [{'字段1': '值1'},{'字段2':'值2'}]},
            {'$or': [{'字段1': '值1'},{'字段2':'值2'}]},
        ]
    )

OR

OR 与显示AND的方法完全相同
db.getCollection('play_info').find(
    {'$or': [{'字段1': '值1'},{'字段2':'值2'}]}
)

子文档操作

Mongodb是Nosql,可以在字段中嵌套字段

例如 {'name': '小张', '爱好': {'运动':'篮球','文艺':'唱歌'}}

查找子文档数据

在查找子文档时需要使用 嵌入式文档名.嵌套字段名

案例

db.getCollection('play_info').find({'字段1.嵌套字段': '值1'})

返回子文档数据

使用 $project来提取数据

数组字段

例如 {'name': '小张', '爱好': ['篮球','唱歌']}

查找保护篮球的爱好

db.getCollection('play_info').find({'爱好': '篮球'})

查找不包含篮球的爱好,在其中运用上$ne

db.getCollection('play_info').find(
    {'爱好': {'$ne':'篮球'}}
)

数组更新

经常遇到,需要更新数组或者追加值进去

往数组追加单个值:

coll.update_one(
    {'pid': pid},
    {"$push": {list_key: value}}  # 更新数组
)

批量追加多个:

coll.update_one(
    {'pid': pid},
    {"$push": {list_key: {"$each": [1,2,3,4]}}}  # 更新数组
)
Last modification:March 18, 2021
如果觉得我的文章对你有用,请随意赞赏