Home > Mobile >  Change object into array in key-value collection
Change object into array in key-value collection

Time:09-27

I've got a document in MongoDB like this:

{
  "name": "wine",
  "foodstuffSelectedPortions": {
    "id_1": [
      {
        "foodstuffId": "f1",
        "portion": {
          "portionName": "portion name 1",
          "portionWeight": {
            "value": 1,
            "unit": "KG"
          }
        }
      },
      {
        "foodstuffId": "f2",
        "portion": {
          "portionName": "portion name 2",
          "portionWeight": {
            "value": 100,
            "unit": "ML"
          }
        }
      }
    ],
    "id_2": [
      {
        "foodstuffId": "f3",
        "portion": {
          "portionName": "portion name 3",
          "portionWeight": {
            "value": 15,
            "unit": "ML"
          }
        }
      }
    ]
  }
}

and I want to update foodstuffSelectedPortions.portion object into array that contains this object. So the expected result should look like this:

{
  "name": "wine",
  "foodstuffSelectedPortions": {
    "id_1": [
      {
        "foodstuffId": "f1",
        "portion": [
          {
            "portionName": "portion name 1",
            "portionWeight": {
              "value": 1,
              "unit": "KG"
            }
          }
        ]
      },
      {
        "foodstuffId": "f2",
        "portion": [
          {
            "portionName": "portion name 2",
            "portionWeight": {
              "value": 100,
              "unit": "ML"
            }
          }
        ]
      }
    ],
    "id_2": [
      {
        "foodstuffId": "f3",
        "portion": [
          {
            "portionName": "portion name 3",
            "portionWeight": {
              "value": 15,
              "unit": "ML"
            }
          }
        ]
      }
    ]
  }
}

I've tried this query:

db.foodstuff.update(
{ },
{ $set: { "foodstuffSelectedPortions.$[].portion": ["$foodstuffSelectedPortions.$[].portion"] } }
)

but it gives me an error: Cannot apply array updates to non-array element foodstuffSelectedPortions: which looks fine because the foodstuffSelectedPortions is an object not array.

How to write this query correctly? I use MongoDB 4.4.4 and Mongo Shell.

CodePudding user response:

Query (the one you asked)
(do updateMany to update all to the new schema)

  • converts to array
  • map on array
  • map on nested array, replacing portion object with array

Test code here

db.collection.update({},
[
  {
    "$set": {
      "foodstuffSelectedPortions": {
        "$arrayToObject": {
          "$map": {
            "input": {
              "$objectToArray": "$foodstuffSelectedPortions"
            },
            "in": {
              "k": "$$f.k",
              "v": {
                "$map": {
                  "input": "$$f.v",
                  "in": {
                    "$mergeObjects": [
                      "$$f1",
                      {
                        "portion": [
                          "$$f1.portion"
                        ]
                      }
                    ]
                  },
                  "as": "f1"
                }
              }
            },
            "as": "f"
          }
        }
      }
    }
  }
])

Query (alternative schema with 2 levels of nesting instead of 3, and without data on keys)

An example why data on keys is bad(i guess there are exceptions), is why you are stuck, you wanted to update all, but you didn't know their names so you couldn't select them. If you had them in array you could do a map on all or you could use update operator to do update in all members of an array (like the way you tried to do it, and complained that is object)

*But dont use this schema unless it fits your data and your queries better.

Test code here

db.collection.update({},
[
  {
    "$set": {
      "foodstuffSelectedPortions": {
        "$reduce": {
          "input": {
            "$objectToArray": "$foodstuffSelectedPortions"
          },
          "initialValue": [],
          "in": {
            "$let": {
              "vars": {
                "f": "$$this"
              },
              "in": {
                "$concatArrays": [
                  "$$value",
                  {
                    "$map": {
                      "input": "$$f.v",
                      "in": {
                        "$mergeObjects": [
                          "$$f1",
                          {
                            "name": "$$f.k",
                            "portion": [
                              "$$f1.portion"
                            ]
                          }
                        ]
                      },
                      "as": "f1"
                    }
                  }
                ]
              }
            }
          }
        }
      }
    }
  }
])

Pipeline updates require MongoDB >=4.2

  • Related