ОБНОВЛЕНИЕ : теперь есть документ по структурированию данных . Также посмотрите этот отличный пост о структурах данных 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 о денормализации данных, так что посмотрите и их . Я вижу, что Фрэнк уже опубликовал статью Ананта, поэтому я не буду повторять это здесь, но это также отличное чтение.