30/01/2018 Development / java script

Essa parada do firebase não ter um join, innerjoin, groupBy, é meio frustante, então, eu vou descrever aqui, oque eu tenho feito para suprir as minhas necessidades.

O firebase tem um evento listener chamado ON e ONCE, onde você pode aplicar alguns valores:

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 ONCE escuta uma vez só, e ON fica escutando sempre.

Seguindo essa documentação, imagina que eu tenho uma LISTA de LIVROS, e pra cada LIVRO eu gostaria de trazer o AUTHOR e todo mundo que já comprou esse livro.

Bom, com SQL ou até mesmo com MONGO, isso seria uma tarefa fácil, mas com essa porra de firebase, já é chatinho, mas vamo la!!!

No cenário mais simples, a gente usuário o child_added, que pra cada livro a gente iria consultar o author e os clientes. Vamos fazer a 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 oque aconteceria nesse caso? Primeiro, ia dar o console.log de todos os nomes dos nossos livros e depoooooois, iria dar o console do nome dos autores. Ex:

nome do livro é: SENHOR DOS ANEIS
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 oque a gente queria 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

Ai você pensa: Ahhhhhh, fácil, no retorno da segunda consulta eu mostro o nome do livro junto com o nome do author :-), Que malandro sou eu? hummmmm

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;

})
})

Tá, assim funciona! Mas e agora no caso da gente adicionar o terceiro listener, que é o caso dos clientes que compraram, oque a gente pode fazer? Ao mesmo tempo que você esta consultando o author, também esta consultando os clientes, você não sabe qual vai ser o primeiro ou último a responder, por isso não pode jogar o console no listener do author ou no listener do cliente, certo?

Nesse caso, usamos o conceito de Promisse() e ao inves de ON, usamos o ONCE que retorna uma Promisse, e a implementação ficaria da seguinte maneira:

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.

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, 1 pedido contem o codigo do cliente e codigo do livro e como o pedido é um snapshot de como estão as informações no momento. Aprendi isso trabalhando na B2W! Pra mim oque vale é o nome do cliente naquele dia.

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'))

     $('pre').text(array.join('\n'))

    })

})

Essa foi a saida para a implementação usando uma espécie de JOIN, claro que ao inves 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!