替换firebase密钥中无效字符的好方法?

替换firebase密钥中无效字符的好方法?,firebase,Firebase,我的用例是保存用户的信息。当我尝试使用用户的电子邮件地址作为密钥将数据保存到Firebase时,Firebase抛出以下错误: 错误:无效密钥e@e.ee(不能包含$[].) 所以,显然,我不能通过他们的电子邮件索引用户信息。更换的最佳做法是什么 我成功地将更改为-,但这并不能阻止它,因为有些电子邮件的地址中有- 目前,我正在使用 var cleanEmail = email.replace('.','`'); 但在这一点上可能会有冲突 我们已经多次处理过这个问题,虽然从表面上看,使用电子邮件

我的用例是保存用户的信息。当我尝试使用用户的电子邮件地址作为密钥将数据保存到Firebase时,Firebase抛出以下错误:

错误:无效密钥e@e.ee(不能包含
$[].

所以,显然,我不能通过他们的电子邮件索引用户信息。更换
的最佳做法是什么

我成功地将
更改为
-
,但这并不能阻止它,因为有些电子邮件的地址中有
-

目前,我正在使用

var cleanEmail = email.replace('.','`');

但在这一点上可能会有冲突

我们已经多次处理过这个问题,虽然从表面上看,使用电子邮件作为密钥似乎是一个简单的解决方案,但它会导致许多其他问题:必须清理/解析电子邮件,以便实际使用。如果电子邮件发生变化怎么办

我们发现,更改数据存储方式的格式是一条更好的途径。假设您只需要存储一个东西,用户名

john@somecompany.com: "John Smith"
改成

randomly_generated_node_name
   email:  "john@somecompany.com"
   first:  "John"
   last:   "Smith"
随机生成的\u节点\u名称是Firebase可以通过childByAutoId生成的字符串,或者实际上是任何不直接绑定到数据的引用类型

这提供了很大的灵活性:你现在可以改变人们的姓氏——比如说他们结婚了。或者更改他们的电子邮件。您可以添加一个可用于排序的“索引”子级0、1、2等。可以查询任何子数据的数据。这都是因为随机生成的节点名称是对节点内变量子数据的静态引用

它还允许您在将来扩展数据,而无需更改现有数据。添加地址、喜爱的食物、分类索引等

编辑:用于ObjC中电子邮件的Firebase查询:

//references all of the users ordered by email
FQuery *allUsers = [myUsersRef queryOrderedByChild:@"email"];

//ref the user with this email
FQuery *thisSpecificUser = [allUsers queryEqualToValue:@“john@somecompany.com”]; 

//load the user with this email
[thisSpecificUser observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
  //do something with this user
}];

在电子邮件地址中,将点
替换为逗号
。这种模式是最佳实践

逗号
在电子邮件地址中是不允许的字符,但在Firebase密钥中是允许的。对称地,点
在电子邮件地址中是允许的字符,但在Firebase密钥中是不允许的。因此,直接替换将解决您的问题。您可以为电子邮件地址编制索引,而无需循环

你还有另一个问题

const cleanEmail = email.replace('.',','); // only replaces first dot
将仅替换第一个点
,但电子邮件地址可以有多个点。要替换所有点,请使用正则表达式

或者,您也可以使用
split()
-
join()
模式替换所有点

const cleanEmail = email.split('.').join(','); // also replaces all dots

我可以想出两个主要的方法来解决这个问题:

  • 编码/解码功能
  • 由于Firebase密钥中允许的字符集有限,解决方案是将密钥转换为有效格式(encode)。然后使用反向函数(解码)将编码密钥转换回原始密钥

    一般的编码/解码功能可能是将原始密钥转换为字节,然后将其转换为十六进制表示。但密钥的大小可能是一个问题

    假设您希望使用电子邮件作为密钥存储用户:

    # path: /users/{email} is User;
    /users/alice@email.com: {
        name: "Alice",
        email: "alice@email.com"
    }
    
    上面的例子不起作用,因为路径中有点。因此,我们使用encode函数将密钥转换为有效格式<代码>alice@email.com十六进制是
    616c69636540656d61696c2e636f6d
    ,然后:

    # path: /users/{hex(email)} is User;
    /users/616c69636540656d61696c2e636f6d: {
        name: "Alice",
        email: "alice@email.com"
    }
    
    任何客户端都可以访问该资源,只要它们共享相同的
    hex
    函数

    编辑:Base64还可用于对密钥进行编码/解码。可能比十六进制更有效,但有许多不同的实现。如果客户端不共享完全相同的实现,那么它们将无法正常工作

    也可以使用专门的功能(例如,仅处理电子邮件)。但一定要处理所有的边缘情况

  • 存储原始密钥的编码功能
  • 对键进行单向转换要容易得多。因此,与其使用解码函数,不如将原始密钥存储在数据库中

    对于这种情况,一个很好的编码功能是SHA-256算法。这是一种常见的算法,在许多平台上都有实现。碰撞的可能性非常小

    上一个SHA-256示例如下所示:

    # path: /users/{sha256(email)} is User;
    /users/55bf4952e2308638427d0c28891b31b8cd3a88d1610b81f0a605da25fd9c351a: {
        name: "Alice",
        email: "alice@email.com"
    }
    

    任何具有原始密钥(电子邮件)的客户端都可以找到此条目,因为编码函数是已知的(已知的)。而且,即使密钥变大,SHA-256的大小也将始终相同,因此,保证是有效的Firebase密钥。

    我使用以下代码将电子邮件转换为哈希,然后在Firebase中将哈希用作密钥

    公共类HashingUtils{
    公共HashingUtils(){
    }
    //使用SHA-256生成256位哈希
    公共字符串生成器hashkeysha_256(字符串电子邮件){
    字符串结果=null;
    试一试{
    MessageDigest=MessageDigest.getInstance(“SHA-256”);
    byte[]hash=digest.digest(email.getBytes(“UTF-8”);
    return byteToHex(hash);//使其可打印
    }捕获(例外情况除外){
    例如printStackTrace();
    }
    返回结果;
    }
    //使用SHA-1生成160位哈希
    公共字符串生成器hashkeysha_1(字符串电子邮件){
    字符串结果=null;
    试一试{
    MessageDigest=MessageDigest.getInstance(“SHA-1”);
    byte[]hash=digest.digest(email.getBytes(“UTF-8”);
    return byteToHex(hash);//使其可打印
    }捕获(例外情况除外){
    例如printStackTrace();
    }
    返回结果;
    }
    公共字符串byteToHex(字节[]字节){
    格式化程序格式化程序=新格式化程序();
    for(字节b:字节){
    格式化程序。格式(“%02x”,b);
    }
    字符串十六进制=格式化程序.toString();
    返回十六进制;
    }
    }
    
    用于将用户添加到firebase的代码

    p
    
    # path: /users/{sha256(email)} is User;
    /users/55bf4952e2308638427d0c28891b31b8cd3a88d1610b81f0a605da25fd9c351a: {
        name: "Alice",
        email: "alice@email.com"
    }