Ответ 1
UPDATE: теперь существует doc для структурирования данных. Также см. Этот отличный пост в Структуры данных NoSQL.
Основная проблема с иерархическими данными, в отличие от РСУБД, заключается в том, что она соблазняет вложение данных, потому что мы можем. Как правило, вы хотите в какой-то степени нормализовать данные (как и с SQL), несмотря на отсутствие заявлений и запросов на соединение.
Вы также хотите denormalize в местах, где эффективность чтения является проблемой. Это метод, используемый всеми крупномасштабными приложениями (например, 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 о денормализации данных чтобы проверить их также. Я вижу, что Фрэнк уже опубликовал статью Ананта, поэтому я не буду повторять, что здесь, но это также замечательно.