Search code examples
node.jsmongodbmongoosemongodb-querymongoose-schema

$addToSet array of object if object doesn't exist


I have those kind of documents:

{
  "_id": "accountId 1",
  "codes": [
    {
      "key": "advantageCode 1",
      "dateBegin": "01/02/2012",
      "dateEnd": "01/02/2013"
    },
    {
      "key": "advantageCode 2",
      "dateBegin": "01/02/2012",
      "dateEnd": "01/02/2013"
    }
  ]
}

I would like to add a code in codes if it doesn't exist. I have tried this:

bulkWrite([{updateOne: {
            filter: { _id: commands[0].accountId },
            update: {$addToSet: {codes: {key: commands[0].advantageCode, dateBegin: commands[0].dateBegin, dateEnd: commands[0].dateEnd}}},
            upsert: true
}}])

My problem is that the object is always added into the set, even if the key property already exists. It is normal (because it is another reference), but I would like to know how to rebase the $addToSet to the key of the objects.

I need the request to be a bulkwrite, because i gather many different requests.


Solution

  • Using update/aggregation ( since 4.2+) you can do something even better:

     db.collection.update({
      "_id": "accountId 1"
     },
     [
     {
      $set: {
      codes: {
        $cond: [
          {
            $in: [
              "advantageCode 2",
              "$codes.key"
            ]
          },
          {
            $map: {
              input: "$codes",
              in: {
                $mergeObjects: [
                  "$$this",
                  {
                    $cond: [
                      {
                        $eq: [
                          "$$this.key",
                          "advantageCode 2"
                        ]
                      },
                      {
                        "dateBegin": "01/02/2020",
                        "dateEnd": "01/02/2021"
                      },
                      {}
                    ]
                  }
                ]
              }
            }
          },
          {
            $concatArrays: [
              "$codes",
              [
                {
                  "key": "advantageCode 2",
                  "dateBegin": "01/02/2022",
                  "dateEnd": "01/02/2023"
                }
              ]
            ]
          }
        ]
      }
     }
     }
    ])
    

    Explained:

    Update dateBegin and dateEnd for codes.key that matches , or if there is no match add the new codes object to the set.

    Playground

    Afcourse , if you only want to $addToSet if key is missing then you can do:

    Playground2

    The bulkWrite operation will look something like this:

    bulkWrite([{updateOne: {
     filter: { _id: commands[0].accountId },
     update: [{ $set: { codes: { $cond: [ { $in: [commands[0].advantageCode,"$codes.key"]},{ $map: { input: "$codes",in: { $mergeObjects: [ "$$this", {}]}}},{$concatArrays: ["$codes",[{
            "key": commands[0].advantageCode , 
            "dateBegin": commands[0].dateBegin,
            "dateEnd": commands[0].dateEnd
              }
              ]
            ]
          }
        ]
       }
      }
     }
     ]
     }}])
    

    If you need to upsert document in case accountId is not found here is the option:

     db.collection.update({
     "_id": "accountId 2"
     },
     [
     {
      $set: {
      codes: {
        "$cond": [
          {
            $ne: [
              {
                $type: "$codes"
              },
              "missing"
            ]
          },
          {
            $cond: [
              {
                $in: [
                  "advantageCode 3",
                  "$codes.key"
                ]
              },
              {
                $map: {
                  input: "$codes",
                  in: {
                    $mergeObjects: [
                      "$$this",
                      {}
                    ]
                  }
                }
              },
              {
                $concatArrays: [
                  "$codes",
                  [
                    {
                      "key": "advantageCode 3",
                      "dateBegin": "01/02/2022",
                      "dateEnd": "01/02/2023"
                    }
                  ]
                ]
              }
            ]
          },
          {
            "key": "advantageCode 3",
            "dateBegin": "01/02/2022",
            "dateEnd": "01/02/2023"
          }
        ]
        }
       }
      }
      ],
      {
       upsert: true
     })
    

    Explained:

    1. In case accountId is not found insert new document.
    2. In case codes.key is not found insert new object with the new key to the codes array.

    Playground3