Пн goose - Как постепенно вычесть / уменьшить значение в массиве поддокументов, которое находится в другом массиве поддокументов - PullRequest
1 голос
/ 30 мая 2020

Это довольно сложно,

У меня есть этот образец документа

[{
  _id: '111',
  products: [{
    _id: 'productId',
    items: [{
      _id: 'aaa',
      quantity: 5
    }, {
      _id:'bbb',
      quantity: 8
    }]
  }]
}]

Теперь, когда я покупаю Предмет, мне нужно, чтобы его количество было уменьшено, начиная с первого в предметах до второго, третьего и т. д. c ...

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

Например, если я куплю 3 элемента, он должен вычесть 3 элемента в первом элементе массива и в результате Массив элементов должен быть

...
    items: [{
      _id: 'aaa',
      quantity: 2
    }, {
      _id: 'bbb',
      quantity: 8
    }]

С другой стороны, если бы я купил 7 элементов, он должен уменьшить все 5 элементов из первого элемента массива, а затем удалить 2 элемента из второго элемента ... и, следовательно, окончательный массив будет выглядеть примерно так:

...
    items: [{
      _id: 'aaa',
      quantity: 0
    }, {
      _id: 'bbb',
      quantity: 6
    }]

ЧТО Я ПЫТАЛ:

Я пробовал использовать findOneAndUpdate вот так, просто чтобы добраться до массива элементов, но у меня нет Идея, как мне продолжить остроумие h часть предметов.

        Shop.findOneAndUpdate(
            { _id: '111', products._id: 'productId' }
        )

1 Ответ

1 голос
/ 31 мая 2020

Хотя это возможно с MongoDB v4.2 ( конвейер обновления ), где вы можете обновлять документы на основе их текущего значения. Это было бы очень сложно, поскольку у вас действительно нет собственного способа хранить локальные переменные (чтобы отслеживать, сколько элементов вы уже вычли). Вы можете использовать $reduce, чтобы сохранить это значение, но это будет выглядеть очень сложным, поскольку у вас есть глубоко вложенные структуры данных. Будет намного проще, если вы сможете сделать это на уровне приложения, но тогда у вас возникнут проблемы с параллелизмом, если ваш документ изменится между ними.

Предложения

  1. Измените структуру данных, чтобы быть более плоским, возможно, разделить на несколько коллекций. Сложно выполнять обновления в глубоко вложенных элементах
  2. Подождите, пока MongoDB v4.4, где у вас есть операторы $accumulator и $function, которые могут сохранять состояние и более гибко обрабатывает ваши данные, используя JavaScript
  3. Используйте MongoDB v4.2 конвейер обновления вместе с $reduce и $cond. При таком подходе вы должны поддерживать состояние, используя $reduce, и применять значение количества в соответствии с состоянием, поскольку у вас есть вложенные структуры массивов, вам также придется использовать $map.
  4. Как упоминалось выше, вы можете реализовать запрос документа, обновить в своем приложении и вернуть изменения в MongoDB, но вы должны убедиться, что нет параллельной операции, которая пытается обновить ваш документ между чтением и записью.
  5. Обновление: другое решение - дождаться стадии агрегации v4.4 $merge, которая позволяет вывести результат конвейера агрегации в ту же коллекцию.

Обновление: я попытался реализовать №3, чтобы показать сложность. Вдохновение взято из ответа на ваш другой вопрос

Shop.updateOne({
  "_id": '111', "products._id": "productId" // don't forget to replace "productId"
}, [{
    "$set":{
      "products":{
        "$map":{
          "input":"$products", // iterate through products, we can not update inside the array directly as "arrayFilters" update option isn't available in pipeline update
          "in":{
            "$cond":[
              {
                "$ne":[
                  "$$this._id",
                  "productId" // don't forget to replace "productId"
                ]
              },
              "$$this",
              {
                "$mergeObjects":[
                  "$$this",
                  {
                    "items":{
                      "$reduce":{
                        "input":"$$this.items",
                        "initialValue":{ // initialise accumulator
                          "acc": 7, // the number you want to subtract
                          "items":[]
                        },
                        "in":{
                          "acc":{
                            "$subtract":[
                              "$$value.acc",
                              {
                                "$min":[
                                  "$$this.quantity",
                                  "$$value.acc"
                                ]
                              }
                            ]
                          },
                          "items":{
                            "$concatArrays":[
                              "$$value.items",
                              [
                                {
                                  "$mergeObjects":[
                                    "$$this",
                                    {
                                      "quantity":{
                                        "$subtract":[
                                          "$$this.quantity",
                                          {
                                            "$min":[
                                              "$$this.quantity",
                                              "$$value.acc"
                                            ]
                                          }
                                        ]
                                      }
                                    }
                                  ]
                                }
                              ]
                            ]
                          }
                        }
                      }
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  }, // at this stage, you'll have all information you need, add the next stage only if you want it in the same structure
  {
    "$set":{
      "products":{
        "$map":{
          "input":"$products",
          "in":{
            "$cond":[
              {
                "$ne":[
                  "$$this._id",
                  "productId"
                ]
              },
              "$$this",
              {
                "$mergeObjects":[
                  "$$this",
                  {
                    "items":{
                      "$ifNull":[
                        "$$this.items.items",
                        "$$this.items"
                      ]
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  }
])

Обновление # 2 «Более короткая» версия, которая требует только одного этапа, с использованием массива вместо объекта для сохраните аккумулятор.

Shop.updateOne({
  "_id": '111', "products._id": "productId" // don't forget to replace "productId"
}, [
  {
    "$set": {
      "products": {
        "$map": {
          "input": "$products",
          "in": {
            "$cond": [
              {
                "$ne": [
                  "$$this._id",
                  "productId" // don't forget to replace "productId"
                ]
              },
              "$$this",
              {
                "$mergeObjects": [
                  "$$this",
                  {
                    "items": {
                      "$arrayElemAt": [
                        {
                          "$reduce": {
                            "input": "$$this.items",
                            "initialValue": [
                              7, // the number you want to subtract
                              []
                            ],
                            "in": [
                              {
                                "$subtract": [
                                  {
                                    "$arrayElemAt": [
                                      "$$value",
                                      0
                                    ]
                                  },
                                  {
                                    "$min": [
                                      "$$this.quantity",
                                      {
                                        "$arrayElemAt": [
                                          "$$value",
                                          0
                                        ]
                                      }
                                    ]
                                  }
                                ]
                              },
                              {
                                "$concatArrays": [
                                  {
                                    "$arrayElemAt": [
                                      "$$value",
                                      1
                                    ]
                                  },
                                  [
                                    {
                                      "$mergeObjects": [
                                        "$$this",
                                        {
                                          "quantity": {
                                            "$subtract": [
                                              "$$this.quantity",
                                              {
                                                "$min": [
                                                  "$$this.quantity",
                                                  {
                                                    "$arrayElemAt": [
                                                      "$$value",
                                                      0
                                                    ]
                                                  }
                                                ]
                                              }
                                            ]
                                          }
                                        }
                                      ]
                                    }
                                  ]
                                ]
                              }
                            ]
                          }
                        },
                        1
                      ]
                    }
                  }
                ]
              }
            ]
          }
        }
      }
    }
  }
])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...