arrays - MongoDB 使用来自另一个集合的查询结果更新集合中的所有记录
问题描述
我有大约 40k 条记录要更新,每条记录都从查询另一个集合中获取数据。
我有一个现有的查询来执行此操作,但它运行了一个多小时。它通常断开连接,然后我再次运行它。
我认为有更好的方法可以做到这一点,我只是 mongodb 的菜鸟,这个解决方案有效,但我对执行速度不满意。
也许您有更好或更快的解决方案。
为了更好地说明数据,请参见下图:
帐户
[
{
"_id": ObjectId("AC101"),
"emails":null,
"name":"Account 101",
...
},
{
"_id": ObjectId("AC102"),
"emails":null,
"name":"Account 102",
...
},
{
"_id": ObjectId("AC103"),
"emails":null,
"name":"Account 103",
...
},
...
]
account_contacts
[
{
"_id": Object("ACC001"),
"account": {
"$ref" : "account",
"$id" : ObjectId("AC101")
},
"email":"acc001@test.com",
"name":"Contact 001",
...
},
{
"_id": Object("ACC002"),
"account": {
"$ref" : "account",
"$id" : ObjectId("AC102")
},
"email":"acc002@test.com",
"name":"Contact 002",
...
},
{
"_id": Object("ACC003"),
"account": {
"$ref" : "account",
"$id" : ObjectId("AC103")
},
"email":"acc003@test.com",
"name":"Contact 003",
...
},
{
"_id": Object("ACC004"),
"account": {
"$ref" : "account",
"$id" : ObjectId("AC103")
},
"email":"acc004@test.com",
"name":"Contact 004",
...
},
{
"_id": Object("ACC005"),
"account": {
"$ref" : "account",
"$id" : ObjectId("AC103")
},
"email":"acc005@test.com",
"name":"Contact 005",
...
},
...
]
询问:
db.getCollection('accounts').find({ 'emails':{ $eq:null } }).forEach(p => {
const emails = [];
db.getCollection('account_contacts').find({"account.$id": p._id}).forEach(c => {
emails.push(c.email);
});
db.getCollection('accounts').updateOne({"_id": p._id}, {$set: {"emails": emails}});
});
我有一个过滤器来仅获取具有 null 的帐户emails
,因此如果它收到超时错误(1 小时)...我只是重新运行脚本,它将处理那些具有 null 电子邮件的帐户。
目前,我对如何改进查询一无所知......但我知道这不是这种情况的最佳解决方案,因为它需要一个多小时。
更新:
虽然我仍然无法使聚合/查找方法起作用,但我确实尝试在 mongo 控制台中运行旧脚本,我之前运行过它并在我的 ID 中执行了一个多小时......如果你直接在 mongo 控制台中运行它,它只需要 12-14 分钟,这还不错。
这就是我现在所做的,但我仍然想将我的脚本转换为使用聚合。
TIA
解决方案
使用 MongoDB 4.2,如果您愿意使用临时集合,则可以避免将文档拉到客户端。
使用聚合来匹配所有带有空电子邮件的文档,仅提取 _id 并将其存储在临时集合中。请注意,如果您有一个索引,{emails:1, _id:1}
它将简化这部分。您可能希望以程序方式生成临时集合名称,这样它就不会为连续运行使用相同的名称。
db.accounts.aggregate([
{$match: {emails: null}},
{$project: {_id: 1}},
{$out: "temporary_null_email_collection"}
])
然后聚合临时集合,从 account_contacts 集合中查找电子邮件,删除任何无关字段,并将结果与 accounts 集合合并回来。
db.temporary_null_email_collection.aggregate([
{$lookup:{
from: "account_contacts",
localField: "_id",
foreignField: "$id", // verify this field name is correct
as: contacts
}},
{$project: {
_id: 1,
emails: "$contacts.emails"
}},
{$merge: "accounts"}
])
推荐阅读
- c++ - 如何调用存储在基类中并由继承该基类两次的类调用的虚拟方法?
- java - Java 内部类内存泄漏
- reactjs - 如何在更新 element.subscribersCount 上添加动画?
- java - JDBC 要求 Sybase 的时间戳偏移一个小时?
- python - 我无法在 for 循环中的列表中增加特定值
- java - 如何使 JTextField 与窗口一样宽
- dialogflow-es - Webhook 订阅已被禁用,我无法重新开启
- c - 使用 Qt Creator 时用于 C(非 C++)程序的 printf(不使用任何类型的 Qt 框架)
- jquery - 无法识别的表达式:选择具有多个类的元素
- asp.net-core - 电子邮件确认 - 奇怪的行为