如果没有读取快照或尝试写入(对于唯一用户名),Firebase会检查是否存在值?

时间:2017-01-30 23:25:24

标签: android firebase firebase-realtime-database firebase-authentication firebase-security

我已经坚持这个问题好几个小时,所以任何帮助都表示赞赏。尝试使用Firebase制作Android应用以进行用户身份验证(简单登录电子邮件和密码)以及唯一用户名。我的注册屏幕字段放在一个屏幕上,例如:

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

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

我的意思是,这感觉应该非常简单,只需要使用字符串对Firebase进行简单查询,然后让Firebase返回 True False ,但之后几个小时的谷歌搜索我什么都找不到。

我不想使用快照执行此操作的原因是因为我不想通过将“read”设置为true来向公众公开我的所有用户名和他们的UID(我遵循了本指南以便我的安全性)规则就像这样设置,以及我的数据库结构Firebase android : make username unique)。

这是我的规则,它们目前正在工作(我不喜欢读取设置为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"
    }
  }
}

谢谢!

1 个答案:

答案 0 :(得分:1)

遗憾的是,没有办法测试数据是否存在而没有通过SDK实际下载。数据结构在这里将是至关重要的(建议阅读:NoSQL Data Structures,当优化和规模至关重要时,您不应该害怕denormalize一些数据。

一般来说,您应该保持数据结构良好,以便有效负载很小并获取它。如果您正在获取无法等待获取字节的内容(例如,游戏,奇怪的一次性管理员操作非常大的数据集等等),那么这里有一些合理的方法来模拟这个:

通过REST API获取密钥列表

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

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

创建可以查询的非规范化索引

如果您确实需要从Firebase数据库中挤出一些额外的性能和速度(提示:除非您每分钟运行数百万次查询,并且可能仅用于游戏逻辑和类似操作,否则您不需要这样做),您可以按如下方式双写您的记录(即非规范化):

/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(),代价是下载一个布尔值。