为我的应用推荐的 Firebase 数据结构

Recommended Firebase data structure for my app

问题是关于我为我的 Firebase 应用程序定义的数据结构。 它正在工作,但是,我对效率和可扩展性表示怀疑。

想象一个只有一个全球聊天室的聊天应用程序 但您只会看到来自与您有关系的用户的消息。 "Relation" 在这个意义上意味着您共享相同的组成员资格。 这些是组:

+---------+---------+---------+
| Group A | Group B | Group C |
+---------+---------+---------+
| User1   | User4   | User7   |
| User2   | User5   | User8   |
| User3   | User6   | User1   |
+---------+---------+---------+

请注意,User1 是 A 组和 C 组的成员。 这个概念是用户 1 将看到用户 2、3、7 和 8 发布的消息 而用户 4 只会看到来自用户 5 和 6 的消息。 为此,每个用户的关系存储在Firebase实时数据库中,如下所示:

Friends
├── User1
│   ├── User2: true
│   ├── User3: true
│   ├── User7: true
│   └── User8: true
├── User2
    ├── User1: true
    └── User3: true

等等。出于演示目的,我在这里使用用户名,实际上,这些是 Firebase UID。 显然,这需要将许多 (users_per_group)² 条目写入数据库。 可能的组数和每个组的用户数是无限的。 消息会像这样添加到 Firebase:

Messages
├── User1
│   ├── timestamp
│   │   └── message: "This is a message"
│   ├── timestamp
│   │   └── message: "This is another message"
├── User2
    ├── timestamp
    │   └── message: "..."

权限的实际执行很容易通过以下 Firebase 安全规则实现:

"Messages": {
    "$uid": {
        // allow read only if the current user is a friend of the message creator
        // OR if the user is the creator of the message
        ".read": "(root.child('Friends/' + $uid + '/' + auth.uid).val() === true || $uid === auth.uid)"
    }
}

这是推荐的数据结构方式吗? 由于跟踪用户关系所需的数据量,我有点不确定。

当我们谈论效率和可扩展性时,Firebase 中最重要的规则是让数据尽可能扁平化。根据这个规则,我建议你像这样改造你的数据库:

firebase-url
    |
    --- users
    |     |
    |     ---- userId_1
    |     |       |
    |     |       ---- userName: "John"
    |     |       |
    |     |       ---- userAge: 30
    |     |       |
    |     |       ---- groups
    |     |              |
    |     |              ---- groupName1 : true
    |     |              |
    |     |              ---- groupName2 : true
    |     |
    |     ---- userId_2
    |             |
    |             ---- userName: "Anna"
    |             |
    |             ---- userAge: 25
    |             |
    |             ---- groups
    |                    |
    |                    ---- groupName3 : true
    |                    |
    |                    ---- groupName4 : true
    |
    ---- groups
    |      |
    |      ---- groupIdId_1
    |            |
    |            ---- groupName: "groupName1"
    |            |
    |            ---- users
    |                   |
    |                   ---- userId_1: true
    |                   |
    |                   ---- userId_2: true
    |
    --- messages
          |
          ---- groupId_1
                  |
                  ---- messageId_1
                  |       |
                  |       ---- messageText: "Hello!"
                  |       |
                  |       ---- messageTimeStamp: 1492189663846
                  |
                  ---- messageId_2
                          |
                          ---- messageText: "Hy!"
                          |
                          ---- messageTimeStamp: 1492189685692

通过这种方式,您可以非常简单地查询数据库以显示属于一个组的所有用户:firebase-url/groups/groupId/users/。用户所在的所有群组:firebase-url/users/userId/groups/ 以及来自单个群组的所有消息:firebase-url/groupId_1/

要了解有关正确构建 Firebase 数据库的更多信息,请阅读这篇文章 post

希望对您有所帮助。

很抱歉让您失望了,但不幸的是,您不能使用此数据结构来实现您想要实现的目标。

首先,只要组中的用户不是太多,数据树的性能应该没问题。我通过重建您概述的数据结构并在 Firebase 实时数据库中创建他们之间的“朋友”关系的同时将一个又一个玩家添加到组中来测试解决方案的性能。这些是结果:

Added user #10. Needed time: 0.00156599283218384 seconds
Added user #30. Needed time: 0.00276100635528564 seconds
Added user #50. Needed time: 0.00490301847457886 seconds
Added user #100. Needed time: 0.013949990272522 seconds
Added user #150. Needed time: 0.0460770130157471 seconds
Added user #200. Needed time: 0.0947499871253967 seconds
Added user #300. Needed time: 0.451290011405945 seconds
Added user #400. Needed time: 1.81872600317001 seconds

如您所见,性能对于较小的团体来说似乎完全没问题。

但问题主要不是你想如何存储数据,而是你想如何获取数据。您写道,您希望使用数据库规则来获取允许用户根据其“朋友”关系查看的所有消息。但这是某种过滤机制,借助数据库规则进行过滤 prohibited/not 在 Firebase 实时数据库中是可能的。

来自Firebase docs

Rules Are Not Filters

Rules are applied in an atomic manner. That means that a read or write operation is failed immediately if there isn't a rule at that location or at a parent location that grants access. Even if every affected child path is accessible, reading at the parent location will fail completely.

还有...

.read and .write rules work from top-down, with shallower rules overriding deeper rules.

这意味着您必须直接为您​​的 Messages 父节点定义某种读取规则。但是此规则将覆盖您对各个子节点的规则。

希望对您有所帮助!