skip to Main Content

Is it possible to use a document field to key into an external object in a MongoDB update using aggregation pipeline?

Here’s the idea with an object that has a single key, my actual use case will be an updateMany with an object with many ObjectIds as keys. Imagine there’s a MongoDB doc with an _id of ‘123456’:

const fooBarId = '123456'
const someObj = {}
someObj[fooBarId] = 'fooBar'

mongoApi.update(
   { _id: ObjectId(fooBarId) },
   [{ $set: { someField: someObj['$_id'] } }]
)

Currently, I see null as the value in the updated document when I’m testing this. I should see fooBar. If I can get this update working with one document, then I can apply the same logic to an updateMany where someObj has many keys of ObjectIds with values that I’d like to set the new field to individually for each.

2

Answers


  1. Chosen as BEST ANSWER

    Ended up going with this:

    const fooBarId = '123456'
    const someObj = {}
    someObj[fooBarId] = 'fooBar'
    
    mongoApi.update(
            { _id: ObjectId(fooBarId) },
            [
                {
                    $set: {
                        used: {
                            $let: {
                                vars: { id: { $toString: "$_id" }, extObj: someObj },
                                in: {
                                    $function: {
                                        body: `function(_id, obj) { return obj[_id] }`,
                                        args: [ "$$id", "$$extObj" ],
                                        lang: "js"
                                    }
                                }
                            }
                        }
                    }
                }
            ]
        )
    

    Needed aggregation to be performant since someObj could theoretically be 5000 keys in length.


  2. One option is to use $objectToArray:

    const someObj = {
                        "649d0bae47fa0b4db69f2cf6": 1,
                        "649d0bae47fa0b4db69f2cf8": 2
                    };
    db.collection.updateMany(
    {$expr: {$in: [{$toString: "$_id"}, Object.keys(someObj)]}},
    [
      {$set: {
          someField: {$getField: {
              input: {$first: {$filter: {
                    input: {$objectToArray: someObj},
                    cond: {$eq: ["$$this.k", {$toString: "$_id"}]}
              }}},
              field: "v"
          }}
      }}
    ])
    

    See how it works on the playground example

    Another option is to use bulkUpdate for such cases.

    EDIT:
    For mongodb versions older than 5.0, you can use this:

    const someObj = {
                        "649d0bae47fa0b4db69f2cf6": 1,
                        "649d0bae47fa0b4db69f2cf8": 2
                    };
    db.collection.updateMany(
    {$expr: {$in: [{$toString: "$_id"}, Object.keys(someObj)]}},
    [
      {$set: {someField: {
            $first: {$reduce: {
                input: {$objectToArray: someObj},
                initialValue: [],
                in: {$concatArrays: [
                    "$$value",
                    {$cond: [
                        {$eq: ["$$this.k", {$toString: "$_id"}]},
                        ["$$this.v"],
                        []
                    ]}
                ]}
            }}
      }}}
    ])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search