Mongodb 更新或附加到mongo中的子集合

Mongodb 更新或附加到mongo中的子集合,mongodb,mongoose,Mongodb,Mongoose,我有一个包含子集合的集合。在一个请求中,我想更新子集合中的一条记录,如果不存在匹配项,则将其追加。对于一个额外的点,我也希望这个更新是一个合并,而不是一个覆盖 一个粗略的例子: // Schema { subColl: [ { name: String, value: Number, other: Number, }, ]; } // Existing record {

我有一个包含子集合的集合。在一个请求中,我想更新子集合中的一条记录,如果不存在匹配项,则将其追加。对于一个额外的点,我也希望这个更新是一个合并,而不是一个覆盖

一个粗略的例子:

// Schema
{
    subColl: [
        {
            name: String,
            value: Number,
            other: Number,
        },
    ];
}

// Existing record
{
    _id : 123,
    subColl: [
        {name: 'John',
        value: 10,
        other: 20}
    ]
}

// example
const update = { _id: 123, name: 'John', other: 1000 };
const { _id, name, other } = update;
const doc = await Schema.findById(_id);
const idx = doc.subColl.findIndex(({ name: nameInDoc }) => nameInDoc === name);
if (idx >= 0) {
    doc.subColl[idx] = { ...doc.subColl[idx], other };
} else {
    doc.subColl.push({ name, other });
}
doc.save();

目前,我可以通过拉取记录并手动执行更新/追加来实现这个结果,但我假设使用纯mongo查询来实现这个结果会快得多

我试过:

Schema.findOneAndUpdate(
    { _id: 123, 'subColl.name': 'John' },
    { $set: { 'subColl.$': [{ name: 'John', other: 1000 }] } }
)

但这不会处理追加行为,也不会将对象与现有记录合并,而是完全覆盖它。

我不确定在单个查询中是否有直接的方法来实现这一点

从MongoDB v4.2开始,
  • $cond
    检查名称是否在
    子卷
    数组中
  • 如果条件为true,则需要与现有对象合并,
    $map
    要迭代循环,请检查条件是否匹配,然后使用
    $mergeObjects
    将新数据对象与当前对象合并
  • 错误条件,需要使用
    $concatarray
    concat数组、当前
    subColl
    数组和新对象

谢谢你的回答。它在您的游乐场中按预期工作,但将名称转换为我的应用程序中实际使用的名称后,我得到的唯一行为是将数组设置为长度1,并将空对象作为唯一元素。我必须从$set对象外部移除外部的方括号以避免出现错误,所以我想知道这是否是导致错误的原因?(mongoose v5.11.11)同样,您能否建议如何单独调试此类嵌套聚合操作?我没有这方面的经验!聚合管道中需要大括号[],您会遇到错误,因为您的mongodb版本低于4.2,我在回答中提到,此功能从mongodb 4.2开始,您能确认您的mongodb安装版本吗?您能建议如何调试=>您可以在操场进行调试,并从中了解开始的操作员
$
的操作,如果您没有经验,我建议您从mongodb文档中学习一些基本查询和聚合管道。您的需求很复杂,我认为不需要任何其他方法来简单查询。或者你可以做两个查询,首先查找然后更新。啊,抱歉,你是对的。Mongo仅为v3.0.15。我会升级它,看看它是否解决。现在答案被接受,我可以确认它是否有效。可悲的是,没有我所希望的那么快!
const _id = 123;
const update = { name: 'John', other: 1000 };

Schema.findOneAndUpdate(
  { _id: _id },
  [{
    $set: {
      subColl: {
        $cond: [
          { $in: [update.name, "$subColl.name"] },
          {
            $map: {
              input: "$subColl",
              in: {
                $cond: [
                  { $eq: ["$$this.name", update.name] },
                  { $mergeObjects: ["$$this", update] },
                  "$$this"
                ]
              }
            }
          },
          { $concatArrays: ["$subColl", [update]] }
        ]
      }
    }
  }]
)