Zic*_*sus 44 java android database-design firebase google-cloud-firestore
我在我的测试社交网络应用程序中使用Firebase实时数据库,您可以在其中关注并接收您关注的人员的帖子.传统的社交网络.我将我的数据库构造成这样的东西 -
Users
--USER_ID_1
----name
----email
--USER_ID_2
----name
----email
Posts
--POST_ID_1
----image
----userid
----date
--POST_ID_2
----image
----userid
----date
Timeline
--User_ID_1
----POST_ID_2
------date
----POST_ID_1
------date
Run Code Online (Sandbox Code Playgroud)
我还有另一个节点"内容",它只包含所有用户帖子的ID.因此,如果"A"跟在"B"之后,那么B的所有帖子ID都添加到A的时间轴.如果B发布了一些内容,那么它也会添加到其所有关注者的时间轴中.
现在这是我的实时数据库解决方案,但它显然存在一些可扩展性问题
这些都是一些问题.
现在,我正在考虑将整个事情转移到firestore上,因为它声称是"可扩展的".那么我应该如何构建我的数据库,以便在firestore中消除我在实时数据库中遇到的问题.
Ale*_*amo 34
我稍后见过您的问题,但我也会尝试为您提供我能想到的最佳数据库结构.所以希望你会发现这个答案很有用.
我正在考虑一个有三个顶级集合的模式users,users that a user is following和posts.
Firestore-root
|
--- users (collection)
| |
| --- uid (documents)
| |
| --- name: "User Name"
| |
| --- email: "email@email.com"
|
--- following (collection)
| |
| --- uid (document)
| |
| --- userFollowing (collection)
| |
| --- uid (documents)
| |
| --- uid (documents)
|
--- posts (collection)
|
--- uid (documents)
|
--- userPosts (collection)
|
--- postId (documents)
| |
| --- title: "Post Title"
| |
| --- date: September 03, 2018 at 6:16:58 PM UTC+3
|
--- postId (documents)
|
--- title: "Post Title"
|
--- date: September 03, 2018 at 6:16:58 PM UTC+3
Run Code Online (Sandbox Code Playgroud)
如果有人拥有10,000个关注者,则新帖子被添加到所有10,000个关注者的时间线中.
这根本不是问题,因为这就是Firestore中收藏的原因.根据Cloud Firestore数据库建模的官方文档:
Cloud Firestore针对存储大量小型文档进行了优化.
这就是我userFollowing作为集合而不是作为可以容纳其他对象的简单对象/地图添加的原因.请记住,根据有关限制和配额的官方文档,文档的最大大小为1 MiB (1,048,576 bytes).在收集的情况下,对集合下面的文档数量没有限制.事实上,对于这种结构,Firestore进行了优化.
因此,以这种方式拥有这10,000名粉丝,将完美无缺.此外,您可以以不需要在任何地方复制任何内容的方式查询数据库.
正如您所看到的,数据库几乎是非规范化的,允许您非常简单地查询它.让我们举一些例子,但在让我们创建一个与数据库的连接并uid使用以下代码行获取用户之前:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
Run Code Online (Sandbox Code Playgroud)
如果要查询数据库以获取用户正在关注的所有用户,可以使用get()以下引用上的调用:
CollectionReference userFollowingRef = rootRef.collection("following/" + uid + "/userFollowing");
Run Code Online (Sandbox Code Playgroud)
因此,通过这种方式,您可以获得用户正在关注的所有用户对象.有了threir uid,你可以简单地获得所有的帖子.
假设您希望在时间轴上了解每个用户的最新三篇帖子.当使用非常大的数据集时,解决此问题的关键是以较小的块加载数据.我在我的答案从这个解释后建议的方式,您可以通过查询游标与合成分页查询limit()方法.我还建议您看一下这个视频,以便更好地理解.因此,要获得每个用户的最新三篇帖子,您应该考虑使用此解决方案.因此,首先您需要获取您正在关注的前15个用户对象,然后根据他们uid获取最新的三个帖子.要获取单个用户的最新三篇帖子,请使用以下查询:
Query query = rootRef.collection("posts/" + uid + "/userPosts").orderBy("date", Query.Direction.DESCENDING)).limit(3);
Run Code Online (Sandbox Code Playgroud)
在向下滚动时,加载其他15个用户对象并获取最新的三个帖子,依此类推.除此之外,date您还可以为post对象添加其他属性,例如喜欢,评论,分享等数量.
如果有人拥有大量帖子,那么每个新粉丝都会在他的时间轴中收到所有这些帖子.
没门.没有必要做这样的事情.我已经解释了为什么.
Alb*_*haw 14
如果您的网络上有相当数量的活动(例如关注 1,000 人的人,或发布 1,000 个帖子的人),其他答案将变得非常昂贵。
我的解决方案是向每个用户文档添加一个名为“recentPosts”的字段,该字段将是一个数组。
现在,每当发布帖子时,都会有一个云函数来检测 onWrite(),并更新recentPosts其 userDocument 上的海报数组以添加有关该帖子的信息。
因此,您可以将以下映射添加到recentPosts数组的前面:
{
"postId": xxxxxxxxxxx,
"createdAt": tttttt
}
Run Code Online (Sandbox Code Playgroud)
将recentPosts 数组限制为 1,000 个对象,超过限制时删除最旧的条目。
现在,假设您正在关注 1,000 个用户并想要填充您的摘要...获取所有 1,000 个用户文档。这将算作 1k 次读取。
拥有 1,000 个文档后,每个文档都会有一个recentPosts. 将客户端上的所有数组合并到一个主数组中,并按createdAt排序。
现在,您可能拥有多达 100 万个帖子的 docID,全部按时间顺序排序,仅适用于 1,000 次读取。现在,当您的用户滚动他们的 feed 时,只需根据需要通过其 docID 查询这些文档,大概一次 10 个或其他。
现在,您可以加载 Y 位关注者的 X 个帖子的摘要以供Y + X阅读。
因此,来自 100 个关注者的 2,000 个帖子只能获得 2,100 次阅读。
因此,来自 1,000 个关注者的 1,000 个帖子只能获得 2,000 次阅读。
ETC...
编辑1)进一步优化。in加载 userDocuments 时,您可以使用查询一次对它们进行 10 次批处理...通常这不会有什么区别,因为即使是批处理,它仍然是 10 次读取...但您也可以按字段进行过滤recentPostsLastUpdatedAt,并检查它是否是大于该用户文档的缓存值,那么任何尚未更新其recentPosts数组的用户文档将不会被读取。理论上,这可以节省 10 倍的碱基读取量。
编辑 2) 您也可以将侦听器附加到每个 userDocument,以便在最近的帖子发生变化时获取新帖子,而无需每次需要刷新提要时查询每个关注者。(虽然 1,000 多个快照侦听器可能是不好的做法,但我不知道它们在幕后是如何工作的)(Edit3:Firebase 将项目限制为只有 1k 个侦听器,因此 edit2 不是可扩展的优化)
Niy*_*yas 12
有两种情况
您应用中的用户拥有少量关注者。
您应用中的用户拥有大量关注者。如果我们要将整个关注者存储在 firestore 中的单个文档中的单个数组中。然后它将达到每个文档 1 MiB 的 Firestore 限制。
在第一种情况下,每个用户必须保留一个文档,该文档将关注者列表存储在单个数组中的单个文档中。通过使用arrayUnion(),arrayRemove()可以有效地管理关注者列表。当您要在时间线中发布内容时,您必须在发布文档中添加关注者列表。
并使用下面给出的查询来获取帖子
postCollectionRef.whereArrayContains("followers", userUid).orderBy("date");
Run Code Online (Sandbox Code Playgroud)在第二种情况下,您只需要根据关注者数组的大小或数量来打破用户关注文档。在将数组的大小达到固定大小后,下一个关注者的 id 必须添加到下一个文档中。并且第一个文档必须保留字段“hasNext”,该字段存储一个布尔值。添加新帖子时,您必须复制帖子文档,并且每个文档都包含较早中断的关注者列表。我们可以使用上面给出的相同查询来获取文档。
我一直在为她建议的解决方案苦苦挣扎,主要是由于技术差距,所以我想出了另一个适合我的解决方案。
对于每个用户,我都有一个包含他们关注的所有帐户的文档,以及所有关注该用户的所有帐户的列表。
当应用程序启动时,我会获取关注当前用户的帐户列表,当用户发布帖子时,帖子对象的一部分是所有关注他们的用户的数组。
当用户 B 也想获得他们关注的人的所有帖子时,我只是向查询添加一个简单的whereArrayContains("followers", currentUser.uid).
我喜欢这种方法,因为它仍然允许我按我想要的任何其他参数对结果进行排序。
基于:
这种方法应该适用于拥有约 37,000 名关注者的用户。
我浏览了一些 Firebase 文档,我很困惑为什么https://firebase.google.com/docs/database/android/structural-data#fanout上的建议实现在您的情况下不起作用。像这样的东西:
users
--userid(somedude)
---name
---etc
---leaders:
----someotherdude
----someotherotherdude
leaders:
--userid(someotherdude)
---datelastupdated
---followers
----somedude
----thatotherdude
---posts
----postid
posts
--postid
---date
---image
---contentid
postcontent
--contentid
---content
Run Code Online (Sandbox Code Playgroud)
该指南继续提到“这是双向关系的必要冗余。它允许您快速有效地获取 Ada 的成员资格,即使用户或组列表规模扩大到数百万。”,所以它似乎并不存在可扩展性完全是 Firestore 的事情。
除非我遗漏了一些东西,否则主要问题似乎是时间线节点本身的存在。我知道它可以更轻松地生成特定用户时间线的视图,但这是以必须维护所有这些关系为代价的,并且会严重延迟您的项目。使用查询根据提交的用户从类似于上述的结构动态构建时间线是否效率太低?
| 归档时间: |
|
| 查看次数: |
6627 次 |
| 最近记录: |