mongodb - 根据可能不存在的字段查询和排序mongo中的大量数据
问题描述
我对 mongo 比较陌生,我有一个看起来像这样的集合:
[
{
"stored": {
"box": [
{
"parcelId": "uwb1",
"status": "ACTIVE"
}
]
},
"checked": {
"box": [
{
"parcelId": "uwb1",
"status": "ACTIVE"
}
]
}
},
{
"stored": {
"box": [
{
"parcelId": "aqrf123",
"status": "PENDING"
}
]
},
"checked": {
"box": [
{
"parcelId": "aqrf123",
"status": "PENDING"
}
]
}
},
{
"checked": {
"box": [
{
"parcelId": "zuz873",
"status": "ACTIVE"
}
]
}
}
]
关于数据的一些观察:
- 文档将始终具有该
checked
字段,但可能没有该stored
字段 - 和字段具有相同
checked
的stored
架构 - 两者都将始终具有该
box
字段,我们可以假设该box
字段将始终在数组中具有 1 个元素(只有 1 个,而不是更多,而不是更少) - 此集合中的文档数量相对较高(约 1 亿)
我想要实现的是让文档按该status
字段排序,这就像一个枚举,它可以有 3 个值-ACTIVE
和.PENDING
REJECTED
- 如果对于文档,该
stored
字段存在,我将从那里获取并忽略该checked
字段。 - 否则,我将不得不将它从
checked
保证存在的字段中取出,如前所述。 - 一个重要的要求是将整个文档返回给消费者/客户端,所以我不能
projection
用来减少来自文档的数据量(这可能会使整个操作更快)。
我试图通过使用如下所示的聚合来实现这一点:
db.getCollection('entries')
.aggregate([{
$addFields: {
sortStatus: {
$ifNull: [{
$let: {
vars: {
box: {
$arrayElemAt: [
"$stored.box", 0
]
}
},
in: "$$box.status"
}
}, {
$let: {
vars: {
box: {
$arrayElemAt: [
"$checked.box", 0
]
}
},
in: "$$box.status"
}
}]
}
}
},
{
$sort: {
sortStatus: 1
}
}
], {
allowDiskUse: true
})
这似乎可以完成这项工作,但感觉很慢。还有allowDiskUse
一点让我有点不舒服。如果我忽略它,我会收到Sort exceeded memory limit of x bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in
错误消息。
所以我的问题是:
- 是否有更快的替代方案,无论是否有聚合?
allowDiskUse
在进行聚合时使用该选项是否有任何风险?- 改变一点文档结构并将该可排序字段添加到文档的根目录,为其添加索引并使用它会更好(或者它是“mongo”方式)
.sort({"statusField": 1})
吗?这将是最后的选择,因为我必须迁移现有数据。
解决方案
sortStatus
可以使用以下方法获得您的字段值:
{ $addFields: { sortStatus: { $ifNull: [ "$stored.box.status", "$checked.box.status" ] } } },
这会使查询更快吗?不,但代码更简单。
(1) 是否有更快的替代方案,无论是否有聚合?
我不知道,现在。
(2) 聚合时使用allowDiskUse选项有什么风险吗?
使用该allowDiskUse:true
选项意味着当此操作的内存 (RAM) 超过其限制时,排序操作会使用磁盘获取额外资源。与内存相比,磁盘 IO非常慢,因此“风险”是慢得多的排序操作。当排序操作需要的内存超过 100MB 的限制时,此选项变为必需(请参阅有关聚合中的排序和内存限制的文档)。
(3)改变一点文档结构并将可排序字段添加到文档的根目录,为其添加索引并使用 .sort({"statusField “:1})?这将是最后的选择,因为我必须迁移现有数据。
创建新的状态字段和在该字段上的索引意味着新的考虑:
- 创建一个新字段“状态”需要在编写文档时进行额外的计算(也可能在更新期间)。
- 在这个新字段上创建索引也是写入期间的额外开销。请注意,索引大小会随着文档数量的增加而变大。
这些会影响应用程序的写入性能。
但是,查询将变成一个简单的排序。在集合中有大量文档的情况下,用于排序的索引在操作期间可能适合内存,也可能不适合内存。如果没有一些实际试验,您无法确定此选项如何提供帮助。
这是有关索引策略的一些文档。
推荐阅读
- c# - wpf中的实时图表
- c# - 动态下拉组合框
- java - 是否可以使用 Stream API 替换不同集合上的嵌套循环
- c# - 进程无法访问该文件,因为它正在被另一个进程在第二次调用时使用
- php - Symfony4 SwitchUserSubscriber 不区分两个用户
- node.js - Angular在没有列表的情况下在firestore中添加新文档
- laravel - 为什么相关的 Answer 模型没有得到 project_id?
- corda - 为 Corda 节点登录创建 Web 应用程序
- c# - 创建一个 Linq 到实体 IQueryable 扩展以进行日期分组 (GroupBy)
- angular - Angular - 多个错误拦截器