Search code examples
node.jsmongodbmongoose

Mongo Mongoose - How to dynamically pick the value for $in operator inside a condition?


I am ierating through an object with key value pairs, where the value is an array of objects. If certain prop (lets say title) is already in the db, I do not want to save that object containing same title. When I hard code the title as a string, it works fine If I pass a map iteration to $in, nothing works (the objects are being saved in all cases then). What am I doing wrong?

 for (let key in reports) {
            const condition = reports[key].map((x) => {
                return x.title;
            });
            await Product.findOneAndUpdate({
                    "name": key
                },
                [
                    {
                        "$set": {
                            "reports": {
                                "$cond": [
                                    {
                                        "$in": [
                                            //this works as expected:
                                            "IAmProduct1",
                                            "$reports.title"

                                            //this does Not work when iterating:
                                            // condition,
                                            // "$reports.title"
                                        ]
                                    },
                                    "$reports",
                                    {
                                        "$concatArrays": [
                                            "$reports",
                                            reports[key]
                                        ]
                                    }
                                ]
                            }
                        }
                    }
                ], {new: true}) 
         }


Solution

  • Function reports[key].map((x) => { return x.title; }) (or just reports[key].map(x => x.title)) returns an array rather than a string:

    const reports = { key1: [{ title: "IAmProduct1" }] };
    
    print(reports['key1'].map(x => x.title));
    
    -> [ 'IAmProduct1' ]
    

    Which is not

    'IAmProduct1'
    

    Maybe $setUnion would be simpler:

    Product.findOneAndUpdate(
       { "name": key },
       [
          {
             $set: {
                reports: { $setUnion: ["$reports", reports[key]] }
             }
          }
       ]
    )
    

    Or put the condition in your filter, should be much more efficient:

    Product.findOneAndUpdate(
       { "name": key, "reports.title": {$ne: condition } },
       [
          {
             "$set": {
                "reports": {
                   "$concatArrays": [
                      "$reports",
                      reports[key]
                   ]
                }
             }
          }
       ],
       { new: true }
    )