skip to Main Content

Lets say I have records like:

{
    "title": "something",
    "tags": [
        {
            "name": "tag1",
            "value": "something"
        },
        {
            "name": "tag2",
            "value": "anything"
        },
        {
            "name": "tag3",
            "value": "whatever"
        }
    ]
}

I want to update tags based on tags.name. I know I can use arrayFilters. However, what if I wish to bulk update multiple records of objects in the array? Suppose I want to "upsert" 2 tags

[
    { 
        "name": "tag2", 
        "value": "updated" 
    },
    { 
        "name": "tag-new", 
        "value": "new tag" 
    }
]

Can I do this merge in one operation such that I get something like:

{
    "title": "something",
    "tags": [
        {
            "name": "tag1",
            "value": "something"
        },
        {
            "name": "tag2",
            "value": "updated"
        },
        {
            "name": "tag3",
            "value": "whatever"
        },
        {
            "name": "tag-new",
            "value": "new tag"
        }
    ]
}

How can I acheive this is the best way?

2

Answers


  1. Chosen as BEST ANSWER

    Managed to achive that using

    const upsertTags = [
        {
            "name": "t2",
            "v": "updated"
        },
        {
            "name": "tag-new",
            "v": "new tag"
        }
    ]
    
    db.test.updateOne({
        title: "xyz"
    }, [
        {
            $set: {
                tags: {
                    $map: {
                        input: "$tags",
                        in: {
                            $mergeObjects: [
                                "$$this",
                                {
                                    $switch: {
                                        branches: upsertTags.map(upsertTag => ({
                                            case: { $eq: ["$$this.name", upsertTag.name] },
                                            then: upsertTag
                                        })),
                                        default: {}
                                    }
                                }
                            ]
                        }
                    }
                }
            },
        },
        // Upsert tags that are not in the original array
        {
            $set: {
                tags: {
                    $concatArrays: [
                        "$tags",
                        {
                            $map: {
                                input: {
                                    $filter: {
                                        input: upsertTags,
                                        as: "tag",
                                        cond: {
                                            $not: {
                                                $in: ["$$tag.name", "$tags.name"]
                                            }
                                        }
                                    }
                                },
                                in: "$$this"
                            }
                        }
                    ]
                }
            },
        },
    ])
    

  2. You can use the $set and $addToSet operators with a combination of the arrayFilters option to update existing tags and add new ones. However, the challenge here is updating existing tags (name: tag2) and adding new tags (name: tag-new) in a single update operation.

    Query:

    db.collection.update(
      { "title": "something" }, // Filter the document
      [
        {
          $set: {
            // Update existing tags by finding matching tag name
            tags: {
              $map: {
                input: "$tags",
                as: "tag",
                in: {
                  $cond: [
                    { $eq: ["$$tag.name", "tag2"] },  // Condition to match tag by name
                    { name: "$$tag.name", value: "updated" },  // Update tag2's value
                    "$$tag"  // Keep other tags unchanged
                  ]
                }
              }
            }
          }
        },
        {
          $addToSet: {
            // Add new tag if not already present
            tags: { name: "tag-new", value: "new tag" }
          }
        }
      ]
    )
    

    $map is used to iterate over the tags array. It checks if the tag’s name matches tag2 and updates its value. Otherwise, the tag remains unchanged.
    $addToSet adds the new tag (tag-new) to the array only if it doesn’t exist.

    hope its work…

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search