【问题标题】:Which is a more optimal Firestore schema for getting a Social Media feed?哪个是获取社交媒体提要的最佳 Firestore 架构?
【发布时间】:2019-10-26 07:56:08
【问题描述】:

我正在考虑将 Firestore 用于社交媒体供稿的几个想法。到目前为止,我的想法还没有付诸实施,所以我希望得到社区的反馈。

这个想法是允许用户发布信息或记录他们的活动,并向任何关注/订阅该信息的用户显示它。帖子信息将位于名为 posts 的根集合中。

据我所知,这些方法需要大致相同数量的读取和写入。

一个想法是在users/{userId} 中有一个名为posts 的字段,它是我有兴趣为用户提取的documentId 数组。这将允许我直接从posts 中提取数据并获取最新版本的数据。

另一种方法似乎更像 Firebasey,即将文档存储在 users/{userId}/feeds 中,它们是帖子本身的副本。我可以使用与posts 中的数据相同的postID。据推测,如果我需要更新任何评论的数据,我可以使用组集合查询来获取所有称为提要的集合,其中 docID 是相等的(或者只是创建一个字段来做一个正确的"where", "==", docId)。

第三种方法是更新应该查看帖子的人员列表。只要帖子列表比关注者列表短,这似乎更好。不是维护每个关注者的所有帖子,而是维护每个帖子的所有关注者。对于每个新关注者,您需要更新所有帖子。

此列表不会是用户自己的帖子。相反,它将是显示该用户的所有帖子的列表。

三个挑战者:

  1. users/{userId} 字段名为 feed - 指向全局帖子的文档 ID 数组。获取该提要,按 ID 获取所有文档。每次用户有活动时,都需要为每个关注者更新每个数组。

    users (coll)
        -> uid (doc)
        -> uid.feed: postId1, postId2, postId3, ...] (field)
    
    posts (coll)
        -> postId (doc)
    

查询(伪):

doc(users/{uid}).get(doc)
    feed = doc.feed
    for postId in feed:
        doc(posts/{postId}).get(doc)
  1. users/{userId}/feed 包含您希望此用户看到的所有 posts 的副本。每个活动/帖子都需要添加到每个相关的提要列表中。

    users (coll)
        -> uid (doc)
             -> feed: (coll)
                   -> postId1 (doc)
                   -> postId2
                   -> postId3
    
    posts (coll)
        -> postId (doc)
    

查询(伪):

collection(users/{uid}/feed).get(docs)
    for post in docs:
        doc(posts/{post}).get(doc)
  1. users/{userId}/feed 包含您希望此用户看到的所有 posts 的副本。每个活动/帖子都需要添加到每个相关的提要列表中。

    users (coll)
        -> uid (doc)
    
    
    posts (coll)
        -> postId (doc)
        -> postId.followers_array[followerId, followerId2, ...] (field)
    

查询(伪):

collection(posts).where(followers, 'array_contains', uid).get(docs)

读/写

1.更新数据 对于每个活动的作者user,找到所有关注该活动的用户 用户。目前,用户被存储为集合中的文档,所以这是 followerNumber 文档读取。对于每个用户,通过添加 postId 来更新他们的数组,这将是 followerNumber 文档写入。

1.显示数据/Feed 对于提要的每次获取:从用户文档中获取数组(读取 1 个文档)。对于每个 postId,请致电 posts/{postId}

这将是 numberOfPostsCalled 文档读取。

2。更新数据 对于每个活动的作者user,找到所有关注该活动的用户 用户。目前,用户被存储为集合中的文档,所以这是 followerNumber 文档读取。对于每个用户,将 ID 为 postId 的新文档添加到 users/{userId}/feed,这将是 followerNumber 文档写入。

2。显示数据/Feed 对于提要的每次获取:从users/{userId}/feed获取一定数量的帖子

这将是 numberOfPostsCalled 文档读取。

第二种方法要求我在编辑时使所有文档保持最新。因此,尽管这种方法看起来更像 firebase,但持有 postId 并直接获取它的方法似乎更合乎逻辑。

3。更新数据 对于每个新关注者,被关注者撰写的每个帖子都需要更新。新的追随者被附加到一个名为追随者的数组中。

3。显示数据 对于提要的每次提取:从posts 获取一定数量的帖子,其中uid == viewerUid

【问题讨论】:

  • 我真的很想讨论这个问题,但我是一个有眼光的人,你介意对你的模式和你没有完成的查询做样本吗?
  • 您心目中的最佳视觉效果是什么?您只是指架构的表格布局吗?很高兴!
  • 是的,这已经足够了,你也尝试过的查询:)
  • 嘿@andresmijares 我刚刚添加了视觉和第三种方法。我实际上喜欢第三种方法,但我可以看到维护所有帖子会变得很麻烦。获取所有帖子是一个不太复杂的查询,但不确定它是否有回报。
  • 我只添加了显示部分,我认为它比查看上传数据的架构更容易阅读。我放了 3. 为 3 种方法更新数据。

标签: firebase google-cloud-firestore


【解决方案1】:

很好,当我谈到什么更优化时,我真的需要一个点或一个质量属性来比较,我会假设你关心速度(不是必要的性能)和成本。

这就是我解决问题的方法,它涉及多个集合,但我的目标只是 1 个查询。

用户(列)

{
 "abc": {},
 "qwe": {}
}

帖子(列)

{
  "123": {},
  "456": {}
}

users_posts (col)

{
  "abc": {
    "posts_ids": ["123"]
  }
}

到目前为止一切顺利,但问题是,我需要进行多次查询才能获取所有帖子信息……这就是云功能进入游戏的地方。您可以创建第 4 个集合,您可以在其中预先计算您的提要

users_dashboard

{
  "abc": {
    posts: [
    {
       id: "123", /.../
    }, {
       id: "456", /.../
     }
    ]
  }
}

云函数如下所示:

/* on your front end you can manage the add or delete ids from user posts */
export const calculateDashboard = functions.firestore.document(`users_posts/{doc}).onWrite(async(change, _context) {
   const firestore = admin.firestore()
   const dashboardRef = firestore.collection(`users_dashboard`)
   const postRef = firestore.collection(`posts`)

   const user = change.after.data()
   const payload = []
   for (const postId of user.posts_ids) {
      const data = await postRef.doc(postId).get().then((doc) => doc.exists ? doc.data() : null)
      payload.push(data)
   }
   // Maybe you want to exponse only certain props... you can do that here
   return dashboardRef.doc(user.id).set(payload) 
})

文档的最大大小为1 MiB (1,048,576 bytes),您可以在其中存储大量数据,因此您可以在这里发布很多帖子。让我们谈谈成本;我以前认为 Firestore 更像是拥有几个小文档,但我发现在实践中它同样适用于将大尺寸文档放入大量文档中。

现在在您的仪表板上您只需要查询:

const dashboard = firestore.collection(`users_dashboard`).doc(userID).get()

这是解决此问题的一种非常自以为是的方法。您可以避免使用users_posts,但也许您不想为与帖子相关的更改触发此过程。

【讨论】:

  • 感谢分享,安德烈斯。为了总结一下,这需要为用户的每个追随者更新,对吧?因此,如果用户 A 关注用户 B,则需要更新用户 A 的仪表板以合并用户 B 的帖子。以及用户 A 关注的每个帖子。所以用户 C 发了一个帖子,用户 C 的所有关注者的提要都需要更新。我正在尝试优化速度和成本,现在强调成本,以便最大限度地减少写入和读取。这种方法似乎可行,似乎是上面方法#1和#2的组合。
  • 是的,先生,这是正确的,最好在云功能上执行此操作,这样用户不必为此付费,而且很便宜:)
  • 我想更新帖子类似于 collection('users_dashboard').where(post.postID == postId) 那么你可以只更新地图数组中的一个元素吗?您是否认为这里的写入数量已优化,每个新帖子 + 关注者的所有这些更新?顺便说一句,非常感谢!
  • 是的,这可以工作,老实说,不要担心写入次数,你只做了几次,你会做很多读取,我一直在处理这个大约一年的生产方法,收集了超过 9 万条记录...我的发票不到 10 美元??‍♂️,首先为用户优化,而不是稍后再做任何安排
  • 澄清一点:user_posts 的目的是充当用户关注者和确保 users_dashboard 更新之间的中介?我可以查找一个新帖子,然后获取该帖子作者的所有关注者,然后通过将数据添加到他们的 feeds.posts 数组来生成所有这些用户的提要?这一切都说得通,同时针对用户进行优化也是很好的建议
【解决方案2】:

看起来你的第二种方法在这种情况下是最好的。我真的不明白@andresmijares 想要做什么,他提到了将帖子存储在文档中这不是一个好方法,想象一下如果你有超过 20K 个帖子(我认为一个文档可以容纳),那么该文档将无法存储更多数据。更好的方法是将帖子作为文档存储在 Collection 中(就像在您的第二个选项中一样) .. 所以让我们在这里回忆一下什么是最好的方法。

1)_ 您在(帖子“收藏”)和您关注的用户(订阅“收藏”)中分享帖子。也许这可以通过云功能完成,不要忘记聚合(使用云功能也)需要出现在用户个人资料中的帖子数量。

2)_ 您关注一个用户并将他们的所有帖子从(帖子“收藏”)获取到您的(Feed“收藏”)中,这样您就可以在您的 Feed 上看到他们的所有帖子。

使用这种方法,一次会有很多写入,但读取会很快。如果您的应用程序是关于阅读更多和更少写入,那么除非我错了,否则没有什么可担心的。

【讨论】:

  • 我比安德烈斯更接近这个方向。在文档中存储在实践中几乎没有意义。所以我所做的只是在一个名为 feeds 的集合中为用户关注的每个用户的每个帖子创建一个文档。更容易处理。通过like limit(10)获取最新Feed很简单
  • @DanFein 你是在云端还是前端处理??如果是云端,功能如何?
  • 云端。我寻找这个:exports.userFollowed = functions.firestore .document('users/{userId}/following/{otherUserId}') .onCreate((snap, context) => { 如果我看到了,我会抓住最后一个10 个帖子并批量写入 feedsRef.doc(${userId}/posts/${postId}),这基本上只是每个用户的提要集合。
  • 所以如果一个用户有 1000 个关注者,你会创建文档 1001 次?我也不确定这是否有意义。这也会使更新帖子变得困难,因为您必须在所有 1001 个位置进行更新
  • 事实证明,Firestore 并不是“社交网络”数据库的最佳选择。我选择了图形数据库。
猜你喜欢
  • 2011-01-24
  • 2020-01-22
  • 2023-03-26
  • 2019-06-10
  • 2021-02-12
  • 2021-07-19
  • 1970-01-01
  • 2019-04-20
  • 2011-02-03
相关资源
最近更新 更多