简体   繁体   English

Toggle 带有单个查询的 Mongo 文档日期属性

[英]Toggle Mongo Document Date Property With Single Query

Knowing just the _id of a document in Mongo is there a straightforward way to write ONE update query to do the following?只知道 Mongo 中文档的_id是否有一种直接的方法来编写一个更新查询来执行以下操作? I think the answer is "no", but I'd like to know about it if there is something that does let this happen.我认为答案是“不”,但我想知道是否有什么东西可以让这种情况发生。

  1. Target a document by _id (single document)._id (单个文档)定位文档。
  2. If the readAt field exists on the document, remove it如果文档中存在readAt字段,请将其删除
  3. If the readAt field does NOT exist on the document, set it to the current time如果文档中不存在readAt字段,则将其设置为当前时间

Essentially, I want to "toggle" the "read" state (using a Date as a flag) of the document without actually first fetching the document.本质上,我想“切换”文档的“读取”state(使用日期作为标志)而不实际首先获取文档。

Currently I'm using something like:目前我正在使用类似的东西:

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

which works fine, but requires I first read in that field.效果很好,但需要我首先阅读该领域。 I'm not really worried about race conditions or anything, I'd just prefer to cut out the round trip to the DB.我并不真正担心比赛条件或其他任何事情,我只是更愿意减少到 DB 的往返行程。


I've thought of a couple of ways to do it with two update queries in serial with distinct conditions, but that doesn't seem like an improvement.我已经想到了几种方法来通过两个具有不同条件的串行更新查询来做到这一点,但这似乎并不是一种改进。

If your MongoDB version is >= 4.2 then you can specify aggregate pipeline in you update query.如果您的MongoDB 版本 >= 4.2 ,那么您可以在更新查询中指定聚合管道。

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

Above query will set readtAt to null (or remove it if $$REMOVE is provided) if it exists and to current date time if not exist/null上面的查询将readtAt设置为null (如果提供$$REMOVE则将其删除) if it exists ,则设置为当前日期时间if not exist/null

You can see how the comparison works 你可以看到比较是如何工作的

The update (toggle) logic can be implemented as follows - I will explain with an example.更新(切换)逻辑可以实现如下 - 我将用一个例子来解释。 Consider a collection with two documents:考虑一个包含两个文档的集合:

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

The following aggregation removes the readAt field in the document with _id: 1 and sets the readAt: "2020-07-08" for the document with _id: 2 .以下聚合删除了文档中具有_id: 1readAt字段,并为具有_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"  ] }
              }
          }
      }
  }
])

Based on this aggregation you can update the document in one of the following two ways.基于此聚合,您可以通过以下两种方式之一更新文档。 The first option works with MongoDB versions 4.2 or newer (uses Update with Aggregation Pipeline ), and the second option works with versions earlier than the 4.2.一个选项适用于 MongoDB 4.2 或更高版本(使用Update with Aggregation Pipeline ),第二个选项适用于 4.2 之前的版本。


1) Update with Aggregation Pipeline 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) Aggregate and Update: 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 ) )

NOTES: You can use the NOW aggregation variable with MongoDB v4.2 for setting the date value in an aggregation.注意:您可以在 MongoDB v4.2 中使用NOW聚合变量来设置聚合中的日期值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM