检查值在 Firebase 中是否已存在?

huangapple go评论70阅读模式
英文:

Check value already exists or not in Firebase?

问题

这是我的 Firebase 数据库结构。我想检查用户将要输入的新大学是否已经存在于大学列表中?问题是大学节点有随机键,所以我不知道如何检查大学的有效性?
如果大学已经存在,就不应该再次写入。

英文:

This is my Firebase Database structure. I want to check in Universities that new University which user will enter already exists or not? Problem is universities node have random key So I am not getting idea how can i check validity of Universities?
If university already exists It should not write it again

检查值在 Firebase 中是否已存在?

答案1

得分: 1

在你当前的结构中,你可以使用以下查询检查大学名称在 /Universities 下作为一个值是否已经存在:

String name = "Comsats";
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Universities");
ref.orderByValue().equalTo(name).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        Log.i("Firebase", "已存在 "+dataSnapshot.getChildrenCount()+" 个名为 "+name+" 的大学");
        for (DataSnapshot universitySnapshot: dataSnapshot.getChildren()) {
            Log.i("Firebase", universitySnapshot.getKey() + ": " + universitySnapshot.getValue(String.class));
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
});

或者只是检查是否存在,这会稍微简单/更快:

ref.orderByValue().equalTo(name).limitToFirst(1).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if (dataSnapshot.exists()) {
            Log.i("Firebase", "大学 "+name+" 已经存在");
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
});

然而,如果你使用上述方式来添加大学,你会遇到竞态条件。当一个客户端正在检查名称是否已经存在时,另一个客户端可能正在添加它。由于操作的顺序不能保证符合你期望的顺序,你仍然可能会得到两个具有相同名称的大学。

唯一防止这种情况的方法是将大学名称用作节点的,而不是值。你的结构将变成:

"Universities": {
  "University of Haripu": true,
  "Comsats": true,
  "UET": true
}

节点键在特定位置下保证唯一,因为无法插入具有相同名称的第二个子节点。因此,使用这种结构可以自动在数据结构中满足唯一性要求。

上述结构中的true值没有特定的含义,只是因为 Firebase 不能存储没有值的键。如果有更有意义的值,你也可以使用。实际上,你可能需要这样做...


Firebase 有一些不允许出现在节点键中的字符,但允许出现在值中。如果你的值可能包含这些字符,你需要对用户输入的值进行编码,以便存储为大学的键。

两种常见的编码方式是:

  1. 从字符串中删除不允许的字符。
  2. 使用字符串的哈希码。

第一种方法会产生更易识别的键,所以我会在这里使用它。实际上,第二种方法在代码中通常更好、更简单,因为哈希功能在现代通常是标准功能,而其他编码可能会根据你的用例或 Firebase 的情况而定制。

在两种情况下,你都需要存储用户实际输入的值。所以,假设键不允许包含空格和大写字符(实际上这两者都是允许的,所以这只是一个说明性的例子),你最终会得到:

"Universities": {
  "universityofharipu": "University of Haripu",
  "comsats": "Comsats",
  "uet": "UET"
}
英文:

In your current structure you can check how often the university name already exists as a value under /Universities with a query like this:

String name = "Comsats";
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Universities");
ref.orderByValue().equalTo(name).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        Log.i("Firebase", "There are "+dataSnapshot.getChildrenCount()+" universities named "+name);
        for (DataSnapshot universitySnapshot: dataSnapshot.getChildren()) {
            Log.i("Firebase", universitySnapshot.getKey+": "+universitySnapshot.getValue(String.class));
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
}

Or just to check if it exist at all, this would be slightly simpler/faster:

ref.orderByValue().equalTo(name).limitToFirst(1).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if (dataSnapshot.exists()) {
            Log.i("Firebase", "University "+name+" already exists");
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        throw databaseError.toException();
    }
}

If you use the above however to then add the university, you end up with a race condition. While one client is checking whether the name already exists, another might be adding it. Since the operations are not guaranteed to be in the order you'd like them, you could still end up with two universities with the same name.

The only way to prevent this is to use the university name as the key* of the nodes instead of as the value. Your structure would become:

"Universities": {
  "University of Haripu": true,
  "Comsats": true,
  "UET": true
}

Node keys are guaranteed to be unique under a certain location, as there's no way to insert a second child node with the same name. So with this structure you automatically guarantee your uniqueness requirement in the data structure.

The true values have no specific meaning in the above structure, and are just there because Firebase can't store a key without a value. If you have a more meaningful value, you can use that too. In fact, you may have to do that....


Firebase has a few characters that are not allowed to be present in a node key, which are allowed in the values. If your values may have such values, you'll want to perform some encoding from the value the user entered to the key that you store for the university.

Two common encodings are:

  1. Remove the offending characters from the string.
  2. Use the hashcode of the string.

The first approach leads to more recognizable keys, so I'll use that here. In practice the second approach is usually better and simpler in code, as hashing is pretty standard functionality these days while other encodings are likely going to be custom for your use-case or Firebase.

In both cases, you'll then want to store the actual value the user entered. So say that spaces and uppercase characters aren't allowed for keys (they are both allowed, so this is just an illustrative example), you'd end up with:

"Universities": {
  "universityofharipu": "University of Haripu",
  "comsats": "Comsats",
  "uet": "UET"
}

Guaranteeing uniqueness has been covered quite a few times before, so I recommend checking out these:

huangapple
  • 本文由 发表于 2020年9月12日 23:10:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/63861763.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定