ATUALIZAÇÃO : existe agora um documento sobre estruturação de dados . Além disso, veja esta excelente postagem sobre estruturas de dados NoSQL .
O principal problema com dados hierárquicos, em oposição ao RDBMS, é que é tentador aninhar dados porque podemos. Geralmente, você deseja normalizar os dados até certo ponto (assim como faria com SQL), apesar da falta de instruções e consultas de junção.
Você também deseja desnormalizar em locais onde a eficiência de leitura é uma preocupação. Esta é uma técnica usada por todos os aplicativos de grande escala (por exemplo, Twitter e Facebook) e, embora vá contra nossos princípios DRY, geralmente é um recurso necessário para aplicativos escaláveis.
A essência aqui é que você deseja trabalhar duro nas gravações para facilitar as leituras. Mantenha os componentes lógicos que são lidos separadamente separados (por exemplo, para salas de bate-papo, não coloque as mensagens, meta-informações sobre as salas e listas de membros no mesmo lugar, se você quiser ser capaz de iterar os grupos mais tarde).
A principal diferença entre os dados em tempo real do Firebase e um ambiente SQL é a consulta de dados. Não há uma maneira simples de dizer "SELECIONE USUÁRIOS ONDE X = Y", devido à natureza em tempo real dos dados (eles estão constantemente mudando, fragmentando, reconciliando, etc, o que requer um modelo interno mais simples para manter os clientes sincronizados sob controle)
Um exemplo simples provavelmente o deixará no estado de espírito certo, então aqui vai:
/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets
Agora, como estamos em uma estrutura hierárquica, se eu quiser iterar os endereços de e-mail dos usuários, faço algo assim:
// 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));
O problema com essa abordagem é que acabei de forçar o cliente a baixar todos os usuários messages
e widgets
também. Não é nada demais se nenhuma dessas coisas chega aos milhares. Mas é um grande problema para 10 mil usuários com mais de 5 mil mensagens cada.
Portanto, agora a estratégia ideal para uma estrutura hierárquica em tempo real se torna mais óbvia:
/user_meta/uid/email
/messages/uid/...
/widgets/uid/...
Uma ferramenta adicional extremamente útil neste ambiente são os índices. Ao criar um índice de usuários com certos atributos, posso simular rapidamente uma consulta SQL simplesmente iterando o índice:
/users_with_gmail_accounts/uid/email
Agora, se eu quiser, digamos, receber mensagens para usuários do gmail, posso fazer algo assim:
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));
Eu ofereci alguns detalhes em outro post do SO sobre desnormalização de dados, então verifique-os também . Vejo que Frank já postou o artigo de Anant, então não vou reiterar isso aqui, mas também é uma ótima leitura.
Firebase não é como um banco de dados relacional. Se você quiser compará-lo com qualquer coisa, eu compararei com um banco de dados hierárquico.
Anant escreveu recentemente uma ótima postagem no blog do Firebase sobre desnormalizar seus dados: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html
Eu realmente sugiro manter o "ID" de cada aplicativo como filho de cada candidato.
fonte
Seu cenário parece um para muitos no mundo relacional, de acordo com seu exemplo, um candidato tem muitas aplicações. Se chegarmos ao firebase nosql, parece que está abaixo. Deve ser escalonado sem quaisquer problemas de desempenho. É por isso que precisamos desnormalização, conforme mencionado abaixo.
fonte