上下文:我创建了一项处理用户数据的服务。我无法修改上游系统中的数据源和顺序(通常它是用户或增量的部分快照(例如用户的新帖子)。我还必须处理重复的信息。我围绕文档数据库(MongoDB )使用单个“用户”集合,因为我处理没有任何关系的独立用户,我不需要更新多个表,并且我的读取模型类似于我的文档的架构。我总是可以通过他的/来识别用户她的 _id 和登录名,所以每当我需要更新用户的信息时,我可以只更新一个文档。此外,通常我不想覆盖用户的数据,而是附加它,以便文档创建用户的日志。我只是在非常罕见的情况(当数据可为空但只会更新一次(详细信息)或者我不关心中间状态(详细信息.已验证)。
问题:每当我收到有关给定用户的信息时,在匹配它之后,我想检查该用户是否应该被修改并在单个原子查询/更新中更新它。问题是我可能有几个字段或数组条目需要检查。
在给定的示例中,我想要:
另一个问题是我可以有例如只是有关给定用户的新帖子(也可以是重复的)的信息,但它还不存在,因此我需要更新它。
所以我想介绍一些在单个查询中更新用户数据的用例,例如:
基于单一标准的更新似乎是直截了当的。如果没有聚合,我可以仅使用一个匹配条件来更新文档。聚合管道可以包含多个阶段,但如果在没有小组阶段的情况下满足某些条件,我不确定是否可以使用push阶段附加到数组。带条件推送的更新插入也存在问题。我最终忽略了 DuplicateKeyException (到目前为止没有聚合,因为如果我们处理重复项,我的服务的逻辑会依赖信息)。也许您知道更好的解决方案。
https://www.mongodb.com/docs/manual/reference/operator/aggregation/push/
请问这种方法是否可行?我想避免替换文档。我将不胜感激一些指南和/或示例查询。
简化文档:
[
{
"_id": 123,
"login": "john",
"details": {
"a": "abc",
"verified": true
},
"locations": [
{
"location": {
"country": "UK",
"region": "foo",
"city": "bar"
},
"createdAt": ISODate("2020-01-22T21:21:41.052Z")
}
],
"profiles": [
{
"preference": {
"a": "foo",
"b": "bar"
},
"details": {
"a": "foo",
"b": "bar"
},
"hash": "hash123",
"createdAt": ISODate("2020-01-22T21:21:41.052Z")
}
],
"posts": [
{
"postId": 234,
"message": "foo bar",
"createdAt": ISODate("2020-01-22T21:21:41.052Z")
},
{
"postId": 345,
"message": "bar foo",
"createdAt": ISODate("2020-01-22T21:21:41.052Z")
}
]
}
]
您的
locations
、profiles
和 posts
的 3 个更新共享相同的更新模式,可以通过以下更新解决:
$let
$cond
执行条件检查;根据结果更新或保留当前数组。如果需要更新,$concatArrays
使用当前数组和保存输入变量的单一大小数组。这可能看起来很抽象,所以这里是一个具体的例子
locations
{
"$set": {
"locations": {
"$let": {
"vars": {
// your location input here
"locationInput": {
"country": "TEST",
"region": "test",
"city": "test"
}
},
"in": {
"$cond": {
"if": {
$ne: [
"$$locationInput",
// last location
{
$last: "$locations.location"
}
]
},
"then": {
"$concatArrays": [
"$locations",
[
"$$locationInput"
]
]
},
"else": "$locations"
}
}
}
}
}
}
然后重复
$set
3次,3个案例。在操场上,为了清晰/可读,它们是分开的。如果愿意,您可以将它们放入一个 $set
中。