Добавить или обновить элемент пары значений ключа словаря в массиве документа в атомарной операции

Учитывая следующий документ:

  {
"_id": ObjectId("57315ba4846dd82425ca2408"),
"myarray": [
  {
    "key": 1
    "mydictionary":
    {
      "PointA": {
        userId: ObjectId("570ca5e48dbe673802c2d035"),
        point: 5
      },
      "PointB":{
        userId: ObjectId("613ca5e48dbe673802c2d522"),
        point: 2
      }
    }
  },
  {
    "key": 2
    "mydictionary":
    {
      "PointA": {
        userId: ObjectId("570ca5e48dbe673802c2d035"),
        point: 5
      },
      "PointB":{
        userId: ObjectId("613ca5e48dbe673802c2d522"),
        point: 2
      }
    }
  }
]

}

Мне нужно добавить или обновить новую точку (на основе имени ключа, например, «Точка A» будет обновлена, но должен быть добавлен новый элемент «Point C») в атомарной операции. А также по ключу элемента конкретного массива.

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

        db.collection.update({
      _id: ObjectId("57315ba4846dd82425ca2408")
    },
    [
      {
        $set: {
          "myarray.mysubarray": {
            $cond: [
              {
                $in: [
                  ObjectId("570ca5e48dbe673802c2d035"),
                  "$myarray.mysubarray.userId"
                ]
              },
              {
                $map: {
                  input: "$myarray.mysubarray",
                  in: {
                    $mergeObjects: [
                      "$$this",
                      {
                        $cond: [
                          {
                            $eq: [
                              "$$this.userId",
                              ObjectId("570ca5e48dbe673802c2d035")
                            ]
                          },
                          {
                            point: 3
                          },
                          {}
                        ]
                      }
                    ]
                  }
                }
              },
              {
                $concatArrays: [
                  "$myarray.mysubarray",
                  [
                    {
                      userId: ObjectId("570ca5e48dbe673802c2d035"),
                      point: 4
                    }
                  ]
                ]
              }
            ]
          }
        }
      }
    ])
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Может быть, что-то вроде этого:

 db.collection.update({},
 [
 {
  "$addFields": {
  "myarray": {
    "$map": {
      "input": "$myarray",
      "in": {
        "$mergeObjects": [
          "$$this",
          {
            mydictionary: {
              "$objectToArray": "$$this.mydictionary"
            }
          }
        ]
      }
     }
    }
  }
  },
  // convert the nested objects to array
  {
   $addFields: {
  myarray: {
    "$map": {
      "input": "$myarray",
      "as": "m",
      "in": {
        "$mergeObjects": [
          "$$m",
          {
            "$cond": {
              "if": {
                $and: [
                  {
                    $eq: [
                      "$$m.key",
                      1
                    ]
                  },
                  {
                    $in: [
                      "PointB",
                      "$$m.mydictionary.k"
                    ]
                  }
                ]
              },
              "then": {
                k: 1,
                mydictionary: {
                  "$map": {
                    "input": "$$m.mydictionary",
                    "as": "md",
                    "in": {
                      "$mergeObjects": [
                        "$$md",
                        {
                          "$cond": {
                            "if": {
                              $eq: [
                                "$$md.k",
                                "PointB"
                              ]
                            },
                            "then": {
                              k:"PointB",
                              v: {
                                point: "The UPDATED element",
                                userId: "The UPDATED element"
                              }
                            },
                            "else": {}
                          }
                        }
                      ]
                    }
                  }
                }
              },
              "else": {
                "$cond": {
                  "if": {
                    $eq: [
                      "$$m.key",
                      1
                    ]
                  },
                  "then": {
                    mydictionary: {
                      "$concatArrays": [
                        "$$m.mydictionary",
                        [
                          {
                            "k": "PointB",
                            "v": {
                              point: "The upserted element",
                              userId: "The upserted element"
                            }
                          }
                        ]
                      ]
                    }
                  },
                  "else": {}
                }
              }
            }
          }
        ]
        }
      }
     }
   }
  },
 // convert back to object
 {
  "$addFields": {
   "myarray": {
    "$map": {
      "input": "$myarray",
      "in": {
        "$mergeObjects": [
          "$$this",
          {
            mydictionary: {
              "$arrayToObject": "$$this.mydictionary"
            }
          }
        ]
      }
    }
  }
  }
  }
 ],
 {
  multi: true
 })

Объяснение:

  1. Преобразуйте вложенный объект mydictionary в массив
  2. Обновите или вставьте новый объект в mydictionary через условную двойную карту/$mergeObjects
  3. Преобразуйте обратно вложенный массив mydictionary в объект

Детская площадка

Также, чтобы подтвердить ваш вопрос:

В MongoDB операция записи является атомарной на уровне одного документа, даже если операция изменяет несколько встроенных документов в одном документе. Для ситуаций, требующих атомарности чтения и записи нескольких документов (в одной или нескольких коллекциях), MongoDB поддерживает транзакции с несколькими документами.

атомарность операции записи

Спасибо. Будет ли это атомным?

Michael17 14.02.2023 10:04

атомарность в mongoDB гарантируется для одного документа, если вы не используете транзакции, поэтому да, обновление или обновление внутри массива будет атомарным, в примере я добавил обновление/вставку (multi:true) для двух документов, операция не будет атомарной для двух документов, но только для одного документа.

R2D2 14.02.2023 10:53

Кажется, это работает. Что делать, если элемент, содержащий точки, не существует, и мы хотим создать его (например, с ключом: 3) и сохранить новую точку в их словаре в той же операции? Насколько я знаю, $addToSet нельзя использовать, потому что это тот же путь. Пробовал с $addToFields, но безуспешно

Michael17 14.02.2023 13:50

Вот почему я добавил вам 2x документа в коллекцию примеров выше, чтобы проиллюстрировать 2x опции, в первом документе это update, так как PointB существует, во втором документе это upsert/addToSet (insert to the array = $concatAraays), так как PointB не существует...

R2D2 14.02.2023 16:13

Да, мой вопрос был больше о том, что если элемент в myarray не существует, и я хочу его создать, но я понял, как это сделать, просто добавьте для этого дополнительный этап addFields. Огромное спасибо за помощь.

Michael17 14.02.2023 16:15

Действительно, вы можете добавить еще один этап addFields в $concatArray:[], если ключ:1 отсутствует...

R2D2 14.02.2023 16:21

Давайте продолжим обсуждение в чате.

R2D2 14.02.2023 16:29

Другие вопросы по теме