Как лучше всего структурировать данные в firebase?

111

Я новичок в firebase и хочу знать, как лучше всего структурировать данные на нем.

У меня есть простой пример:

В моем проекте есть соискатели и заявки. 1 заявитель может подать несколько заявок. Как я могу связать эти 2 объекта с firebase? Работает ли это как реляционная база данных? Или подход должен быть совершенно другим с точки зрения дизайна данных?

бункер
источник

Ответы:

137

ОБНОВЛЕНИЕ : теперь есть документ по структурированию данных . Также посмотрите этот отличный пост о структурах данных NoSQL .

Основная проблема с иерархическими данными, в отличие от СУБД, заключается в том, что существует соблазн вложить данные, потому что мы можем. Как правило, вы хотите до некоторой степени нормализовать данные (как если бы вы делали это с SQL), несмотря на отсутствие операторов соединения и запросов.

Вы также хотите денормализовать в местах, где эффективность чтения является проблемой. Это метод, используемый всеми крупномасштабными приложениями (например, Twitter и Facebook), и, хотя он противоречит нашим принципам DRY, обычно это необходимая функция масштабируемых приложений.

Суть в том, что вам нужно много работать над записью, чтобы облегчить чтение. Храните логические компоненты, которые читаются отдельно (например, для комнат чатов, не помещайте сообщения, метаинформацию о комнатах и ​​списки участников в одно и то же место, если вы хотите иметь возможность повторять группы позже).

Основное различие между данными Firebase в реальном времени и средой SQL - это запросы данных. Нет простого способа сказать «ВЫБЕРИТЕ ПОЛЬЗОВАТЕЛЕЙ, ГДЕ X = Y» из-за характера данных в реальном времени (они постоянно меняются, сегментируются, согласовываются и т. Д., Что требует более простой внутренней модели для контроля синхронизированных клиентов)

Простой пример, вероятно, настроит вас в правильное состояние, так что вот:

/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets

Теперь, поскольку мы находимся в иерархической структуре, если я хочу перебирать адреса электронной почты пользователей, я делаю что-то вроде этого:

// I could also use on('child_added') here to great success
// but this is simpler for an example
firebaseRef.child('users').once('value')
.then(userPathSnapshot => {
   userPathSnapshot.forEach(
      userSnap => console.log('email', userSnap.val().email)
   );
})
.catch(e => console.error(e));

Проблема с этим подходом в том, что я только что заставил клиента загрузить файлы всех пользователей messagesи widgetsтоже. Ничего страшного, если ничто из этого не исчисляется тысячами. Но это большое дело для 10 тыс. Пользователей с более чем 5 тыс. Сообщений каждый.

Итак, теперь оптимальная стратегия для иерархической структуры в реальном времени становится более очевидной:

/user_meta/uid/email
/messages/uid/...
/widgets/uid/...

Дополнительным инструментом, который чрезвычайно полезен в этой среде, являются индексы. Создав индекс пользователей с определенными атрибутами, я могу быстро смоделировать SQL-запрос, просто повторяя индекс:

/users_with_gmail_accounts/uid/email

Теперь, если я хочу, скажем, получать сообщения для пользователей Gmail, я могу сделать что-то вроде этого:

var ref = firebase.database().ref('users_with_gmail_accounts');
ref.once('value').then(idx_snap => {
   idx_snap.forEach(idx_entry => {
       let msg = idx_entry.name() + ' has a new message!';
       firebase.database().ref('messages').child(idx_entry.name())
          .on(
             'child_added', 
             ss => console.log(msg, ss.key);
          );
   });
})
.catch(e => console.error(e));

Я предложил некоторые подробности в другом сообщении SO о денормализации данных, так что посмотрите и их . Я вижу, что Фрэнк уже опубликовал статью Ананта, поэтому я не буду повторять это здесь, но это также отличное чтение.

Като
источник
Спасибо за понимание Като!
hopper
2
На данный момент. Представления в версии Firebase v2 будут содержать отличные возможности для автоматизации этого процесса.
Kato
Зная, что я воскрешаю здесь старую ветку комментариев, но я изо всех сил пытаюсь найти более современное решение. Это все еще лучший подход? т.е. получение всех пользователей users_with_gmail_accounts, а затем запуск forEach?
owiewio
48

Firebase очень не похожа на реляционную базу данных. Если вы хотите сравнить его с чем-либо, я бы сравнил его с иерархической базой данных.

Анант недавно написал отличный пост в блоге Firebase о денормализации ваших данных: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Я действительно предлагаю сохранять «ID» каждого приложения как дочерний для каждого заявителя.

Франк ван Пуффелен
источник
Спасибо, Фрэнк! Это действительно полезно. Именно то, что я искал!
hopper
4

Ваш сценарий выглядит как один ко многим в реляционном мире, согласно вашему примеру у соискателя много приложений. Если мы подойдем к firebase nosql, это выглядит так, как показано ниже. Он должен масштабироваться без проблем с производительностью. Вот почему нам нужна денормализация, как указано ниже.

applicants:{
applicant1:{
    .
    .
    applications:{
        application1:true,
        application3:true
    }
},
applicant2:{
    .
    .
    applications:{
        application2:true,
        application4:true
    }
}}

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Пратеп Гедупуди
источник
Хорошо, но у меня есть продолжение, как нам создать эту структуру из Swift или где-нибудь еще с помощью Firebase SDK? Также как мы можем проверить, что новые данные, добавленные в узел приложений, действительно существуют в списке приложений, используя правила проверки Firebase?
Tommie C.
@prateep, Хороший пример. Но здесь проблема заключается в том, что я удаляю путь applications / application1, где application1 является дочерним для некоторых кандидатов. Если я попытаюсь получить доступ к заявителям пути / application1, которого там нет. поэтому вам нужно обновить индексы в обоих местах, например application1: {Applicants: {Applicant1: true} ...}, так что теперь, когда я удаляю application1, мне нужно проверить его дочерние заявители и обновить дочерний узел заявителей для приложения. :)
Сатиш Соджитра