首页 > 解决方案 > Firestore - 适合用户特定数据的 NoSQL 结构

问题描述

我正在使用 Firestore 开发应用程序。我使用 Firebase 身份验证来管理用户。问题是我的应用程序中的大部分数据都是特定于用户的。这意味着用户只能访问自己的数据。

我已经研究过 Firestore 安全规则,所以我知道如何处理安全部分。

但是,我想知道如何正确地为用户特定的数据建模。我要添加的第一条数据是categories,它(目前)有 2 个属性:nametype.

经过一番研究,我发现最好先创建一个名为的集合categories,然后为每个用户创建一个文档,每个文档都以用户 ID (UID) 命名。

但是,在这样一个特定于用户的文档中,我想添加我的类别。我想出来的一种方法就像下面的屏幕截图一样:

firestore 数据模型 - 类别集合中每个用户的文档,然后映射每个类别的名称和类型

因此,以更通用的方式描述这种方法:

  1. 每种数据类型的新集合(例如categories
  2. 在集合中,每个用户使用 UID 命名的单独文档
  3. 在每个用户特定的文档中,带有对象数据的category_1地图(例如,带有字段的地图name = groceriestype = expense

但是,这里让我担心的是,我需要以某种方式发明这些地图的名称,例如category_1category_2等等......我感觉这个模型有问题,但是我强大的 SQL 背景不允许我通过思考

您是否知道这个模型是否是一个好的模型,或者它以后会产生什么问题?也许您可以建议一种更好的方法来在 Firestore 数据库中对特定于用户的数据进行建模?

标签: firebasegoogle-cloud-firestore

解决方案


单个用户可以拥有多少个类别有任何限制吗?如果没有,那么最好为类别创建一个集合以避免达到1 MB 最大文档大小。如果有限制并且您决定使用地图,我建议创建一个地图字段categories并将它们作为子字段,如下所示,因此如果您在文档中添加任何其他字段,它将更加分类

{
  categories: {
    category_1: {
      name: "",
      type: ""
    },
    category_2: {
      name: "",
      type: ""
    }
  }
}

但是,如果每个类别将来获得更多数据并且您需要对类别进行一些查询,那么创建子集合也可能是更好的选择。

users -> {userId} -> categories -> {categoryId}
(col)     (doc)        (col)          (doc)

由于类别位于子集合中,因此您无需在每个类别文档中添加 userId 字段。但是,您也可以创建 2 个不同的根集合,即 - 用户和类别:

users -> {userId}
(col)      (doc)

categories -> {categoryId}
(col)           (doc)

在这种情况下,您必须存储userId字段以在这些类别的所有者之间进行过滤。

如果您需要深入的解释,@Alex Mamo在这篇文章中完美地解释了使用根级别集合和子集合之间的区别:在 Firestore 中使用根集合与子集合有什么好处?


在这两种情况下,安全规则会有所不同。如果您使用子集合,那么您可以像这样从通配符中读取用户 UID:

match /users/{userId}/categories/{categoryId} {
  allow read, write: if request.auth != null && request.auth.uid == userId;
}

但是,如果您使用根级别的集合,每个类别的文档 ID 通常不会有用户的 UID,但 UID 将作为字段存储在文档中。在这种情况下,您必须userId从所请求的文档中阅读。

match /categories/{categoryId} {
  allow read, write: if request.auth != null && request.auth.uid == resource.data.userId;
}

我在这里担心的是我需要以某种方式发明这些地图的名称

NoSQL 数据库没有架构。您可以根据需要动态添加任何键。只需将每个文档视为 JSON 对象。

const newKey = "category_123"
const newValue = {name: "newCatName", type: "expense"}

const userDoc = firebase.firestore().collection("users").doc("userId")
userDoc.update({
  [`categories.${newKey}`]: newValue
})

此更新操作将使用提供的值在“类别”地图中创建一个新子项。


推荐阅读