如何在 Firebase 中正确地反规范化数据

How to properly denormalize data in Firebase

我对 NoSQL 和非规范化非常陌生。但是,我希望在我的应用程序中允许 SignUp 处的操作定义为:

  1. 如果用户名已被占用,则不允许用户使用它
  2. 如果phone号码已经被占用,则不允许用户使用它
  3. 允许新用户 "sync" 他们的 phone 号码与服务器联系以确定谁是当前用户,并检索他们各自的 uid

鉴于快速需要检查用户注册时是否已存在 username/phone 号码,以及新用户联系 [= 时需要的搜索和比较,我的架构如下所示29=] 号码是 link 给应用程序中已经存在的用户:

{
  "presentUsersByPhoneNumber" : {
    "1614#######" : {
      "uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
    },
    "1614#######" : {
      "uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
    },
    "1614#######" : {
      "uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
    },
    "1614#######" : {
      "uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
    }
  },
  "presentUsersByUsername" : {
    "ak" : {
      "uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
    },
    "ak2" : {
      "uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
    },
    "ak3" : {
      "uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
    },
    "kja" : {
      "uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
    }
  },
  "users" : {
    "1783917f-00e4-47a0-b2cd-987bdf185129" : {
      "phoneNumber" : "614#######",
      "username" : "ak3"
    },
    "99e4989b-a046-4c5f-9478-5ebd8bdc3ded" : {
      "phoneNumber" : "1614#######",
      "username" : "ak2"
    },
    "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd" : {
      "phoneNumber" : "1614#######",
      "username" : "ak1"
    },
    "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de" : {
      "phoneNumber" : "1614#######",
      "username" : "kja"
    }
  }
}

这种方法在非规范化行为中是否过于公平?

在 NoSQL 中,您应该根据应用程序访问数据的方式对数据进行建模。阅读此 article on NoSQL data modeling 了解更多信息。

因此,如果您需要一种有效的方法来检查 phone 号码或用户名是否已被占用,那么为它们存储映射是有意义的。我可能会做的唯一不同的事情就是将它们存储为简单类型:

"phoneNumberToUid" : {
    "1614#######" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
    "1614#######" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"usernameToUid" : {
    "ak" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
    "ak2" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
}

我在您的示例数据中注意到的一件事是您在 presentUsersByUsername 中有一个键 ak,但在 users 中没有对应的子 name .这通常是因为您的代码中途中止或者因为您在开发过程中的某个时刻犯了错误。

您可以通过以下方式预防许多此类问题:

  1. 使用多位置更新,以便所有写入都作为单个命令发送到 Firebase

    ref.update({
      '/users/a96da7b1-7c4e-44bc-b82e-fc75bed52bcd/username': 'ak1',
      '/usernameToUid/ak': null,
      '/usernameToUid/ak1': 'a96da7b1-7c4e-44bc-b82e-fc75bed52bcd'
    });
    

    此更新是将用户名称从 ak 更改为 ak1、擦除旧映射并添加新映射的最安全方法。

  2. 使用验证规则确保每个名称的用户都存在

    "usernameToUid": {
      "$username": {
        ".validate": "newData.parent().parent().child(newData.va()).child('username').val() == $username"
      }
    }