65.9K
CodeProject 正在变化。 阅读更多。
Home

MongoDB 中的聚合

starIconstarIconstarIconstarIconstarIcon

5.00/5 (12投票s)

2016年10月26日

CPOL

6分钟阅读

viewsIcon

39452

downloadIcon

247

在本文中,我们将重点介绍聚合管道。我会尝试使用简单的示例涵盖其中的每个主要部分。我们将编写 mongo shell 命令来执行聚合。

引言

在我上一篇关于 MongoDB 的文章中,我讲了 MongoDB 的介绍和 MongoDB 中的 CRUD 操作。所以是时候学习更多关于 MongoDB 的知识了,因此在本文中我将谈论 MongoDB 的另一个重要方面,即聚合管道。

聚合操作在任何类型的数据库中都非常重要,无论是 SQL 还是 NoSQL。为了执行聚合操作,MongoDB 将来自多个文档的值组合在一起,然后对分组数据执行各种操作以返回单个结果。SQL 使用聚合函数从列值中计算并返回单个值。

MongoDB 有三种执行聚合的方法:聚合管道、map-reduce 函数和单用途聚合方法

在本文中,我们将重点介绍聚合管道。我会尝试使用简单的示例涵盖其中的每个主要部分。我们将编写 mongo shell 命令来执行聚合。

聚合管道

MongoDB 的聚合框架基于数据处理管道的概念。聚合管道类似于 UNIX 世界的管道。最开始是集合,集合被逐个文档地处理,文档通过处理管道进行传递,它们经过一系列阶段,然后我们最终得到一个结果集。

在上图中,您可以看到集合通过不同的阶段进行处理,即 $project$match$group$sort,这些阶段可以出现多次。

管道中的各种阶段包括

  • $project – 选择、重塑数据
  • $match – 过滤数据
  • $group – 聚合数据
  • $sort – 对数据进行排序
  • $skip – 跳过数据
  • $limit – 限制数据
  • $unwind – 规范化数据

让我们尝试通过一个示例来可视化聚合。不用担心语法。我很快就会解释它。

 db.mycollection.aggregate([
	{$match:{'phone_type':'smart'}},
	{$group:{'_id':'$brand_name',total:{$sum:'$price'}}}
])


正如您在图中看到的,我们有一个集合,$match 阶段过滤掉文档,然后在管道的下一阶段,文档被分组,我们得到最终结果集。

准备虚拟数据

要运行 mongo shell 命令,我们需要一个数据库和一些虚拟记录,所以让我们创建我们的数据库和一个集合。

dept = ['IT', 'Sales', 'HR', 'Admin'];
for (i = 0; i < 10; i++) {
    
    db.mycollection.insert({ //mycollection is collection name
        '_id': i,
        'emp_code': 'emp_' + i,
        'dept_name': dept[Math.round(Math.random() * 3)],
        'experience': Math.round(Math.random() * 10),

    });

上面的命令将在 mydb 数据库的名为 mycollection 的集合中插入一些虚拟文档。

语法

db.mycollection.aggregate([
   {$match:{'phone_type':'smart'}},
   {$group:{'_id':'$brand_name',total:{$sum:'$price'}}}
])

语法非常简单,aggregate 函数接受一个数组作为参数,在数组中我们可以传递管道的各种阶段。

在上面的示例中,我们传递了两个管道阶段:$match,它将过滤记录;$group,它将分组记录并生成最终记录集。

管道阶段

1. $project

$project 阶段,我们可以添加一个键,删除一个键,重塑一个键。我们还可以对键使用一些简单的函数:$toUpper$toLower$add$multiply 等。

让我们使用 $project 来重塑我们创建的文档。

db.mycollection.aggregate([
    {
        $project:{
            _id:0,
            'department':{$toUpper:'$dept_name'},
            'new_experience':{$add:['$experience',1]}
        }
    }
])

在此聚合查询中,我们正在投影文档,_id:0 表示我们正在隐藏 _id 这个必需字段,使用之前的 dept_name 字段大写创建了一个名为 department 的新键。这里需要注意的是,字段‘dept_name’前加了‘$’符号,告诉 mongo shell 这个字段是文档的原始字段名。另一个新字段名为 new_experience,它是通过使用 $add 函数在之前的 experience 字段上加 1 来创建的。我们将得到如下输出

2. $match

它的作用与 SQL 中的‘where 子句’完全一样,用于过滤记录。我们可能想要匹配的原因是我们希望过滤结果,只聚合一部分文档,或者在进行分组后搜索结果集的特定部分。假设在我们的集合中,我们想聚合 department 等于 sales 的文档,查询将是

db.mycollection.aggregate([
    {
        $match:{
            dept_name:'Sales'
        }
    }
])

3. $group

顾名思义,$group 根据某个键对文档进行分组。假设我们想按 department 名称对 employees 进行分组,并想找出每个 department 中的 employees 数量。

db.mycollection.aggregate([
    {
        $group:{
            _id:'$dept_name',
            no_of_employees:{$sum:1}
        }
    }
])

在这里,_id 是分组的键,我创建了一个名为 no_of_employees 的新键,并使用 $sum 来查找每个组的总记录数。

让我们改进此查询以更合理地呈现输出。

db.mycollection.aggregate([
    {
        $group:{
            _id:{'department':'$dept_name'},
            no_of_employees:{$sum:1}
        }
    }
])

假设我们想对多个键进行分组,我们所要做的就是在 _id 字段中指定键的名称。

db.mycollection.aggregate([
    {
        $group:{
            _id:{'department':'$dept_name',
                'year_of_experience':'$experience'
            },
            no_of_employees:{$sum:1}
        }
    }
])

4. $sort

排序有助于您根据需要以升序或降序对聚合后的数据进行排序。假设我们想按升序对 department 名称进行分组,并找出 employees 的数量。

db.mycollection.aggregate([
    {
        $group:{
            _id:'$dept_name',
            no_of_employees:{$sum:1}
        }
    },
    {
        $sort:{
            _id:1
        }
    }

])

降序使用 -1。在这里,在 $sort 中,我使用 _id 字段,因为在聚合的第一个阶段,我使用 $dept_name 作为聚合的 _id

5. $skip 和 $limit

$skip$limit 的工作方式与我们执行简单 find 时跳过和限制的工作方式完全相同。除非我们先排序,否则跳过和限制没有意义,否则结果是未定义的。

我们先跳过记录,然后限制。

让我们看看相同的示例。

db.mycollection.aggregate([
    {
        $group:{
            _id:'$dept_name',
            no_of_employees:{$sum:1}
        }
    },
    {
        $sort:{
            _id:1
        }
    },
    {
        $skip:2
    },
    {
        $limit:1
    }

])

文档被分组,然后排序,之后,我们跳过了两个文档,并将文档限制为只有一个。

6. $first 和 $last

我们知道排序在聚合管道中是如何工作的,我们可以了解 $first$last。它们允许我们在聚合管道处理文档时获取每个组的第一个和最后一个值。

db.mycollection.aggregate([
    {
        $group:{
            _id:'$dept_name',
            no_of_employees:{$sum:1},
            first_record:{ $first:'$emp_code'}
        }
    }
])

7. $unwind

我们知道在 MongoDB 中,文档可以包含数组。对数组中的某些内容进行分组并不容易。$unwind 首先会解开数组数据,然后基本上以一种允许我们对其进行分组计算的方式重新组合它。

假设我们有如下文档

{
    a:somedata,
    b:someotherdata,
    c:[arr1,arr2,arr3]
}

在对‘c’进行 $unwind 后,我们将得到三个文档

{
    a:somedata,
    b:someotherdata,
    c:arr1
}
{
    a:somedata,
    b:someotherdata,
    c:arr2
}
{
    a:somedata,
    b:someotherdata,
    c:arr3
}

8. 聚合表达式

让我们看一些在 SQL 和 MongoDB 中都非常常见的表达式。我们有一个替代方案。

  1. $Sum:我们已经看到了它的示例。
  2. $avg:平均值的作用与求和类似,除了它计算每个组的平均值。
  3. $min:找出每个分组文档中的最小值。
  4. $max:找出每个分组文档中的最大值。

延伸阅读

以下是一些有用的链接,您可以从中进一步研究和学习 MongoDB 中的聚合。

结论

我没有解释聚合中的所有主题,但本文将帮助您在项目和学习中使用 MongoDB 聚合。我已附上 mongo shell 命令供您参考。

© . All rights reserved.