为Firebase中的私有数据访问对数据结构进行非规范化? [英] Denormalizing data structures for private data access in Firebase?
问题描述
我想创建可扩展的数据(以跟踪用户的私人数据).
I would like to create data that scales (to track private data of a user).
Firebase文档建议将子对象嵌套在父对象下,如下所示:
The Firebase documentation recommends to nest the child objects under the parent like this:
{
"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()生成的唯一密钥.)
(Where rec_id is a unique key generated by Firebase.push().)
但是,正如对数据进行规范化是正常的我认为最好采用这种结构:
But as mentioned in Denormalizing Your Data is Normal I think it would be better to structure it this way:
{
"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"
}, ...
}, ...
}
}
然后仅允许用户写入/读取自己的数据,请应用以下安全规则:
And then to only allow the user to write/read it's own data apply the following security rules:
{
"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"
}
}
}
}
由于我是这种数据库的新手,所以我想知道我在构造数据库方面是否存在任何缺点.
As I'm new to this kind of databases I wonder if there are any downsides the way I'd like to structure it.
按照第一个示例的建议,将所有待办事项直接嵌套在用户下会更好吗?
Would it be better to nest all todos directly under the user as recommended in the first example?
推荐答案
首先,如果您还没有遇到这些资源,请提供一些资源:
First, a couple resources if you haven't run into them yet:
- "Structuring Your Data" portion of the Getting Started Guide in the Firebase Docs?
- "Creating Data That Scales" section of that guide is the important part, as you've said.
- Also, check out this answer to "Clarification on Firebase denormalization blog post"
由于您的问题中有链接,您显然已经使用了这些资源,但我建议您再读一遍《结构化数据》指南.
- EDIT 2: See this answer @Frank van Puffelen posted for In firebase, is modeling many-many relationships using a separate endpoint a good idea?
- And this
这使我们适应您的情况...
That brings us to your scenario...
建立数据的两种方式实际上可以完成几乎相同的事情!
The two ways you have your data set up actually accomplish almost the same thing!
不过,您会注意到,第一个示例实际上在指南.
You'll notice though, that the first example is actually listed as an anti-pattern in the "Structuring Your Data" guide.
- 如果要加载用户数据,然后在其他时间加载该用户的待办事项,则第二种方法很有用.
- 如果只有一个用户访问每个待办事项列表,则设置方式很好.
- 例如,我在一个应用程序中执行此操作,我知道每个用户在历史记录列表中都有一个位置,并且我只想在某些情况下加载用户的历史记录.
-
/users/$userUid
给了我用户数据,而/history/$userUid
给了我用户历史记录. - 这使得分割负载变得容易.
- Doing it your second way would be useful if you want to load a users data, and then that user's todos at a different time.
- The way you have it set up is fine if you only have one user accessing each todo list.
- For example, I do this in an app where I know that each user has one single location in a history list, and I only want to load the history of a user in certain scenarios.
/users/$userUid
gives me the user data and/history/$userUid
gives me the user's history.- It makes it easy to segment the loading.
不同的方法是:
- 您可以将新的
todo
对象推送到/todos
,而不是在/todos/$uid
下显式设置todo对象,以便它获得新的唯一ID(称为键). - 然后,将该键添加到正确的
user
对象的todos
子级. - 这将允许您首先加载用户的数据,并仅获取用户所属的待办事项的索引(键),并且
- 然后可以独立加载用户所属的所有这些
todos
.
- Instead of explicitly setting todo objects under
/todos/$uid
, you can push a newtodo
object to/todos
so that it gets a new unique ID (called key). - Then, you add that key to the correct
user
object'stodos
child. - This will allow you to load the user's data first and get only the indices (keys) of the todos to which the user belongs, and
- You can then load all those
todos
the user belongs to independently.
以这种方式进行操作
- 防止用户对象变大
- 允许多个用户更新单个
todo
,而不必跨多个位置更新其子参数. - 通过将数据分成单独的路径来拥有可伸缩数据.
- Prevent user objects from getting huge
- Allow multiple users to update a single
todo
, without having to update it's child parameters across multiple location. - Have scalable data by splitting it into separate paths.
这是中的最后一个数据样本指南中的创建可扩展的数据" 部分:(添加了一些评论)
// 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规则阻止访问某些记录时也是如此.
Yes. This is a necessary redundancy for two-way relationships. It allows us to quickly and efficiently fetch Mary's memberships, even when the list of users or groups scales into the millions, or when Security and Firebase Rules would prevent access to some of the records.
问题是,您的数据的外观如何,以便允许用户拥有多个可以与其他用户共享的待办事项列表?
So the question is, how could your data look in order to allow a user to have multiple todo lists which can be shared with other users as well?
这里是一个例子:
{ "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将仅加载第二个列表.如果您正确设置规则,一切都会很好.
- 在操作中,您首先要加载用户的数据,然后再从
/todoList
加载用户todoList中的每个待办事项列表.
- In this case, user A would load both lists, but user B would only load the second list. If you set up your rules correctly, all would be well.
- In operation, you would first load the user's data, then load each todo list in a users todoLists, from
/todoList
- 但是,实际上,如果您要开发的应用程序中一个用户只有一个待办事项列表,而待办事项只有一个内容,那么这一切都是不必要的.
- 旁注,这些"id"可能应该是唯一键,可以使用此答案使用推送生成新ID.
- But really, all of this is totally unnecessary if you're making an app where one user has one todo list with todo items that just have a single content.
- Side note, these "ids" should probably be a unique key, which can be accomplished using
Firebase.push()
.- See this answer for using push to generate a new ID.
总结,这实际上取决于应用程序如何以及何时需要数据,数据多久更新一次以及由谁更新,以及最大程度地减少不必要的读取和观察者.空间通常很便宜,但操作(和观察员)通常却不便宜.
In conclusion, it all really comes down to how and when your app needs your data, how often data is updated and by whom, and also to minimizing unnecessary reads and watchers. Space is usually cheap, operations (and watchers) usually aren't.
最后但并非最不重要的一点是,规则和安全性是另一个极其重要的考虑因素.指南的最后一部分说:
Last but not least, rules and security are another extremely important consideration. The last part of the guide says:
因此,索引更快且效率更高.稍后,当我们讨论保护数据安全时,此结构也将非常重要.由于安全规则和Firebase规则无法在列表上执行任何包含"操作子节点,我们将广泛依赖使用此类键."
"Thus, the index is faster and a good deal more efficient. Later, when we talk about securing data, this structure will also be very important. Since Security and Firebase Rules cannot do any sort of "contains" on a list of child nodes, we'll rely on using keys like this extensively."
现在还很早,我希望我不会感到困惑,但是当我第一次从只了解MySql到使用非结构化时,我遇到了同样的问题,所以希望能对您有所帮助!
It's early and I hope I'm not blabbering, but I ran into these same questions when I first went from only knowing MySql to using unstructured, so I hope that helps!
这篇关于为Firebase中的私有数据访问对数据结构进行非规范化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!