Как я могу выбрать конкретное поле в моем массиве MongoDB и суммировать его - PullRequest
0 голосов
/ 26 сентября 2019

У меня есть схема MongoDB, в которой некоторое время у меня есть один массив, и иногда в нем содержится более 20 значений массивов, каждый массив имеет значение поля, которое я хочу суммировать вместе и вставить сумму в другое поле в моем MongoDB,Вот что я пытаюсь сказать, вот моя схема, Как я могу добавить значение веса вместе для каждого массива пакета, вставленного в схему, и пусть это будет моя схема общего веса

{
  "status": "In warehouse",
  "paymentStatus": "incomplete",
  "_id": "5d8b56476a3e4ae2d01f2953",
  "unitNo": "0002",
  "warehouseNo": "0001",
  "trackingNo": "FPZ848505936",
  "packages": [
    {
      "date": "2019-09-26T06:30:39.561Z",
      "_id": "5d8c5b0f756838f78d5205d7",
      "category": "chil",
      "quantity": "177",
      "description": "a valueablegoods",
      "width": 15,
      "itemweight": 123,
      "length": 17,
      "height": 21,
      "dimension": 31.25903614457831,
      "weight": 32.25903614457831 
    },
    {
      "date": "2019-09-26T06:30:39.561Z",
      "_id": "5d8c5b0f756838f78d5202dd,
      "category": "chil",
      "quantity": "177",
      "description": "a valueablegoods",
      "width": 15,
      "itemweight": 123,
      "length": 17,
      "height": 21,
      "dimension": 35.25903614457831,
      "weight": 30
    },
    {
      "date": "2019-09-26T06:30:39.561Z",
      "_id": "5d8c5b0f756838f78d51aeq",
      "category": "chil",
      "quantity": "177",
      "description": "a valueablegoods",
      "width": 15,
      "itemweight": 123,
      "length": 17,
      "height": 21,
      "dimension": 32.25903614457831,
      "weight": 44
    }
  ],
  "totalWeigth": "This should add all weight value in my packages array together and if it is only 1 it should brings only the one"
  "date": "2019-09-25T11:57:59.359Z",
  "__v": 0
}

ЭтоAPI-маршрут, который добавляет пакеты в поле массива пакетов, и я хочу, чтобы totalWeight обновлялся каждый раз, когда добавляется или обновляется новый пакет

// @route   POST api/admins/addshipment/:unitNo
// @desc    Add shipment for each customer
// @access  Private
router.post(
  '/addshipment/:unitNo',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {

     Shipments.findOne({unitNo: req.params.unitNo}, {paymentStatus: "incomplete"})
      .then(shipments => {
        if(shipments === null || shipments.paymentStatus === "complete"){
          const errwarehouse = "This user doesn't have an existing warehouse";
          return res.status(404).json(errwarehouse);
        }else{
          if (shipments.paymentStatus === "incomplete") {
         function getPrice(){
          if (initial > dimension){
            return initial
            }else if(initial === dimension) {
            return initial
          }else{
          return dimension
          }
        }
          const newPackages = {
          category: req.body.category,
          quantity: req.body.quantity,
          description: req.body.description,
          width: req.body.width,
          itemweight: req.body.itemweight,
          length: req.body.length,
          height: req.body.height,
          dimension,
          weight: getPrice(),
        };
          Shipments.findOneAndUpdate({unitNo: req.params.unitNo ,paymentStatus: "incomplete"}, 
            {"$push": {"packages": newPackages}}, {totalWeight: {"$sum" : {"packages.weight"}}}) //Here is were i add the package to the package array and here is where i tried sumup packages.weight for every time i add new package
            .then(shipments=> res.json(shipments))


          }
        }
        });
    });

Спасибо

Ответы [ 3 ]

1 голос
/ 26 сентября 2019

Если у вас действительно есть MongoDB 4.2 или выше, вы можете использовать синтаксис new , доступный для update .По сути, это означает добавление одного из действительных операторов конвейера агрегации: $addFields, $set (псевдоним $addFields для упрощения чтения «обновлений»), $project или $replaceRoot, а затем фактические операторы агрегирования для выполнения манипуляции.В этом случае $sum:

let writeResult = await db.collection("collection").updateMany(
  {},
  [{ "$set": { "totalWeight": { "$sum": "$packages.weight" } } }]
);

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

Основное преимуществоэто то, что это единственный запрос к серверу, который фактически выполняет ВСЕ обновления на сервере и не требует никакой информации из коллекции для отправки клиенту для повторения.

Если выиметь более раннюю версию (я предлагаю вам не использовать ничего раньше, чем 3.4, но даже 3.2 здесь бы пригодился), тогда вы можете использовать bulkWrite() в цикле:

async function updateCollection() {

  let cursor = db.collection("collection").aggregate([
    { "$project": {
      "totalWeight": { "$sum": "$packages.weight" }
    }}
  ]);

  let batch = [];

  while ( await cursor.hasNext() ) {
    let { _id, totalWeight } = await cursor.next();
    batch.push({
      "updateOne": {
        "filter": { _id },
        "update": { "$set": { totalWeight } }
      }
    });

    if ( batch.length > 1000 ) {
      await db.collection("collection").bulkWrite(batch);
      batch = [];
    })

  }

  if ( batch.length != 0 ) {
    await db.collection("collection").bulkWrite(batch);
    batch = [];
  }

}

И это сделало бы то же самое, но, конечно, на самом деле требуется некоторое взаимодействие с сервером при чтении и записи результата обратно.Хотя при использовании bulkWrite() вы отправляете записи назад только в пакетах , а не на один документ коллекции.

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

NB Возможно, вы захотите добавитьнекоторые обработчики try/catch в таком коде обновления также в случае ошибок.Или, с другой стороны, такие отдельные операции , вероятно, лучше выполнять в чем-то вроде оболочки MongoDB.


Обслуживание

Лучшее решение в целомоднако на самом деле должен поддерживать общее количество при каждом добавлении в массив.Например, это в основном то, что вам нужно при использовании $push для нового элемента массива и $inc для добавления к существующему итогу:

   let package = {
     "date": "2019-09-26T06:30:39.561Z",
     "_id": "5d8c5b0f756838f78d5205d7",
     "category": "chil",
     "quantity": "177",
     "description": "a valueablegoods",
     "width": 15,
     "itemweight": 123,
     "length": 17,
     "height": 21,
     "dimension": 31.25903614457831,
     "weight": 32.25903614457831 
   };

   let writeResult = await db.collection('collection').udpdateOne(
     { "_id": myIdValue },
     {
       "$push": { "packages":  package },
       "$inc": { "totalWeight": package.weight
     }
   );

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

1 голос
/ 26 сентября 2019
   var users =db.users.aggregate([
    {$unwind:"$packages"},
    {
           $group:
            {
               _id: "$_id",
              totalWeigth: { $sum: "$packages.weight" }
            }
        }
    ]).toArray()



 users.forEach((ele)=>{
  db.users.update({_id:ele._id},{$set:{totalWeigth:ele.totalWeigth}})  
 })
0 голосов
/ 27 сентября 2019

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

// @route   POST api/admins/addshipment/:unitNo
// @desc    Add shipment for each customer
// @access  Private
router.post(
  '/addshipment/:unitNo',
  passport.authenticate('jwt', { session: false }),
  (req, res) => {

     Shipments.findOne({unitNo: req.params.unitNo}, {paymentStatus: "incomplete"})
      .then(shipments => {
          const newPackages = {
          category: req.body.category,
          quantity: req.body.quantity,
          description: req.body.description,
          width: req.body.width,
          itemweight: req.body.itemweight,
          length: req.body.length,
          height: req.body.height,
          dimension,
          weight: getPrice(),
        };
        let total = 0;
          Shipments.findOne({unitNo: req.params.unitNo ,paymentStatus: "incomplete"})
            .then(shipments=> {
             shipments.packages.push(newPackages)
            shipments.packages.forEach(i=>{ total += i.weight})
            shipments.totalWeight = total
            shipments.save()
            res.json(shipments)
            }) 
          }
        }
        });
    });

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...