[英]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.我认为答案是“不”,但我想知道是否有什么东西可以让这种情况发生。
_id
(single document)._id
(单个文档)定位文档。readAt
field exists on the document, remove itreadAt
字段,请将其删除readAt
field does NOT exist on the document, set it to the current timereadAt
字段,则将其设置为当前时间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
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: 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" ] }
}
}
}
}
])
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.