MongoDB:单个文档中的条件更新

问题描述 投票:0回答:1

上下文:我创建了一项处理用户数据的服务。我无法修改上游系统中的数据源和顺序(通常它是用户或增量的部分快照(例如用户的新帖子)。我还必须处理重复的信息。我围绕文档数据库(MongoDB )使用单个“用户”集合,因为我处理没有任何关系的独立用户,我不需要更新多个表,并且我的读取模型类似于我的文档的架构。我总是可以通过他的/来识别用户她的 _id 和登录名,所以每当我需要更新用户的信息时,我可以只更新一个文档。此外,通常我不想覆盖用户的数据,而是附加它,以便文档创建用户的日志。我只是在非常罕见的情况(当数据可为空但只会更新一次(详细信息)或者我不关心中间状态(详细信息.已验证)。

问题:每当我收到有关给定用户的信息时,在匹配它之后,我想检查该用户是否应该被修改并在单个原子查询/更新中更新它。问题是我可能有几个字段或数组条目需要检查。

在给定的示例中,我想要:

  1. 匹配_id为123的用户
  2. 仅当最后一个位置与查询中的位置不同时,才将位置条目添加到位置数组中。
  3. 配置文件非常复杂,因此我引入了哈希。仅当最后一个哈希值与查询中提供的哈希值不同时,我才想添加新的配置文件条目。
  4. 仅当新帖子不包含具有给定 postId 的帖子时,才将新帖子添加到 posts 数组中。

另一个问题是我可以有例如只是有关给定用户的新帖子(也可以是重复的)的信息,但它还不存在,因此我需要更新它。

所以我想介绍一些在单个查询中更新用户数据的用例,例如:

  • 用户不存在,我们添加其详细信息
  • 我们追加了一篇尚未保留的新帖子
  • 我们正在尝试追加已保存的帖子
  • 我们追加一个新帖子并检查位置是否未更改(并根据需要进行更新)
  • 我们通过比较哈希值来检查最新用户的个人资料是否已更改,并根据需要进行更新,同时将信息添加到用户的详细信息中。

基于单一标准的更新似乎是直截了当的。如果没有聚合,我可以仅使用一个匹配条件来更新文档。聚合管道可以包含多个阶段,但如果在没有小组阶段的情况下满足某些条件,我不确定是否可以使用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")
      }
    ]
  }
]

https://mongoplayground.net/p/UsS72apI23F

mongodb aggregation-framework event-log
1个回答
0
投票

您的

locations
profiles
posts
的 3 个更新共享相同的更新模式,可以通过以下更新解决:

  1. 使用
    $let
  2. 分配输入变量
  3. 使用
    $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
中。

蒙戈游乐场

© www.soinside.com 2019 - 2024. All rights reserved.