【问题标题】:Toggle Mongo Document Date Property With Single QueryToggle 带有单个查询的 Mongo 文档日期属性
【发布时间】:2020-07-08 17:29:13
【问题描述】:

只知道 Mongo 中文档的 _id 是否有一种直接的方法来编写一个更新查询来执行以下操作?我认为答案是“不”,但我想知道是否有什么事情会导致这种情况发生。

  1. 通过_id 定位文档(单个文档)。
  2. 如果文档中存在readAt 字段,请将其删除
  3. 如果文档中不存在readAt字段,请将其设置为当前时间

本质上,我想“切换”文档的“读取”状态(使用日期作为标志),而无需实际首先获取文档。

目前我正在使用类似的东西:

collection.update(
  {_id: notificationDoc._id},
  notificationDoc.readAt
    ? {$unset: {readAt: true}}
    : {$set: {readAt: new Date()}}
)

效果很好,但需要我首先阅读该领域。我并不真正担心比赛条件或其他任何事情,我只是希望减少到 DB 的往返行程。


我已经想到了几种方法,可以通过两个具有不同条件的串行更新查询来做到这一点,但这似乎不是一种改进。

【问题讨论】:

    标签: javascript node.js mongodb


    【解决方案1】:

    如果您的MongoDB version is >= 4.2,那么您可以在更新查询中指定聚合管道。

    db.collection.update(
    { _id: notificationDoc._id },
    [
      {
        $set: {
          readAt: {
            $cond: [
              {
                $lte: [
                  "$readAt",
                  null
                ]
              },
              "$$NOW",
              "$$REMOVE" //(previously null)
            ]
          }
        },
        
      }
    ])
    

    上述查询会将readtAt 设置为null(如果提供$$REMOVE,则将其删除)if it exists当前日期时间 if not exist/null

    You can see how the comparison works

    【讨论】:

    • 啊,谢谢。这实际上看起来非常接近。目标是 $unset 如果它存在而不是将其设置为空。看起来$cond 总是指定文档中的两个值之一。也许有可能让管道中的第二阶段成为字段上的 $unset,条件是它的值为 null。我可能会尝试追踪聚合管道是否可行,现在我发现在 4.2 中这是可行的。
    • 我已经更新了我的答案,感谢@prasad_ +1 介绍了$$REMOVE
    • 如果 readAt 键不存在,它现在将删除它
    • 经过几次测试,看起来效果很好!学到了一些新东西。感谢您再次访问它。
    【解决方案2】:

    更新(切换)逻辑可以实现如下 - 我将用一个例子来解释。考虑一个包含两个文档的集合:

    { "_id" : 1, "readAt" : "2020-07-01", "fld" : "Y" }
    { "_id" : 2, "fld" : "N" }
    

    以下聚合删除了带有_id: 1 的文档中的readAt字段,并为带有_id: 2 的文档设置了readAt: "2020-07-08"

    var NEW_VALUE = "2020-07-08";
    
    db.collection.aggregate([
      { 
          $addFields: { 
              readAt: { 
                  $let: {
                      vars: { readAtExists: { $ifNull: [ "$readAt", null ] } },
                      in: { $cond: [ {$eq: [ "$$readAtExists", null ]}, NEW_VALUE, "$$REMOVE"  ] }
                  }
              }
          }
      }
    ])
    

    基于此聚合,您可以通过以下两种方式之一更新文档。 第一个选项适用于 MongoDB 4.2 或更高版本(使用Update with Aggregation Pipeline),第二个选项适用于 4.2 之前的版本。


    1) 使用聚合管道更新

    db.collection.update(
      { _id: 1 },  // { _id: 2 }
      [
        { 
            $set: { 
                readAt: { 
                    $let: {
                        vars: { readAtExists: { $ifNull: [ "$readAt", null ] } },
                        in: { $cond: [ {$eq: [ "$$readAtExists", null ]}, NEW_VALUE, "$$REMOVE"  ] }
                    }
                }
            }
        }
      ]
    )
    

    2) 聚合和更新:

    db.collection.aggregate( [
      {
          $match: { _id: 1 }  // { _id: 2 }
      },
      { 
          $addFields: { 
              readAt: { 
                  $let: {
                      vars: { readAtExists: { $ifNull: [ "$readAt", null ] } },
                      in: { $cond: [ {$eq: [ "$$readAtExists", null ]}, NEW_VALUE, "$$REMOVE"  ] }
                  }
              }
          }
      }
    ] ).forEach( doc => db.collection.replaceOne( { _id: doc._id }, doc ) )
    

    注意事项: 您可以在 MongoDB v4.2 中使用 NOW 聚合变量来设置聚合中的日期值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-10
      • 2021-05-30
      • 2014-08-20
      • 1970-01-01
      • 2011-01-15
      • 1970-01-01
      相关资源
      最近更新 更多