mongodb - 在 MongoDB 中加入和分组
问题描述
数据库概述
我有两个集合 - 第一个是员工详细信息,第二个是他们在某些部门的成员资格。
Emps
收集样本(大小约为 331.5k 文档):
{
"_id" : ObjectId("5dc027718da295b969e529ae"),
"emp_no" : 10001,
...,
"gender" : "M",
"titles" : [
{
"title" : "Senior Engineer",
"dept_name" : "Development",
"from_date" : "1986-06-26",
"to_date" : "9999-01-01"
},
{
"title" : "Staff",
"dept_name" : "Human Resources",
"from_date" : "1986-06-26",
"to_date" : "9999-01-01"
}
]
}
departments
收集样本(大约 300k 个文档的大小):
{
"_id" : ObjectId("5dc026438da295b969e01893"),
"dept_no" : "d005",
"dept_name" : "Development",
"emp_no" : 10001,
"from_date" : "1986-06-26",
"to_date" : "9999-01-01"
},
{
"_id" : ObjectId("5dc026438da295b969e01894"),
"dept_no" : "d003",
"dept_name" : "Human Resources",
"emp_no" : 10001,
"from_date" : "1986-06-26",
"to_date" : "9999-01-01"
}
问题
现在我如何计算每个部门有多少员工在工作gender
?M
到目前为止,我想出了这个查询:
db.getCollection("emps").aggregate([
{$match: {'gender': 'F'}},
{$project: {_id: 0, emp_no: 1, gender: 1}},
{$lookup: {
from: 'departments',
localField: 'emp_no',
foreignField: 'emp_no',
as: 'departments',
}},
])
但它缺乏关键阶段,比如
to_date
as的情况"9999-01-01"
意味着该员工仍在部门工作,- 按部门名称分组
我希望查询尽可能高效,因此我尽量避免使用$unwind
它,因为它会生成更多文档。有没有其他方法可以在没有$unwind
阶段的情况下到达内部数组元素?
最后一件事-我看到可以在内部使用管道,$lookup
因此我可以通过在查找的文档上进行投影来摆脱我不感兴趣的字段,但是我没有设法正确地做到这一点我。如果你知道怎么做,请告诉我。
预先感谢您的任何帮助!
解决方案
从员工收集开始是无法避免$unwind
的,我仍然不会说这在管道效率方面是一个交易破坏者。
但是,通过从departments
集合开始管道,您可以避免它,同样启发式(假设公司很大并且已经存在一段时间),您可以通过首先匹配仍在公司工作的人来消除更多人(通过首先匹配to_date
部门集合)比你先匹配性别。(性别消除了 50%,而在职员工可能要低得多10%
)。您实际上可以自己计算分布并决定管道的哪种“方式”对您更有效。
我只是提出这一点,因为管道中最昂贵的阶段是$lookup
,这是对性能影响最大的动作。因此,越小$lookup
,整体性能越好。
这是我将如何做到这一点:
db.departments.aggregate([
{
$match: {
"to_date": "9999-01-01"
}
},
{
$lookup: {
from: "emps",
let: {
empNum: "$emp_no"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$gender",
"M"
]
},
{
$eq: [
"$$empNum",
"$emp_no"
]
}
]
}
}
}
],
as: "employees"
}
},
{
$match: {
"employees.0": {
$exists: true
}
}
},
{
$group: {
_id: "$dept_no",
count: {
$sum: 1
}
}
}
])
因为我觉得问题的核心更多是关于性能而不是功能,所以我将添加以下几个技巧:
您需要确保在部门集合中的字段和emps 字段中的/上都有
index
构建。to_date
gender
emp_no
考虑创建一个新
boolean
字段stillEmployedHere
,仅在当前员工的字段上更新,使用备用索引将比to_date
查询快得多。此技巧仅适用于大规模,否则会产生较小的影响。
推荐阅读
- android - Kotlin:Okhttpclient 创建单个实例的正确方法?OkHttpClient 不通过服务器 ip 检查?
- java - 为什么同步部分尚未完成时另一个线程可以访问被阻止的对象?
- phpstorm - 有没有办法告诉 PhpStorm 像对待普通用户一样使用不必要的用途?
- c# - 如何在 Magick.NET 中创建 8 bpp BMP?
- spring - 如何在解决重复依赖问题的同时选择兼容依赖?
- python - 使用 joblib 记录嵌套函数并行和延迟调用
- python - 如何在 tkinter 按钮小部件中获取 text 参数的值,然后将该值作为参数传递给同一个按钮?
- jsf - p:inputMask 如何以及为什么将 slotChar 作为值提交?
- css - 使用css背面属性时,角度材料输入字段不起作用
- c# - 如何伪造附加鼠标到虚拟 Windows 服务器?