MongoDB Добавить элемент массива с виртуальным порядком

Кто-нибудь знает, как добавить элемент массива в массив mongodb и установить «виртуальный» порядок размера массива, и все это за атомарную операцию?

Итак, что-то вроде:

db.users.updateOne(
    { _id: 1},
    { $addToSet: { images: {name: 'new image', order: "size of the array"} } }
)

Идея состоит в том, чтобы последний добавленный элемент массива всегда имел последний порядок.

Обновлять:

Данные до операции:

{
  _id: 1, 
  images: [
    { name: 'some name', order: 0 }
  ]
}

Данные после операции:

{
  _id: 1, 
  images: [
    { name: 'some name', order: 0 }, 
    { name: 'new image', order: 1 }
  ]
}

Обновление 2:

Если кому-то интересно, чтобы обновить порядок атомарно, вы можете сделать что-то вроде этого (конечно, построить это динамически):

db.collection.update({
  _id: 1
},
{
  $set: {
    "images.$[elem].order": 4,
    "images.$[elem2].order": 3
  }
},
{
  arrayFilters: [
    {
      "elem.name": {
        $eq: "some name"
      }
    },
    {
      "elem2.name": {
        $eq: "new image"
      }
    }
  ]
})

Спасибо!

🤔 А знаете ли вы, что...
MongoDB поддерживает шардирование данных для распределения нагрузки и хранения больших наборов данных...


2
54
2

Ответы:

Решено

Предполагая, что ваше поле order будет целочисленным, вы можете использовать $let для вычисления следующего номера заказа и использовать $concatArrays для добавления его в конец массива.

Некоторые сценарии:

  1. images уже содержит данные: $max получит наибольшее order число, $add 1 к нему.
  2. images — пустой массив: результат $max вернется к 0 из-за $ifNull
  3. images.order не является совершенно 0-м упорядоченным: между ним могут быть пробелы или он не начинается с 0. Он будет обрабатываться $max и $add 1 логикой.
  4. images является пустым полем или не существует: оно будет защищено $ifNull и вернется к пустому массиву.
db.collection.update({},
[
  {
    "$set": {
      "images": {
        "$let": {
          "vars": {
            "seq": {
              "$ifNull": [
                {
                  "$add": [
                    {
                      "$max": "$images.order"
                    },
                    1
                  ]
                },
                0
              ]
            }
          },
          in: {
            "$concatArrays": [
              {
                "$ifNull": [
                  "$images",
                  []
                ]
              },
              [
                //your payload here
                {
                  "name": "new image",
                  "order": "$$seq"
                }
              ]
            ]
          }
        }
      }
    }
  }
],
{
  multi: true
})

Игровая площадка Монго


Обратите внимание, что вы уже храните детали изображения в массиве, поэтому их порядок уже определяется позицией в массиве. Таким образом, если мотивом использования атрибута order является запрос индекса массива позже, есть и другие способы сделать это, и это может быть избыточно.

Например, оператор $unwind опционально возвращает индекс массива. Вы можете запросить это вместо настраиваемого поля, которое необходимо поддерживать отдельно каждый раз при обновлении поля images.