在Firebase数据库中检查特定值是否存在

15

我正在使用 Firebase 实时数据库制作一个 Android 应用。当新用户在我的应用上注册时,该用户的数据将保存在 Firebase 数据库中。

用户必须提供以下详细信息进行注册:

  1. 全名
  2. 电子邮件地址
  3. 用户名
  4. 密码

数据库结构

enter image description here

每当新用户尝试注册时,我必须确保每个用户的用户名都是唯一的,因此我检查数据库以确定用户输入的 username 是否已经存在于数据库中。

为此,我编写了以下方法:

private boolean usernameExists(String username) {
     DatabaseReference fdbRefer = FirebaseDatabase.getInstance().getReference("Users/"+username);
     return (fdbRefer != null);
}

我采用这种方法的逻辑是,如果getReference方法找不到指定路径的引用,它会返回null,因此我可以返回fdbRefer是否为null。如果fdbRefernull,那么就意味着username在数据库中不存在。

问题

该方法存在的问题是,无论输入的username是否存在于数据库中,它总是返回true。这让我相信fdbRefer永远不会是null

这带来了我的问题...

问题

getReference方法在无法找到指定路径的引用时会返回什么?检查username是否已经存在于数据库中的正确方法是什么?


我非常欣赏你在SO社区提问的方式,感谢你的贡献。 - Nilesh Singh Dahiya
6个回答

31
为了检查用户是否存在,请使用以下代码:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userNameRef = rootRef.child("Users").child("Nick123");
ValueEventListener eventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if(!dataSnapshot.exists()) {
            //create new user
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
    }
};
userNameRef.addListenerForSingleValueEvent(eventListener);

您也可以使用查询来实现相同的功能,像这样:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("Users").orderByChild("userName").equalTo("Nick123");
query.addValueEventListener(/* ... */);

这是另一种方法,它通过循环整个Users节点,但不仅使用单个用户的直接引用。当您使用uid作为用户之间唯一标识符时(就像您现在所做的那样)更可能使用此选项。因此,如果您的数据库结构类似于此:
Firebase-root
   |
   --- Users
        |
        --- uid
             |
             --- userName: "Test User"
             |
             --- emailAddress: "user@email.com"

第二种解决方案是推荐的。
还有另一种解决方案,涉及创建另一个名为userNames的节点,在其中只保存唯一的用户名。请参考下面的安全规则:
"Users": {
  "$uid": {
    ".write": "auth !== null && auth.uid === $uid",
    ".read": "auth !== null && auth.provider === 'password'",
    "userName": {
      ".validate": "
        !root.child('userNames').child(newData.val()).exists() ||
        root.child('userNames').child(newData.val()).val() == $uid"
    }
  }
}

但在这种情况下,由于您的用户名已经是节点名称,我建议您采用第一个选项。


3
请注意这两种方法的区别。使用第一种解决方案将有助于节省带宽。 - Alex Mamo
1
dataSnapshot对象表示以下位置的快照:Firebase-root -> Users -> Nick123。最好的解决方案是直接在dataSnapshot上使用exists()方法来检查存在性。当您使用查询时,您正在遍历整个对象,这意味着浪费带宽。我也给出了查询解决方案,也许您将来会需要。 - Alex Mamo
@AlexMamo 你怎么知道第一个解决方案没有循环,而第二个有?你能分享一些证实这一点的Firebase文档吗?谢谢。 - Red M
1
@RedM 第一种解决方案是使用直接指向特定用户的引用,而第二种解决方案是使用查询来搜索整个对象以找到相应的用户。 - Alex Mamo
2
在这种情况下,您应该使用Peter的答案。 - Alex Mamo
显示剩余13条评论

3

试试这个:

DatabaseReference ref=FirebaseDatabase.getInstance().getReference().child("Users");
ref.orderByChild("username").equalTo(Nick123).addValueEventListener(new ValueEventListener(){
  @Override
  public void onDataChange(DataSnapshot dataSnapshot){
      if(dataSnapshot.exist() {
         //username exist
          }
        }

您需要使用orderbychildequalto来检查是否存在该值。 如果不使用orderbychildequalto,则只会检查username子节点是否存在,而不关心值。

这个orderByChild("username").equalTo(Nick123)就像在说:

WHERE username=Nick123

3

对我来说完美地工作了

DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
        Query query = reference
                .child(getString(R.string.dbname_users))
                .orderByChild("username")
                .equalTo(username);
        query.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if(dataSnapshot.getChildrenCount()>0) {
                    //username found

                }else{
                    // username not found
                }

            }
            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

2

您可以使用orderBy查询来检查用户名是否已经存在,而不是检查引用是否存在。

如果数据已经存在,orderByChild('username').equalTo(username)查询将返回数据,否则将返回null。


2

像这样检查...

    fdbRefer.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exist() {
//username exist
}
else {
//username doesn't exist
}
}
});

2

既然您已经找到了解决方案,我会尝试解释为什么您的代码没有起作用。

Original Answer翻译成"最初的回答"

 DatabaseReference fdbRefer = FirebaseDatabase.getInstance().getReference("Users/"+username);
你可能认为在上述语句中,如果数据库中不存在"Users/"+用户名,fdbRefer将为空。
但实际上,此语句仅会将指定字段的路径存储到getReference()中。因此,当你检查fdbRef!=null时,它始终为真,因为fdbRefer将保存类似于'https://.firebase.com/Users/Nick123'的值。"最初的回答"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接