mongodb - 嵌入重复的“友谊”子文档来模拟 MongoDB 中的相互友谊边缘
问题描述
我正在尝试在 MongoDB 中模拟双向友谊。“双向”意味着,像 Facebook 但与 Twitter 不同,如果您是 Sam 的朋友,那么 Sam 也必须是您的朋友。在 MongoDB 中,通常推荐的解决方案(example)似乎是这样的:
- 创建一个
User
集合(又名节点以使用正确的图论术语)和一个Friendship
集合(又名边) - 每个
User
文档都包含一个嵌入式friends
数组,其中每个元素都包含每个朋友的 ObjectID。每个元素还可以缓存有关每个朋友的只读信息(例如姓名、照片 URL),这可以避免针对“显示我的朋友列表”等常见用例的跨文档查询。 - 添加友谊包括插入一个新
Friendship
文档,然后$push
将Friendship
's Object ID 插入friends
两个用户的数组中,以及关于朋友的只读缓存信息(例如姓名),以避免在显示朋友列表时进行多文档查询。
我正在考虑一种不同的设计,而不是单独的Friendship
集合,边缘数据将存储(复制)在双向关系的两个节点中。像这样:
{
_id: new ObjectID("111111111111111111111111"),
name: "Joe",
pictureUrl: "https://foo.com/joe.jpg",
invites: [
... // similar schema to friends array below
],
friends: [
{
friendshipId: new ObjectID("123456789012345678901234"),
lastMeeting: new Date("2019-02-07T20:35:55.256+00:00"),
user1: {
userId: new ObjectID("111111111111111111111111"),
name: "Joe", // cached, read-only data to avoid multi-doc reads
pictureUrl: "https://foo.com/joe.jpg",
},
user2: {
userId: new ObjectID("222222222222222222222222"),
name: "Bill", // cached, read-only data to avoid multi-doc reads
pictureUrl: "https://foo.com/bill.jpg",
},
}
]
},
{
_id: new ObjectID("222222222222222222222222"),
name: "Bill",
pictureUrl: "https://foo.com/bill.jpg",
invites: [
... // similar schema to friends array below
],
friends: [
{
friendshipId: new ObjectID("123456789012345678901234"),
lastMeeting: new Date("2019-02-07T20:35:55.256+00:00"), // shared data about the edge
user1: { // data specific to each friend
userId: new ObjectID("111111111111111111111111"),
name: "Joe", // cached, read-only data to avoid multi-doc reads
pictureUrl: "https://foo.com/joe.jpg",
},
user2: { // data specific to each friend
userId: new ObjectID("222222222222222222222222"),
name: "Bill", // cached, read-only data to avoid multi-doc reads
pictureUrl: "https://foo.com/bill.jpg",
},
}
]
}
以下是我打算如何处理以下问题:
- 读取 - 常见操作的所有读取仅发生在单个
User
文档中。有关朋友的高级信息(例如姓名、图片 URL)被缓存在friends
数组中。 - 邀请朋友 - 将新文档添加到
invites
嵌入到两个用户中的数组(上面未显示),该数组在结构和功能上与friends
上面显示的集合相似 - 接受邀请 - 使用向两个用户的数组
updateMany
添加一个新的相同嵌入文档friends
,并$pull
从两个用户的数组中添加一个元素invites
。最初,我将使用多文档事务来进行这些更新,但由于添加友谊不是时间关键,这可以适应使用最终一致性。 - 取消友谊 -与来自两个用户数组的友谊子文档
updateMany
的过滤器一起{'friends.friendshipId': new ObjectID("123456789012345678901234")}
使用。像上面一样,这最初可以使用多文档事务,如果需要规模化,可以在以后使用最终的一致性。$pull
friends
- 更新缓存数据 - 如果用户更改缓存中的信息
friends
(例如名称或图片 URL),这是一个不常见的操作,可以进行缓慢且一次一个文档,因此最终的一致性很好。
我有两个基本问题希望得到您的建议:
上述方法有哪些问题和陷阱?我知道一些显而易见的事情:额外的存储、较慢的更新、需要添加排队和重试逻辑以支持最终一致性,以及边缘数据在两个副本之间不同步的风险。我想我可以解决这些问题。但是我可能会遇到其他不明显的问题吗?
与其为边的每个节点都设置一个字段
user1
,user2
不如使用一个 2 元素数组来代替?为什么或者为什么不?这是我的意思的一个例子:friends: [ { friendshipId: new ObjectID("123456789012345678901234"), lastMeeting: new Date("2019-02-07T20:35:55.256+00:00"), users: [ { userId: new ObjectID("111111111111111111111111"), name: "Joe", // cached, read-only data to avoid multi-doc reads pictureUrl: "https://foo.com/joe.jpg", }, { userId: new ObjectID("222222222222222222222222"), name: "Bill", // cached, read-only data to avoid multi-doc reads pictureUrl: "https://foo.com/bill.jpg", }, ], } ]
顺便说一句,我知道与 MongoDB 相比,图形数据库甚至关系数据库在建模关系方面做得更好。但是由于各种原因,我现在已经选择了 MongoDB,所以请限制对 MongoDB 解决方案的回答,而不是让我使用图形或关系数据库。谢谢!
解决方案
推荐阅读
- windows - 使用凭据打开 url 的批处理文件
- reactjs - 如何在 React Navigation 中单击底部选项卡时打开抽屉?
- html - 寻找一个好的 ePub3 样板/IDE
- arrays - Typescript Array 和 ChartJs 的问题
- oracle - Oracle CLOB 与 BLOB
- certificate - 如何用通配符证书替换“Kubernetes 假证书”(在裸机私有云上)Nginx Ingress 和证书管理器
- swift - Swift 5 LLDB 错误:警告:
:12:9: 警告:从未使用过变量“$__lldb_error_result”的初始化 - python - 使用 Python SDK 检索上个月 Azure 订阅的成本
- regex - jmeter中用下划线替换空格的正则表达式
- c# - 如何向我现有的 Specflow 测试添加另一个参数?