我想创建可扩展的数据(跟踪用户的私人数据).
Firebase文档建议将子对象嵌套在父对象下,如下所示:
{
"users": {
"google:1234567890": {
"displayName" : "Username A",
"provider" : "google",
"provider_id" : "1234567890",
"todos": {
"rec_id1": "Walk the dog",
"rec_id2": "Buy milk",
"rec_id3": "Win a gold medal in the Olympics",
...
}
}, ...
}
}
(其中rec_id是Firebase.push()生成的唯一键.)
但正如Denormalizing Your Data is Normal中提到的那样,我认为以这种方式构造它会更好:
{
"users" : {
"google:1234567890" : {
"displayName" : "Username A",
"provider" : "google",
"provider_id" : "1234567890"
}, ...
},
"todos" : {
"google:1234567890" : {
"rec_id1" : {
"todo" : "Walk the dog"
},
"rec_id2" : {
"todo" : "Buy milk"
},
"rec_id3" : {
"todo" : "Win a gold medal in the Olympics"
}, ...
}, ...
}
}
然后只允许用户写入/读取它自己的数据应用以下安全规则:
{
"rules": {
"users": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid"
}
},
"todos": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid"
}
}
}
}
由于我是这类数据库的新手,我想知道是否有任何缺点我想要构建它的方式.
如第一个例子中所推荐的那样,将所有待办事项直接嵌套在用户下面会更好吗?
1> Sinan Bolel..:
首先,如果你还没有遇到过几个资源:
Firebase文档中"入门指南"的"构建数据"部分?
正如您所说,该指南的"创建可扩展的数据"部分是重要的部分.
另外,请查看"关于Firebase非规范化博客文章的澄清"的答案
编辑:你显然遇到了这些资源,因为它在你的问题中已经链接了,但我建议你多阅读一下"构建你的数据"指南.
编辑2:看到这个答案 @Frank van Puffelen发布在In firebase中,使用单独的端点建模许多关系是一个好主意吗?
而且这个
我还建议阅读规范化[1] [2]和非规范化[3] [4],我认为可能存在一些混淆,两者中你指的是哪一个.
这将我们带到你的场景......
您设置数据的两种方式实际上完成了同样的事情!
但是,您会注意到,第一个示例实际上是在"构建您的数据"指南中列为反模式.
如果要加载用户数据,然后在不同时间加载该用户,则以第二种方式执行此操作将非常有用.
如果您只有一个用户访问每个待办事项列表,那么设置它的方式很好.
例如,我在应用程序中执行此操作,我知道每个用户在历史记录列表中只有一个位置,我只想在某些情况下加载用户的历史记录.
/users/$userUid
给我用户数据并/history/$userUid
给我用户的历史记录.
它可以轻松分割加载.
但是,如果todo列表在不同用户之间共享并且必须从多个源更新,则此结构不会带来任何好处.
如果您想要共享访问,那么您就是在正确的轨道上,只需要使用密钥作为参考.
不同的方法是:
/todos/$uid
您可以将新todo
对象推送到新的对象,/todos
以便获取新的唯一ID(称为密钥),而不是在其下显式设置待办事项对象.
然后,将该键添加到正确user
对象的todos
子项中.
这将允许您首先加载用户的数据,并仅获取用户所属的待办事项的索引(键),以及
然后,您可以todos
独立加载用户所属的所有内容.
这样做会:
防止用户对象变得庞大
允许多个用户更新单个用户todo
,而无需在多个位置更新其子参数.
通过将其拆分为单独的路径来获得可伸缩数据.
以下是本指南"创建数据量表"部分的最后一个数据样本:( 我添加了一些评论)
// An index to track Mary's memberships
{
"users": {
"mchen": {
"name": "Mary Chen",
// index Mary's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
// these keys are used to figure out which groups should
// be loaded (at whatever appropriate time) for Mary,
// without having to load all the group's data initially (just the keys).
"alpha": true,
"charlie": true
}
},
...
},
// Here is /groups. In here, there would be a group with the key
// 'alpha' and another one with the key 'charlie'. Once Mary's
// data is loaded on the client, you would then proceed to load
// the groups from this list, since you know what keys to look for.
"groups": { ... }
}
这实现了更平坦的结构.
正如文件所说,
是.这是双向关系的必要冗余.它允许我们快速有效地获取Mary的成员资格,即使用户或组的列表可以扩展到数百万,或者安全和Firebase规则会阻止访问某些记录.
所以问题是,您的数据如何才能让用户拥有多个待办事项列表,这些列表也可以与其他用户共享?
这是一个例子:
{
"users" : {
"google:1234567890" : {
"displayName" : "Username A",
"provider" : "google",
"provider_id" : "1234567890",
"todoLists" : {
"todoList1": true,
"todoList2": true
}
},
"google:0987654321" : {
"displayName" : "Username B",
"provider" : "google",
"provider_id" : "0987654321",
"todoLists" : {
"todoList2": true
}
}
},
"todoLists" : {
"todoList1" : {
// 'members' user for rules
"members" : {
"google:1234567890" : true
},
"records" : {
"rec_id1" : {
"todo" : "Walk the dog",
"createdAt" : "1426240376047"
},
"rec_id2" : {
"todo" : "Buy milk",
"createdAt" : "1426240376301"
},
"rec_id3" : {
"todo" : "Win a gold medal in the Olympics",
"createdAt" : "1426240376301"
}
}
},
"todoList2" : {
"members" : {
"google:1234567890" : true,
"google:0987654321" : true
},
"records" : {
"rec_id4" : {
"todo" : "Get present",
"createdAt" : "1426240388047"
},
"rec_id5" : {
"todo" : "Run a mile",
"createdAt" : "1426240399301"
},
"rec_id6" : {
"todo" : "Pet a cat",
"createdAt" : "1426240400301"
}
}
}
}
}
在这种情况下,用户A将加载两个列表,但用户B将仅加载第二个列表.如果你正确地设置规则,一切都会很好.
在操作中,您首先要加载用户的数据,然后在用户todoLists中加载每个待办事项列表, /todoList
但实际上,如果您正在创建一个应用程序,其中一个用户有一个todo列表,其中只有一个内容的待办事项,那么所有这些都是完全没必要的.
注意,这些"ID"应该是一个唯一的密钥,可以使用Firebase.push()
.
有关使用push生成新ID的信息,请参阅此答案.
总而言之,这完全取决于您的应用程序何时以及何时需要您的数据,数据更新频率以及由谁更新,以及最小化不必要的读取和观察者.空间通常很便宜,操作(和观察者)通常不是.
最后但同样重要的是,规则和安全是另一个非常重要的考虑因素 指南的最后一部分说:
"因此,索引速度更快,效率更高.后来,当我们谈论保护数据时,这种结构也非常重要.因为Security和Firebase规则不能在子节点列表上做任何"包含" ,我们将依赖广泛使用这样的密钥."
这是早期的,我希望我不是在喋喋不休,但是当我第一次从了解MySql到使用非结构化时,我遇到了同样的问题,所以我希望有所帮助!