Измените объект в массив в коллекции ключ-значение

#mongodb #mongodb-query

Вопрос:

У меня есть такой документ в MongoDB:

 {
  "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"
          }
        }
      }
    ]
  }
}
 

и я хочу обновить foodstuffSelectedPortions.portion объект в массив, содержащий этот объект. Таким образом, ожидаемый результат должен выглядеть следующим образом:

 {
  "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"
            }
          }
        ]
      }
    ]
  }
}
 

Я пробовал этот запрос:

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

но это выдает мне ошибку: Cannot apply array updates to non-array element foodstuffSelectedPortions: которая выглядит нормально, потому foodstuffSelectedPortions что это объект, а не массив.

Как правильно написать этот запрос? Я использую MongoDB 4.4.4 и оболочку Mongo.

Комментарии:

1. id_1 , id_2, я думаю, не знают схемы, верно? это данные, и у вас может быть их неизвестное количество? например, id_25 и т. Д. Или у вас всегда есть ровно 2 с именами id_1 и id_2 ? Если вы храните данные о ключах, я думаю, что лучше всего изменить вашу схему на что-то вроде этого , если часть будет содержать более 1 элемента, затем создайте массив документов «часть», в противном случае документ подойдет. Подумайте об этом, исходя из ваших потребностей.

2. Хорошо, я согласен с изменением схемы, как вы описали, но для этого мне нужен запрос, потому что у меня много данных, и изменение вручную-это не вариант.

Ответ №1:

Запрос (тот, который вы задали)
(сделайте updateMany, чтобы обновить все до новой схемы)

  • преобразуется в массив
  • карта на массиве
  • сопоставьте вложенный массив, заменив объект части массивом

Тестовый код здесь

 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"
          }
        }
      }
    }
  }
])
 

Запрос
(альтернативная схема с 2 уровнями вложенности вместо 3 и без данных о ключах)

Примером того, почему данные о ключах плохие(я думаю, есть исключения), является то, почему вы застряли, вы хотели обновить все, но вы не знали их имен, поэтому не могли их выбрать. Если бы они были у вас в массиве, вы могли бы создать карту для всех или использовать оператор обновления для обновления всех элементов массива (например, как вы пытались это сделать и пожаловались, что это объект).

*Но не используйте эту схему, если она лучше не соответствует вашим данным и вашим запросам.

Тестовый код здесь

 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"
                    }
                  }
                ]
              }
            }
          }
        }
      }
    }
  }
])
 

Для обновления конвейера требуется MongoDB >=4.2