Qual é a melhor forma de estruturar dados no firebase?

111

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

Eu tenho um exemplo simples:

Existem candidatos e inscrições no meu projeto. 1 candidato pode ter vários aplicativos. Como posso relacionar esses 2 objetos no firebase? Funciona como um banco de dados relacional? Ou a abordagem precisa ser completamente diferente em termos de design de dados?

tremonha
fonte

Respostas:

137

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 messagese widgetstambé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.

Kato
fonte
Obrigado por esta visão Kato!
tremonha de
2
Por enquanto. As visualizações na versão v2 do Firebase conterão alguns excelentes recursos para automatizar esse processo.
Kato
Ciente de que estou ressuscitando um antigo tópico de comentários aqui, mas estou lutando para encontrar uma solução mais atualizada. Essa ainda é a melhor abordagem? ou seja, obter todos os users_with_gmail_accounts e executar um forEach?
owiewio
48

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.

Frank van Puffelen
fonte
Obrigado Frank! Isso é realmente útil. Exatamente o que eu estava procurando !
tremonha de
4

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.

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

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Prateep Gedupudi
fonte
Bom, mas eu tenho um follow-on, como criamos essa estrutura no Swift ou em qualquer lugar usando o Firebase SDK? Além disso, como podemos validar se os novos dados adicionados ao nó de aplicativos realmente existem na lista de aplicativos usando as regras de validação do Firebase?
Tommie C.
@prateep, bom exemplo. Mas aqui o problema é quando eu excluo o caminho applications / application1, onde application1 é filho para alguns candidatos. Se eu tentar acessar o caminho candidate / application1, que não está lá. então você precisa atualizar os índices em ambos os lugares, como application1: {candidate: {candidate1: true} ...} agora, quando eu excluo o Applicion1, eu tenho que verificar seus candidatos filhos e atualizar o nó filho dos candidatos para a aplicação. :)
Satish Sojitra