Update or append to a subcollection in mongo

狂风中的少年 提交于 2021-02-05 08:17:26

问题


I have a collection containing a subcollection. In one request, I would like to update a record in the subcollection or append to it if a match doesn't exist. For a bonus point I would also like this update to be a merge rather than an overwrite.

A crude example:

// 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();

Currently I can achieve this result by pulling the record, and doing the update/append manually but I am assuming that achieving it with a pure mongo query would be much faster.

I have tried:

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

but this won't handle the append behaviour and also doesn't merge the object with the existing record, rather it overwrites it completely.


回答1:


I am not sure is there any straight way to do this in single query,

Update with aggregation pipeline starting from MongoDB v4.2,

  • $cond to check name is in subColl array,
  • true condition, need to merge with existing object, $map to iterate loop, check condition if matches condition then merge new data object with current object using $mergeObjects
  • false condition, need to concat arrays, current subColl array and new object using $concatArrays
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]] }
        ]
      }
    }
  }]
)

Playground



来源:https://stackoverflow.com/questions/65774158/update-or-append-to-a-subcollection-in-mongo

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!