【问题标题】:Firebase database: Referencing a dynamic valueFirebase 数据库:引用动态值
【发布时间】:2016-06-04 09:07:59
【问题描述】:

假设我正在尝试构建一个群消息应用程序,因此我将数据库结构设计为如下所示:

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() 调用更新用户名。

这真的是最好的方法吗?我真的需要复制这么多数据(数百万条消息),只是因为我引用了一个动态值(用户名)吗?

或者在处理动态数据时有更好的方法吗?

【问题讨论】:

标签: firebase firebase-realtime-database


【解决方案1】:

这是一个完美的例子,说明为什么通常父节点名称(键)应该与它们包含或表示的值分离。

因此,一些大局思维可能会有所帮助,考虑用户体验可能会提供答案。

现在假设在我的应用程序中,我想显示 每封邮件的发件人用户名。

但你真的想这样做吗?您的用户真的想要滚动浏览包含 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

每个用户登录并且应用程序执行一个查询来观察他们所属的消息节点 - 应用程序显示消息的消息文本以及也观察该消息的每个用户名称(“组”) .

这也可以用于仅显示发布它的用户的用户名。

解决方案一:应用启动时,将用户节点中的所有用户加载到字典中,以uid_为key。

当消息节点被观察时,每条消息都被加载,你将把其他用户(或海报)的 uid 按键存储在 users_dict 中,所以只需选择他们的名字:

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/用户名。再次,尽管这涉及到异步计时问题,您将异步调用嵌套在异步调用或循环中,这可能应该避免。

任何解决方案的重点是用户体验和保持 Firebase 结构尽可能平坦。

例如,如果您确实想要加载 10,000 条消息,请考虑将消息文本或主题拆分到另一个节点中,然后仅为您的初始 UI 列表加载这些节点。随着用户深入了解消息,然后加载其余数据。

【讨论】:

    【解决方案2】:

    要遵循的步骤:

    • 每次重启应用时获取用户名

    • 在本地缓存它们

    • 根据 uid 从缓存中显示用户名

    • 完成

    注意:如何获取用户名取决于您的实现方式

    【讨论】:

      【解决方案3】:

      你只需要这个结构

        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 条消息 ...2000 条更新只需一次 updateChildren() 调用这将是非常低效的,因为它不可扩展,并且您正在更新在现实生活场景中肯定没有用户会再次看到的数据(例如 2000 条聊天消息中的第一条消息)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-02-20
        • 1970-01-01
        • 1970-01-01
        • 2018-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多