让我们假设我正在尝试构建一个群发消息应用程序,因此我设计了我的数据库结构:
users: {
uid1: { //A user id using push()
username: "user1"
email: "aaa@bbb.ccc"
timestampJoined: 18594659346
groups: {
gid1: true,
gid3: true
}
}
uid2: {
username: "user2"
email: "ddd@eee.fff"
timestampJoined: 34598263402
groups: {
gid1: true,
gid5: true
}
}
....
}
groups: {
gid1: { //A group id using push()
name: "group1"
users: {
uid1: true,
uid2: true
}
}
gid2: {
name: "group2"
users: {
uid5: true,
uid7: true,
uid80: true
}
}
...
}
messages: {
gid1: {
mid1: { //A message id using push()
sender: uid1
message: "hello"
timestamp: 12839617675
}
mid2: {
sender: uid2
message: "welcome"
timestamp: 39653027465
}
...
}
...
}
根据Firebase的文档,这将扩展得很好。
现在让我们假设在我的应用程序中,我想在每条消息上显示发件人的用户名。
查询每条消息的用户名显然很糟糕,因此我找到的解决方案之一就是在每条消息中复制用户名。
消息节点现在看起来像这样:
messages: {
gid1: {
mid1: { //A message id using push()
sender: uid1
username: "user1"
message: "hello"
timestamp: 12839617675
}
mid2: {
sender: uid2
username: "user2"
message: "welcome"
timestamp: 39653027465
}
...
}
...
}
现在我想为用户添加更改用户名的选项。
因此,如果用户决定更改其用户名,则必须在用户节点以及他发送的每条消息中进行更新。
如果我选择“每个消息的监听器”方法,那么更改用户名会很容易,因为我需要在一个位置更改名称。
现在,我还必须更新他发送的每个组的每条消息中的名称。
我认为查询整个消息节点的用户ID是一个糟糕的设计,所以我考虑创建另一个节点来存储用户发送的所有消息的位置。
看起来像这样:
userMessages: {
uid1: {
gid1: {
mid1: true
}
gid3: {
mid6: true,
mid12: true
}
...
}
uid2: {
gid1: {
mid2: true
}
gid5: {
mid13: true,
mid25: true
}
...
}
...
}
现在,我可以快速获取特定用户的所有消息的位置,并使用单个updateChildren()调用更新用户名。
这真的是最好的方法吗?我是否真的必须复制这么多数据(数百万条消息),因为我正在引用一个动态值(用户名)?
或者在处理动态数据时是否有更好的方法?
答案 0 :(得分:3)
这是一个很好的例子,说明为什么父节点名称(键)应该与它们包含或表示的值取消关联。
所以一些大局观可能有所帮助,并且考虑到用户体验可能会提供答案。
现在让我们假设在我的应用程序中,我想显示 每封邮件的发件人用户名。
但你真的想这样做吗?您的用户是否真的要滚动浏览10,000条消息的列表?可能不是。最有可能的是,该应用程序将显示这些消息的一个子集,甚至可能一次显示10或12个消息。
以下是一些想法:
假设用户表:
users
uid_0
name: Charles
uid_1
name: Larry
uid_2:
name: Debbie
和消息表
messages
msg_1
sender: uid_1
message: "hello"
timestamp: 12839617675
observers:
uid_0: true
uid_1: true
uid_2: true
每个用户登录并且应用程序执行查询以观察他们所属的消息节点 - 应用程序显示器显示消息的消息文本以及每个用户名也会观察该消息( '组'。)
这也可以用于显示发布它的用户的用户名。
解决方案1:当应用程序启动时,加载用户节点中的所有用户将其存储在字典中,并以uid_为键。
当观察消息节点时,每个消息都被加载,你将按键存储在users_dict中的其他用户(或海报)的uid,所以只需选择他们的名字:
let name = users_dict["uid_2"]
解决方案2:
假设您的用户节点(这是典型的)和一千个用户存储了大量数据。当您感兴趣的是他们的名字时,加载所有数据是没有意义的,所以你可以
a)使用解决方案#1并忽略除了uid和名称或
之外的所有其他数据b)创建一个单独的名称' firebase中的节点,它只保留用户名,因此您不需要将其存储在用户节点中。
names:
uid_0: Charles
uid_1: Larry
uid_2: Debbie
正如您所看到的,即使有几千个用户,也只需加载一小部分数据。而且......这里很酷的是,如果您向名称节点添加一个侦听器,用户更改其名称将通知应用程序,并可相应地更新您的UI。
解决方案3:
根据需要加载您的姓名。虽然从技术上讲你可以做到这一点,但我不推荐它:
观察用户所属的所有消息节点。这些节点将被读入并在读入时,构建一个你需要名字的uid字典。然后根据uid对每个用户名执行查询。这可以工作,但您必须考虑Firebase的异步特性,并留出时间来加载名称。同样,您可以加载消息,然后使用路径加载该消息的用户名:users / uid_x / USER_NAME。虽然这会进入异步计时问题,你在异步调用或循环中嵌套异步调用,这应该是可以避免的。
使用任何解决方案的重要一点是用户体验并尽可能保持Firebase结构。
例如,如果您确实想要加载10,000条消息,请考虑将消息文本或主题分解为另一个节点,并仅为初始UI列表加载这些节点。当用户深入查看消息时,请加载其余数据。
答案 1 :(得分:1)
要遵循的步骤:
每次重启app时都会获取用户名
在本地缓存
根据uid显示来自缓存的用户名
注意:获取用户名的方式取决于您的实施方式
答案 2 :(得分:1)
你只需要这个结构
mid1: { //A message id using push()
sender: uid1
message: "hello"
timestamp: 12839617675
}
可以直接从用户读取用户名" users / uid1 / username"在阅读每个孩子后使用单值事件监听器。 Firebase应该与顺序调用一起使用,因为您无法在SQL中创建复杂的查询,
为了保持高效,你可以:
1)创建一个引用字典,将其用作缓存处理程序,在您阅读每条消息后,验证您是否拥有每个密钥的值:
[uid1:"John",uid2:"Peter",....etc...]
如果密钥不存在,则使用指向/ users / $ uid / username的单值监听器添加,以处理"添加到缓存"在其回调中
2)使用limitTo startAt和endAt查询对侦听器进行分页,避免引入用户赢得的数据
*没有必要在每次用户更改时实际更新所有消息和所有节点,想象一个包含100个用户的聊天组,其中每个用户都有20条消息...使用单个updateChildren()调用进行2000次更新这将是非常低效的,因为它不可扩展,你正在更新数据肯定没有用户将在现实生活场景中再次看到(如2000聊天消息的第一条消息)