Добавление данных Firebase, точек и косой черты

Я пытаюсь использовать firebase db, я обнаружил очень важные ограничения, которые не описаны в справке или FAQ firebase.

Первая проблема заключается в том, что символ: точка '.' запрещено в ключах,

то есть firebase отклонить (по неизвестной причине) далее:

        nameRef.child('[email protected]').set('Pirat');

Вторая проблема с косой чертой в ваших ключах '/', когда вы пытаетесь добавить такой ключ

        {'02/10/2013': true}

В firebase вы можете увидеть:

         '02': {
             '10': {
                 '2013': true
             }
         }       

У вас есть идеи, как это решить (автоматически)? Можно установить какой-то флаг, что это строковый ключ со всеми символами? Конечно, я могу анализировать/восстанавливать данные каждый раз перед записью и после чтения, но...

Кстати '.' '/' - все запрещенные символы для firebase?


person IL55    schedule 02.10.2013    source источник
comment
Используйте github.com/cartant/firebase-key.   -  person eozzy    schedule 30.08.2020


Ответы (10)


Причина, по которой добавление дочернего элемента 02/10/2013 создает структуру в Firebase, заключается в том, что косая черта приводит к созданию нового уровня.

Итак, я предполагаю, что вы используете что-то похожее на: firebaseRef.child('02/10/2013').set(true) эквивалентно firebaseRef.child('02').child('10').child('2013').set(true).

Чтобы избежать проблем, связанных с невозможностью использования следующих символов в именах ссылочных ключей (источник),

  • . (период)
  • $ (знак доллара)
  • [ (левая квадратная скобка)
  • ] (правая квадратная скобка)
  • # (решётка или знак фунта)
  • / (косая черта)

мы можем использовать одну из встроенных в JavaScript функций кодирования, поскольку, насколько я могу судить, Firebase не предоставляет для этого встроенный метод. Вот беглый просмотр, чтобы увидеть, какой из них наиболее эффективен для наших целей:

var forbiddenChars = '.$[]#/'; //contains the forbidden characters
escape(forbiddenChars); //results in ".%24%5B%5D%23/"
encodeURI(forbiddenChars); //results in ".%24%5B%5D%23%2F"
encodeURIComponent(forbiddenChars); //results in ".%24%5B%5D%23%2F"

Очевидно, что наиболее эффективным решением является encodeURIComponent. Однако это не решает всех наших проблем. Символ . по-прежнему представляет проблему, как показано в приведенном выше тесте и попытке encodeURIComponent вашего тестового адреса электронной почты. Мое предложение состояло бы в том, чтобы связать функцию замены после encodeURIComponent, чтобы иметь дело с периодами.

Вот как будет выглядеть решение для ваших двух примеров:

encodeURIComponent('[email protected]').replace(/\./g, '%2E') //results in "Henry%2EMorgan%40caribbean%2Esea"
encodeURIComponent('02/10/2013'); //results in "02%2F10%2F2013"

Поскольку оба окончательных результата безопасны для вставки в Firebase в качестве имени ключа, единственной другой проблемой является декодирование после чтения из Firebase, которое можно решить с помощью replace('%2E', '.') и простого decodeURIComponent(...).

person sushain97    schedule 02.10.2013
comment
Кодирование, похоже, не служит цели - person Philar; 11.01.2014
comment
@ Филар, что ты имеешь в виду? - person sushain97; 11.01.2014
comment
Извините за тупость, но кодирование '[' и ']' по-прежнему вызывает то же самое исключение requests.exceptions.HTTPError: 400 Client Error: Bad Request. Лучше избегать использования этих символов в ключах - person Philar; 15.01.2014
comment
@Филар, интересно. Я предполагаю, что вы используете библиотеку запросов Python? Как вы кодируете и отправляете запрос? Возможно, вам придется кодировать дважды, если Requests отменяет кодировку. - person sushain97; 16.01.2014
comment
Приведенная ссылка на источник неверна. Правильная ссылка: firebase.google.com/docs/database/usage/limits#. дерево_данных - person Sourav Kannantha B; 13.04.2021

Я сам столкнулся с той же проблемой и создал firebase-encode. для этой цели.

В отличие от выбранного ответа, firebase-encode кодирует только небезопасные символы (. /[]#$) и % (необходимо из-за того, как работает кодирование/декодирование). Он оставляет другие специальные символы, которые можно безопасно использовать в качестве ключа Firebase, в то время как encodeURIComponent будет их кодировать.

Вот исходный код для деталей:

// http://stackoverflow.com/a/6969486/692528
const escapeRegExp = (str) => str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');


const chars = '.$[]#/%'.split('');
const charCodes = chars.map((c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);

const charToCode = {};
const codeToChar = {};
chars.forEach((c, i) => {
  charToCode[c] = charCodes[i];
  codeToChar[charCodes[i]] = c;
});

const charsRegex = new RegExp(`[${escapeRegExp(chars.join(''))}]`, 'g');
const charCodesRegex = new RegExp(charCodes.join('|'), 'g');

const encode = (str) => str.replace(charsRegex, (match) => charToCode[match]);
const decode = (str) => str.replace(charCodesRegex, (match) => codeToChar[match]);
person Joon    schedule 04.07.2016
comment
Потрясающий! Спасибо ???? - person ckuijjer; 09.08.2016

Ограничения по символам задокументированы на странице https://www.firebase.com/docs/creating-references.html. - вы не можете использовать '.', '/', '[', ']', '#' и '$' в именах клавиш. Не существует автоматического способа экранирования этих символов, я бы рекомендовал вообще избегать их использования или создать свой собственный механизм экранирования/неэкранирования.

person Anant    schedule 02.10.2013

Я написал это для Java (поскольку я пришел сюда, ожидая реализации Java):

public static String encodeForFirebaseKey(String s) {
    return s
            .replace("_", "__")
            .replace(".", "_P")
            .replace("$", "_D")
            .replace("#", "_H")
            .replace("[", "_O")
            .replace("]", "_C")
            .replace("/", "_S")
            ;
}

public static String decodeFromFirebaseKey(String s) {
    int i = 0;
    int ni;
    String res = "";
    while ((ni = s.indexOf("_", i)) != -1) {
        res += s.substring(i, ni);
        if (ni + 1 < s.length()) {
            char nc = s.charAt(ni + 1);
            if (nc == '_') {
                res += '_';
            } else if (nc == 'P') {
                res += '.';
            } else if (nc == 'D') {
                res += '$';
            } else if (nc == 'H') {
                res += '#';
            } else if (nc == 'O') {
                res += '[';
            } else if (nc == 'C') {
                res += ']';
            } else if (nc == 'S') {
                res += '/';
            } else {
                // this case is due to bad encoding
            }
            i = ni + 2;
        } else {
            // this case is due to bad encoding
            break;
        }
    }
    res += s.substring(i);
    return res;
}
person josue.0    schedule 18.09.2016

Если вы используете Swift 3, это работает для меня (попробуйте на игровой площадке):

var str = "this.is/a#crazy[string]right$here.$[]#/"

if let strEncoded = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics) {
    print(strEncoded)

    if let strDecoded = strEncoded.removingPercentEncoding {
        print(strDecoded)
    }
}
person Trev14    schedule 31.05.2017

Меня раздражала эта проблема, поэтому я взял ответ от @sushain97 (спасибо!) и построил глубокий кодировщик/декодер.

https://www.npmjs.com/package/firebase-key-encode

Основное использование:

var firebaseKeyEncode = require('firebase-key-encode');
firebaseKeyEncode.encode('my.bad.key');
// Output: my%2Ebad%2Ekey

Глубокое использование:

var firebaseKeyEncode = require('firebase-key-encode');

var badTree = {
    "pets": [
        {
            "jimmy.choo": 15}
        ],
    "other.key": 5
}

firebaseKeyEncode.deepEncode(badTree);

// Output: {
//    "pets": [
//        {
//            "jimmy%2Echoo": 15}
//        ],
//    "other%2Ekey": 5
// }
person Chris Villa    schedule 29.09.2016

Лично я нашел простой и легкий способ решить ту же проблему, с которой столкнулся.

Я взял dateTime string и преобразовал его с помощью replace('/','|')

результатом будет что-то вроде этого 2017|07|24 02:39:37 вместо 2017/07/24 02:39:37.

person Katherine Fennedy    schedule 23.07.2017

Несмотря на то, что это не то, что спрашивает OP,
но по моему опыту, вместо того, чтобы использовать такие сомнительные ключи, лучше позволить .push() создать идентификатор,
и другие вещи - электронную почту, дату и т. д. сохранить как содержание выделенных полей.

$id: {
   email: "[email protected]"
}

P.S. Не пытайтесь сэкономить объем, вставляя в ключ то, что должно быть в нем.
Преждевременная оптимизация — корень всех зол (с).

person Do-do-new    schedule 18.08.2020

Эффективная реализация C# (для Unity и .net). На основе ответа @josue.0.

    public static string EncodeFirebaseKey(string s) {
            StringBuilder sb = new StringBuilder();
            foreach (char c in s) {
                switch (c) {
                    case '_':
                        sb.Append("__");
                        break;
                    case '$':
                        sb.Append("_D");
                        break;
                    case '.':
                        sb.Append("_P");
                        break;
                    case '#':
                        sb.Append("_H");
                        break;
                    case '[':
                        sb.Append("_O");
                        break;
                    case ']':
                        sb.Append("_C");
                        break;
                    case '/':
                        sb.Append("_S");
                        break;
                    default:
                        sb.Append(c);
                        break;
                }
            }
            return sb.ToString();
        }

        public static string DecodeFirebaseKey(string s) {
            StringBuilder sb = new StringBuilder();
            bool underscore = false;
            for (int i = 0; i < s.Length; i++) {
                if (underscore) {
                    switch (s[i]) {
                        case '_':
                            sb.Append('_');
                            break;
                        case 'D':
                            sb.Append('$');
                            break;
                        case 'P':
                            sb.Append('.');
                            break;
                        case 'H':
                            sb.Append('#');
                            break;
                        case 'O':
                            sb.Append('[');
                            break;
                        case 'C':
                            sb.Append(']');
                            break;
                        case 'S':
                            sb.Append('/');
                            break;
                        default:
                            Debug.LogWarning("Bad firebase key for decoding");
                            break;
                    }
                    underscore = false;
                } else {
                    switch (s[i]) {
                        case '_':
                            underscore = true;
                            break;
                        default:
                            sb.Append(s[i]);
                            break;
                    }
                }
            }
            return sb.ToString();
        }
person hk1ll3r    schedule 03.05.2021

Реализация Python

_escape = {'&': '&&',
       '$': '&36',
       '#': '&35',
       '[': '&91',
       ']': '&93',
       '/': '&47',
       '.': '&46'}

_unescape = {e: u for u, e in _escape.items()}


def escape_firebase_key(text):
    return text.translate(str.maketrans(_escape))


def unescape_firebase_key(text):
    chunks = []
    i = 0
    while True:
        a = text[i:].find('&')
        if a == -1:
            return ''.join(chunks + [text[i:]])
        else:
            if text[i+a:i+a+2] == '&&':
                chunks.append('&')
                i += a+2
            else:
                s = text[i+a:i+a+3]
                if s in _unescape:
                    chunks.append(text[i:i+a])
                    chunks.append(_unescape[s])
                    i += a+3
                else:
                    raise RuntimeError('Cannot unescape')

И несколько тестовых случаев:

test_pairs = [('&hello.', '&&hello&46'),
              ('&&&', '&&&&&&'),
              ('[email protected]', 'some@email&46com'),
              ('#$[]/.', '&35&36&91&93&47&46')]
               
for u, e in test_pairs:
    assert escape_firebase_key(u) == e, f"escaped '{u}' is '{e}', but was '{escape_firebase_key(u)}'"""
    assert unescape_firebase_key(e) == u, f"unescaped '{e}' is '{u}', but was '{unescape_firebase_key(e)}'"
    
try:
    unescape_firebase_key('&error')
    assert False, 'Must have raised an exception here'
except RuntimeError as ex:
    assert str(ex) == 'Cannot unescape'

person Marat Zaynutdinoff    schedule 02.07.2021