javascript

mongodb

mongodb-query

mongoose

How to add multiple objects to array based on key?

I need to add multiple objects in one query, check if each object key doesn't exist or duplicate, else add object. (label can be duplicate)

Schema

new Schema({
      additional: [
        {
          key: { type: String, required: true, unique: true },
          label: { type: String, required: true }
        }
      ]
})

request payload:

[ {key: "city", label: "CITY"}, {key: "gender", label: "GENDER"}
, {key: "city" ,label: "CITY1"}, {key: "city2", label: "CITY"}]

Expected results:

[
 {key: "city", label: "CITY"},
 {key: "gender", label: "GENDER"},
 {key: "city2", label: "CITY"}
]

I tried to find solutions but couldn't find any.

Solution 1

You can try using bulkWrite operation in mongodb

Suppose you have following payload to update

const payload = [
  { key: "city", label: "CITY" }, { key: "gender", label: "GENDER" },
  { key: "city", label: "CITY1" }, { key: "city2", label: "CITY" }
]

Query to update documents in bulk

Model.bulkWrite(
  payload.map((data) => 
    ({
      updateOne: {
        filter: { '_id': 'xxxx', 'additional.key' : { $ne: data.key } },
        update: { $push: { additional: data } }
      }
    })
  )
})

Which will send a request in bulk to update like this

bulkWrite([
  { updateOne: { filter: { '_id': 'xxxx', 'additional.key' : { $ne: data.key } }, update: { $push: { additional: data } } } },
  { updateOne: { filter: { '_id': 'xxxx', 'additional.key' : { $ne: data.key } }, update: { $push: { additional: data } } } }
])

Solution 2

One way to do it since mongoDB version 4.4 is using update with aggregation pipeline with a $merge step:

  1. $addFields with data to add and create a new keys field that contains the existing keys.
  2. $filter the data to include only items with keys that are not in the original array called 'additional'.
  3. $concatArrays: 'additional' and the filtered data array.
db.collection.aggregate({_id: 'xxxx'}, [
  {
    $addFields: {
      data: [{key: "city", label: "CITY"}, {key: "city2", label: "CITY"}],
      keys: "$additional.key"
    }
  },
  {
    $set: {
      data: {
        $filter: {
          input: "$data",
          as: "item",
          cond: {$not: {$in: ["$$item.key", "$keys"]}}
        }
      }
    }
  },
  {
    $set: {
      additional: {$concatArrays: ["$additional", "$data"]},
      data: "$$REMOVE", keys: "$$REMOVE"
    }
  }
])

Playground example