Trabalhando com “JOINS” no Firebase
Neste artigo, o autor fala sobre a ausência das operações join, innerjoin e groupBy no Firebase e como contornar isso.
O fato do Firebase não ter operações como join, innerjoin e groupBy; pode ser meio frustrante em algumas situações. Neste artigo, vou descrever o que eu tenho feito para suprir as minhas necessidades.
O Firebase possui dois event listeners chamados on e once, onde podemos aplicar alguns valores, como: child_added, value, child_removed e child_changed. Pra ficar mais claro, vamos ver um exemplo prático:
let ref = firebase.database().ref();
ref.child('livros').on('child_added', => snapshot { console.log(snapshot.val()); })
ref.child('livros').on('value', snapshot => { console.log(snapshot.val()); })
ref.child('livros').on('child_removed', snapshot => { console.log(snapshot.val()); })
ref.child('livros').on('child_changed', snapshot => { console.log(snapshot.val()); })
A diferença entre on e once é que o segundo escuta uma vez só, enquanto o primeiro fica escutando sempre. Agora imagine que temos uma lista de livros, e para cada um queremos trazer o autor e todos que já compraram esse livro. Com SQL↳SQL64 conteúdosSQL Server – Como evitar SQL Injection?Data · mai 2019Azure SQL DB Managed InstanceData · abr 2019SQL Server – Como evitar SQL Injection? Pare de utilizar Query Dinâmica como EXEC(@Query)Data · abr 2019Ver tudo em Data → ou até mesmo com MongoDB, isso seria uma tarefa trivial, mas com Firebase pode ser tornar um pouco mais complicado.
No cenário mais simples, usamos o child_added e para cada livro consulta o autor e os clientes. Vamos ver como fica essa query!
ref.child('livros').on('child_added', => snapshot {
let livro = snapshot.val();
let authorId = livro.authorId;
console.log('nome do livro: ' + livro.nome);
ref.child('author').orderByChild("authorId").equalTo(authorId).on('value', author => {
console.log('nome do author:' + author.val().nome;
})
})
Sabe o que aconteceria nesse caso? Primeiro, ia dar o console.log() de todos os nomes dos nossos livros e depois iria dar o console do nome dos autores. Ex:
nome do livro é: SENHOR DOS ANÉIS nome do livro é: O ALQUIMISTA nome do livro é: TUDO É POSSÍVEL nome do author: J. R. R. Tolkien nome do author: Paulo Coelho nome do author: Allan Percy
Mas o que queremos fazer é:
nome do livro é: SENHOR DOS ANEIS nome do author: J. R. R. Tolkien nome do livro é: O ALQUIMISTA nome do author: Paulo Coelho nome do livro é: TUDO É POSSÍVEL nome do author: Allan Percy
Você pode pensar: “Ah, fácil de resolver! No retorno da segunda consulta eu mostro o nome do livro junto com o nome do autor!”. Vamos ver se essa abordagem funciona:
ref.child('livros').on('child_added', => snapshot {
let livro = snapshot.val();
let authorId = livro.authorId;
ref.child('author').orderByChild("authorId").equalTo(authorId).on('value', author => {
console.log('nome do livro: ' + livro.nome + '\n');
console.log('nome do author:' + author.val().nome;
})
})
Funciona! Mas e agora, se adicionarmos o terceiro listener – que é o caso dos clientes que compraram – o que podemos fazer? Ao mesmo tempo que você está consultando o autor, também está consultando os clientes, você não sabe qual vai ser o primeiro ou o último a responder. Por isso não pode jogar o console no listener do autor ou no listener do cliente, certo?
Nesse caso, usamos o conceito de Promise. Ao invés de usar o on, usamos o once que retorna uma Promise. Só uma observação: Quando a gente usa orderByChild, é como se fosse um filtro, então, ele retorna um array podendo conter 1 ou mais objetos.
A implementação fica da seguinte maneira:
let promises = [];
ref.child('livros').on('child_added', => snapshot {
let livro = snapshot.val();
let authorId = livro.authorId;
let promise_author = ref.child('author').orderByChild("authorId").equalTo(authorId).once('value') //no caso do author, vai trazer 1 só
let promise_pedidos = ref.child('pedidos').orderByChild("livroId").equalTo(livroId).once('value') //no caso de pedidos, vai trazer varios
promises.push(promise_author, promise_pedidos) //inserindo as promises na fila
// Quando terminar todas as minhas promises, ai sim eu monto a resposta final
Promise.all(promisses).then(function(resp) {
let nomeLivro = livro.nome;
let nomeAuthor = resp[0].val().nome;
let clientePedidos = resp[1].val().map(pedido => {
return pedido.nomeCliente
});
array.push(livro.nome + ' - ' + nomeAuthor + ' - ' + clientePedidos.join('\n'))
console.log(array.join('\n'))
})
})
Essa foi a saída para a implementação usando uma espécie de join. Claro que ao invés de colocar no array, eu fiz um objeto bonitinho, mapeado, e tal, mas isso fica a critério da sua implementação. Espero que sirva de base!






