mongodbaggregation-framework

Add field to nested subdocument where value depends on boolean condition


I have a collection like this:

[
  {
    "name": "Orange",
    "variants": [
      {
        "color": "orange",
        "props": []
      }
    ]
  },
  {
    "name": "Banana",
    "variants": [
      {
        "color": "green",
        "props": [
          {
            "uid": 1,
            "grade": 3
          },
          {
            "uid": 2,
            "grade": 5
          },
          {
            "uid": 3,
            "grade": 7
          }
        ]
      }
    ]
  }
]

available_props = [1,2]

Now I would like to add to every prop in props in every variant a field available which evaluates if the uid of the respective prop is in available_props.

Desired outcome:

[
  {
    "name": "Orange",
    "variants": [
      {
        "color": "orange",
        "props": []
      }
    ]
  },
  {
    "name": "Banana",
    "variants": [
      {
        "color": "green",
        "props": [
          {
            "uid": 1,
            "grade": 3,
            "available": true
          },
          {
            "uid": 2,
            "grade": 5,
            "available": true
          },
          {
            "uid": 3,
            "grade": 7,
            "available": false
          }
        ]
      }
    ]
  }
]

Thanks for your help!


Solution

  • you could use nested $maps combined with $mergeObjects

    db.collection.aggregate([
      {
        $addFields: {
          variants: {
            $map: {
              input: "$variants",
              as: "variant",
              in: {
                $mergeObjects: [
                  "$$variant",
                  {
                    props: {
                      $map: {
                        input: "$$variant.props",
                        as: "prop",
                        in: {
                          $mergeObjects: [
                            "$$prop",
                            {
                              available: { $in: [ "$$prop.uid", [ 1, 2 ] ] }
                            }
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    ])
    

    as a tip if you are familiar with JS you could come up with almost similar logic
    js Array.prototype.map -> mongo $map
    js spread operator(...) -> mongo $mergeObjects
    js Array.prototype.includes -> mongo $in

    const doc =   {
        "name": "Banana",
        "variants": [
          {
            "color": "green",
            "props": [
              {
                "uid": 1,
                "grade": 3,
                "available": true
              },
              {
                "uid": 2,
                "grade": 5,
                "available": true
              },
              {
                "uid": 3,
                "grade": 7,
                "available": false
              }
            ]
          }
        ]
      }
      
      
    const newDoc = {
      ...doc,
      variants: doc.variants.map(variant => ({
        ...variant,
        props: variant.props.map(prop => ({
          ...prop,
          available: [1, 2].includes(prop.uid)
        }))
      }))
    }
    
    console.log(newDoc)