Qual é a melhor maneira de estruturar dados no Firebase?

Eu sou novo no Firebase e quero saber qual é a melhor maneira de estruturar os dados nele.

Eu tenho um exemplo simples:

Existem candidatos e aplicativos no meu projeto. 1 requerente pode ter várias aplicações. Como posso relacionar esses dois objects no Firebase? Funciona como um database relacional? Ou a abordagem precisa ser completamente diferente em termos de design de dados?

ATUALIZAÇÃO : Existe agora um documento sobre estruturação de dados . Além disso, veja este excelente post 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 o SQL), apesar da falta de instruções e consultas de junit.

Você também quer desnormalizar em lugares onde a eficiência de leitura é uma preocupação. Essa é uma técnica usada por todos os aplicativos de grande escala (por exemplo, Twitter e Facebook) e, apesar de contrariar nossos princípios de DRY, geralmente é um recurso necessário de aplicativos escalonáveis.

A essência aqui é que você quer trabalhar duro em gravações para facilitar a leitura. 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 repetir 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 “SELECT USERS WHERE X = Y”, devido à natureza em tempo real dos dados (está constantemente mudando, fragmentando, reconciliando, etc, o que requer um modelo interno mais simples para manter os clientes sincronizados em cheque)

Um exemplo simples provavelmente colocará você no estado de espírito correto, 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 todas as messages e widgets dos usuários também. Nada demais se nenhuma dessas coisas for em milhares. Mas um grande negócio para 10k usuários com mais de 5k 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 que é extremamente útil neste ambiente são índices. Ao criar um índice de usuários com determinados atributos, posso simular rapidamente uma consulta SQL simplesmente iterando o índice:

 /users_with_gmail_accounts/uid/email 

Agora, se eu quiser, por exemplo, 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 isso 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.

O Firebase não é como um database relacional. Se você quiser compará-lo com qualquer coisa, eu o compararia a um database hierárquico.

Anant escreveu recentemente um ótimo post 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.

Seu cenário se parece com 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. Ele deve ser dimensionado sem problemas de desempenho. É por isso que precisamos de desnormalização como mencionado abaixo.

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