如何在 mongodb/mongoose 的嵌套数组中检查是不是存在并更新对象?

Posted

技术标签:

【中文标题】如何在 mongodb/mongoose 的嵌套数组中检查是不是存在并更新对象?【英文标题】:How can I check existance and update an object in nested array in mongodb/mongoose?如何在 mongodb/mongoose 的嵌套数组中检查是否存在并更新对象? 【发布时间】:2021-01-04 16:13:18 【问题描述】:

我们正在尝试使用 node.js 和 typescript 为库存管理 Web 应用程序构建一个 REST API。我们的 Records>Stocks 文档(嵌套数组)位于 Branches 集合下。

记录:以桶模式保存数据。这意味着分支机构一天只能有一个记录文件。 股票:股票可以有'TRANSFER'或'DAILY'之类的类型。一个分店在一条记录中只能有一个'DAILY' 股票条目和许多'TRANSFER' 股票条目。

我们有库存更新要求。我们的Branches Collection示例文档如下所示。


"phones": [
    "Lorem"
],
"_id": 2,
"name": "Üsküdar",
"address": 
    "city": "İstanbul",
    "address_line": "Cinali Sk. No:14 Selamiali 34664 ",
    "district": "Lorem"
,
"owner_id": 38,
"region_id": 12,
"totals": 
    "stock": 
        "P": -63,
        "K": 23,
        "U": -73
    ,
    "sale": 
        "P": -28,
        "K": 82,
        "U": -36
    ,
    "material": 
        "Kutu": 29,
        "Garnitur": 37,
        "Spatula": -55
    
,
"records": [
    
        "delivery": 
            "P": -18,
            "K": -69,
            "U": -15
        ,
        "stocks": [
            
                "time": "10:45",
                "quantity": 
                    "P": [
                        15
                    ],
                    "K": [
                        9
                    ],
                    "U": [
                        11
                    ]
                ,
                "explanation": "Günlük Stok Girişi",
                "type": "DAILY"
            
        ],
        "sales": [
            
                "time": "Lorem",
                "quantity": 
                    "P": [
                        "Lorem"
                    ],
                    "K": [
                        "Lorem"
                    ],
                    "U": [
                        "Lorem"
                    ]
                ,
                "explanation": "Lorem"
            
        ],
        "material": [
            
                "time": "Lorem",
                "quantity": 
                    "Kutu": -68,
                    "Garnitur": 42,
                    "Spatula": 4
                ,
                "explanation": "Lorem"
            
        ],
        "date": "16-09-2020"
    
],
"orders": [
    
        "date": "Lorem",
        "borek": 
            "quantity": 
                "P": 6,
                "K": 77,
                "U": -25
            ,
            "status": "Lorem"
        ,
        "material": 
            "quantity": 
                "Kutu": -43,
                "Garnitur": 29,
                "Spatula": -70
            ,
            "status": "Lorem"
        
    
]

我们的端点如下所示,它获取 branch_id 并使用 req.body "quantity": "P": [11],"K": [4],"U": [4] 创建一个请求

branchRouter.post(
  '/:branchId/daily-stock',
  async (req: Request, res: Response) => 
    const  branchId  = req.params;
    const  quantity  = req.body;

    const timestamp = moment().tz('Europe/Istanbul');
    const date = timestamp.format('DD-MM-YYYY');
    const time = timestamp.format('HH:mm');
    const explanation = 'Günlük Stok Girişi';
    const type = 'DAILY';
    const stockEntry = new Stock( time, quantity, explanation, type );
    const recordEntry = new Record( date, stocks: [stockEntry] );

    try 
      await Branch.findByIdAndUpdate(
        branchId,
        ,
        
          new: true,
          projection: 
            'records.stocks': 1,
            'records.date': 1,
            // records:  $slice: 5 ,  // to get first 5 array object
            records:  $elemMatch:  date: date  ,
            'records.stocks.type': 1,
          ,
          // arrayFilters: [ 'records.stocks.type':  $eq: 'DAILY'  ],
        ,
        async function (err: any, result: any) 
          if (err) 
            res.status(501).send('Branch cannot be updated!');
           else 
            if (result) 
              if (result.records.length === 0) 
                await Branch.findByIdAndUpdate(branchId, 
                  $push:  records: recordEntry ,
                );
                res.status(200).send(
                  message:
                    'No record found for today, it will be creating with daily-stock',
                  record: recordEntry,
                );
               else 
                const entries = result.records[0].stocks;
                if (entries.some((stock: any) => stock.type === 'DAILY')) 
                  // TODO Update data by req.body, you can use something like $replacewith
                  await Branch.findByIdAndUpdate(branchId, 
                    'records.0.stocks': 
                      $cond: 
                        if:  $eq: ['$type', 'DAILY'] ,                        
                        then:  $push:  'records.$[].stocks': stockEntry  ,
                        else:  $push:  'records.$[].stocks': stockEntry  ,
                      ,
                    ,
                    // $addToSet:  'records.$.stocks': stockEntry ,
                  );
                  res.status(200).send(
                    message: `Today's records and daily-stock exist, Daily-Stock Entry Updating...`,
                    record: result.records[0],
                  );
                 else 
                  await Branch.findByIdAndUpdate(
                    branchId,
                     $push:  'records.$[].stocks': stockEntry  ,
                    
                      projection: 
                        'records.stocks': 1,
                        records:  $elemMatch:  date: date  ,
                      ,
                    
                  );
                  res.status(200).send(
                    message: `Today's records exists but not Daily-Stock, Daily-Stock Entry Inserting...`,
                    record: result.records[0],
                  );
                
              
             else 
              res.status(404).send( error: 'Branch not found!' );
            
          
        
      );
     catch (error) 
      res.status(400).send(error.message);
    
  
);

问题是如果我们已经有“每日”股票条目,我们将如何更新它?

其实剧情有点难。我们需要使用条件结构,但是$cond、$replaceWith、$push等没有给出解决这个问题的思路。

【问题讨论】:

【参考方案1】:

我找到了,答案就在这里

const dailyStockUpdatedBranch = await Branch.findOneAndUpdate(
                 _id: branchId ,
                
                  // $pull: 
                  //   'records.$[record].stocks.$[stock]': stockEntry,
                  // ,
                  $set: 
                    region_id:
                      'records.$[record].stocks.$[stock].quantity.P[0]',
                  ,
                ,
                
                  arrayFilters: [
                     'record.date': date ,
                     'stock.type': 'DAILY' ,
                  ],
                  projection: 
                    'records.stocks': 1,
                    'records.date': 1,
                    totals: 1,
                  ,
                
              );

【讨论】:

以上是关于如何在 mongodb/mongoose 的嵌套数组中检查是不是存在并更新对象?的主要内容,如果未能解决你的问题,请参考以下文章

使用嵌套字段在MongoDB / Mongoose中创建多对多关系?

将对象 ID 推送到嵌套数组 MongoDB/Mongoose

MongoDB Mongoose Schema 嵌套文档结构和关系

MongoDB Mongoose 聚合查询深度嵌套数组删除空结果并填充引用

MongoDB/Mongoose - 仅当某个字段是唯一的时才将对象添加到对象数组中

Nodejs mongoose根据对象属性选择嵌套数组中的对象