代码之家  ›  专栏  ›  技术社区  ›  Justin Grant

在MongoDB中嵌入重复的“友谊”子文档来模拟相互友谊边缘

  •  0
  • Justin Grant  · 技术社区  · 6 年前

    example

    • User 集合(aka nodes使用适当的图论术语)和 Friendship 集合(又名边)
    • 每个 用户 friends 数组,其每个元素包含每个朋友的ObjectID。每个元素还可以缓存关于每个朋友的只读信息(例如姓名、照片URL),这样可以避免对常见用例(如“display my friend list”)进行跨文档查询。
    • 添加友谊需要插入一个新的 友谊 记录然后 $push -在 友谊 的对象ID

    我在考虑一个不同的设计 友谊 集合时,边缘数据将存储(复制)在双向关系的两个节点中。这样地:

      {
        _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",
            },
          }
        ]
      }
    

    以下是我计划如何处理以下问题:

    • 用户 数组。
    • 邀请朋友-将新文档添加到 invites 在结构和功能上与 朋友 上面显示的集合
    • 已接受邀请-使用 updateMany 朋友 两个用户的数组,以及 $pull 元素来自 邀请 两个用户的数组。最初,我将使用多文档事务处理这些更新,但由于添加友谊不是时间关键,因此可以对其进行调整以使用最终的一致性。
    • 更新名称 带过滤器 {'friends.friendshipId': new ObjectID("123456789012345678901234")} $拉 来自两个用户的友谊子文档 朋友 数组。与上面一样,这可以在开始时使用多文档事务,如果需要扩展,则最终使用一致性。
    • 朋友 (例如,名称或图片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解决方案的回答,而不是让我使用图形或关系数据库。谢谢!

    0 回复  |  直到 6 年前