Home > front end >  Toggle nested array value in update pipeline with $cond
Toggle nested array value in update pipeline with $cond

Time:12-01

I am trying to toggle names of people who liked a comment in the comments array in a document.

Better visualized than words: Mongo Playground Link


Explanation:

Suppose I have a document

{
    "id": 1,
    "comments": [
      {
        "id": 1,
        "text": "Comment 1",
        "likedBy": [
          "Tomato",
          "Potato"
        ],
        
      },
      {
        "id": 2,
        "text": "Comment 2",
        "likedBy": [
          "Carrot"
        ],
        
      },
      
    ],
    
  },

I would like to "toggle" a likedBy value "Potato" on a particular comment, matched by comments.id in a document.


So far, I came up with this

db.collection.update({
  id: 1,
  "comments.id": 1,
  
},
[
  {
    $set: {
      "comments.likedBy": {
        $cond: [
          {
            $in: [
              "Potato",
              "$comments.likedBy"
            ]
          },
          {
            $setDifference: [
              "$comments.likedBy",
              [
                "Potato"
              ]
            ]
          },
          {
            $concatArrays: [
              "$comments.likedBy",
              [
                "Potato"
              ]
            ]
          }
        ],
        
      },
      
    },
    
  }
])

Expected output:

{
    "id": 1,
    "comments": [
      {
        "id": 1,
        "likedBy": [
           "Tomato",
           //!! Potato is removed from here
        ],
        "text": "Comment 1"
      },
      {
        "id": 2,
        "likedBy": [
          "Carrot"
        ],
        "text": "Comment 2"
      }
    ],

  }

However, I am getting incorrect result. I have a feeling that I am doing something wrong with the positional operators.

CodePudding user response:

Query

  • this removes the "Potato" from likeBy if it exists else adds the "Potato"
  • $map on comments, if comment.id=1 add/remove the "Potato" else leave comment as it is
  • in the bellow code $$this is the comment sub-document

*Not sure if this is what you need exactly but produces the result you want, and looks like your query.

*about the paths, "$comments.likedBy" is an array that contains all the likedBy arrays from all comments, with $map bellow $$this.likedBy is used to contain only the likedBy of a specific comment, i think this was the problem.

Test code here

update(
{"id": 1,"comments.id": 1},
[{"$set": 
    {"comments": 
      {"$map": 
        {"input": "$comments",
          "in": 
          {"$cond": 
            [{"$eq": ["$$this.id", 1]},
              {"$mergeObjects": 
                ["$$this",
                  {"likedBy": 
                    {"$cond": 
                      [{"$in": ["Potato", "$$this.likedBy"]},
                       {"$setDifference": ["$$this.likedBy", ["Potato"]]},
                       {"$concatArrays": ["$$this.likedBy", ["Potato"]]}]}}]},
              "$$this"]}}}}}])
  • Related