Firebase 在不读取快照或尝试写入(对于唯一用户名)的情况下检查值是否存在?

Firebase check if a value exists without reading a snapshot or trying a write (for unique usernames)?

我已经在这个问题上停留了好几个小时了,非常感谢您的帮助。尝试使用 Firebase 制作一个 Android 应用程序用于用户身份验证(简单登录电子邮件和密码)以及 唯一 用户名。我将我的注册屏幕字段布置在一个屏幕上,例如:

"Enter Username"
"Enter Email Address"
"Enter Password"

我很困惑如何查询数据库以检查用户名是否存在没有读取数据库快照和没有 试图将用户名写入数据库(因为这是在用户处于 auth == null 状态时发生的,所以在用户创建他的帐户之前在注册页面上我想通知用户他的用户名是否是否被带走)。

我的意思是,这感觉应该很简单,只是用一个字符串对 Firebase 进行简单查询,然后让 Firebase 变为 return TrueFalse,但经过数小时的谷歌搜索后,我一无所获。

我不想使用快照来执行此操作的原因是因为我不想通过将 "read" 设置为 true 将所有用户名及其 UID 公开给 public (我遵循了本指南,所以我的安全规则以及我的数据库结构都是这样设置的 )。

这是我的规则,它们目前有效(我不喜欢将读取设置为 True 的事实,这就是我问这个问题的原因):

{
  "rules": {
    "usernames": {
      ".read": true,
      "$username": {
        ".write": "auth !== null && !data.exists()"
      }
    },
    "users": {
        "$uid": {
            ".write": "auth !== null && auth.uid === $uid && !data.exists()",
            ".read": "auth !== null && auth.provider === 'password' && auth.uid === $uid",
            "username": {
              ".validate": "(!root.child('users').child(newData.val()).exists() || root.child('usernames').child(newData.val()).val() == $uid)"
     }
    }
   }
 }
}

这是我的数据:

{
  "usernames" : {
    "abcd" : "some-user-uid"
  },
  "users" : {
    "\"some-user-uid\"" : {
      "username" : "abcd"
    }
  }
}

谢谢!

不幸的是,如果不通过 SDK 实际下载数据,则无法测试数据是否存在。数据结构在这里将是至高无上的(推荐阅读:NoSQL Data Structures and you shouldn't be afraid to denormalize 优化和规模至关重要时的一点数据。

一般来说,您应该保持数据结构良好,以便有效负载较小并获取它。如果你正在获取一些不能等待字节被获取的东西(例如游戏,非常大的数据集上的奇怪 one-off 管理员操作等),那么这里有一些合理的方法来模拟这个:

正在通过 REST 获取键列表 API

在对 REST API 的调用中使用属性 shallow=true 将阻止加载大型数据集,并且 return 仅阻止加载该路径上的键。请注意,如果您存储一百万条记录,它们仍然必须加载到服务器上的内存中(慢)并且您仍然必须获取一百万个字符串(昂贵)。

因此,在不实际下载数据的情况下检查路径中数据是否存在的一种方法是调用路径,例如 https://<YOUR-FIREBASE-APP>.firebaseio.com/foo.json?shallow=true,并检查是否有任何键 returned.

正在创建一个您可以查询的非规范化索引

如果您真的需要从您的 Firebase 数据库中挤出一些额外的性能和速度(提示:您不需要这个,除非您每分钟 运行 数百万次查询并且可能仅用于游戏逻辑和类似),您可以 dual-write 您的记录(即非规范化),如下所示:

/foo/data/$id/... data goes here...
/foo/index/$id/true (just a boolean value)

要双重写入,您将使用更新命令,以及类似于以下内容的写入(Android SDK 示例):

public void addRecord(Map<String, Object> data) {
   DatabaseReference db = FirebaseDatabase.getInstance().getReference();

   // create a new record id (a key)
   String key = db.child("foo").push().getKey();

   // construct the update map
   Map<String, Object> dualUpdates = new HashMap<>();
   dualUpdates.put("/data/" + key, /* data here */);
   dualUpdates.put("/index/" + key, true);

   // save the new record and the index at the same time
   db.child("foo").updateChildren(dualUpdates);
}

现在要确定记录是否存在,无需实际下载数据,我可以简单地查询 /foo/index/$id 并尝试 DataSnapshot.exists(),但要下载一个布尔值。