O objetivo desse artigo é mostrar ao programador t-SQL, pl-SQL ou desenvolvedores em geral, como são feitas as consultas básicas numa base de dados NoSQL, mais especificamente o Firebase.
Será algo bem prático. Não entrarei em detalhes, pois acho que não serão necessários. Pelo menos não agora.
Tabela usuário
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
1 | Evelyn | Mendes | evelyn@transnerd.com | 35 | Porto Alegre | RS |
2 | John | Doe | john@doe.com | 20 | Rio de Janeiro | RJ |
3 | Esmeralda | Silva | esmeralda@email.com | 44 | Florianópolis | SC |
4 | João | Silva | joao@email.com | 66 | Bagé | RS |
Temos linhas e colunas bem definidas, tudo arrumadinho.
Vamos ver como fica no Firebase, vulgo json.
{ "usuario" : { "1" :{ "nome" : "Evelyn", "sobrenome" : "Mendes", "email": "evelyn@transnerd.com", "idade" : 35, "cidade": "Porto Alegre", "estado" : "RS" }, "2":{ "nome" : "Jonh", "sobrenome" : "Doe", "email": "jonh@email.com", "idade" : 20, "cidade": "Rio de Janeiro", "estado" : "RS" }, "4":{ "nome" : "Esmeralda", "sobrenome" : "Silva", "email": "esmeralda@email.com", "idade" : 44, "cidade": "Florianópolis", "estado" : "SC" }, "5":{ "nome" : "João", "sobrenome" : "Silva", "email": "joao@email.com", "idade" : 66, "cidade": "Bagé", "estado" : "RS" } } }
Que bagunça!
Dentro desses dois contextos, vamos agora começar a fazer nossas consultas.
Obs: Irei supor que todas as configurações do Firebase de acesso e de referência raiz já estejam definidas como o “rootref”: const rootref = firebase.database().ref(); o que chamamos de root, é o que o Firebase entende por documento raiz.
Na imagem root, é “diversidade-6d120”. Tudo abaixo dele são os registros que serão utilizados, como o nó/tabela palavras.
1 – Recuperar um registro de um usuário pelo seu id
SQL
SELECT * FROM usuario WHERE id = 1;
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
1 | Evelyn | Mendes | evelyn@transnerd.com | 35 | Porto Alegre | RS |
Firebase
const usuarioRef = rootRef.child('usuario').child('1');
Resultado
"1" :{ "nome" : "Evelyn", "sobrenome" : "Mendes", "email": "evelyn@transnerd.com", "idade" : 35, "cidade": "Porto Alegre", "estado" : "RS" }
2 – Recuperar um usuario pelo seu nome
SQL
select * from usuario where nome = 'John';
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
2 | John | Doe | john@doe.com | 20 | Rio de Janeiro | RJ |
Firebase
const nomeRef = rootRef.child('usuario') .orderByChild('nome') .equalTo('John');
Resultado
"2":{ "nome" : "Jonh", "sobrenome" : "Doe", "email": "jonh@email.com", "idade" : 20, "cidade": "Rio de Janeiro", "estado" : "RS" }
3 – Limitar a quantidade de registros que devem retornar em 2
SQL
select top 2 * from usuario --ou select * from usuario limit 2 --Obs: eu prefiro limit, top é muito heterocisnormativo, blergh!!
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
1 | Evelyn | Mendes | evelyn@transnerd.com | 35 | Porto Alegre | RS |
2 | John | Doe | john@doe.com | 20 | Rio de Janeiro | RJ |
Firebase
const limitRef = rootRef.child('usuario').limitToFirst(2); //firebase usa limit, não usa top. Muito amor
Resultado
"1" :{ "nome" : "Evelyn", "sobrenome" : "Mendes", "email": "evelyn@transnerd.com", "idade" : 35, "cidade": "Porto Alegre", "estado" : "RS" }, "2":{ "nome" : "Jonh", "sobrenome" : "Doe", "email": "jonh@email.com", "idade" : 20, "cidade": "Rio de Janeiro", "estado" : "RS" }
4 – Pesquisar os usuarios aonde o nome comece com a letra e
SQL
select * from usuario where nome like 'E%';
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
1 | Evelyn | Mendes | evelyn@transnerd.com | 35 | Porto Alegre | RS |
4 | Esmeralda | Silva | esmeralda@email.com | 44 | Florianópolis | SC |
Firebase
const startERef = rootRef.child('usuario') .orderByChild('nome') .startAt('E');
Resultado
"1" :{ "nome" : "Evelyn", "sobrenome" : "Mendes", "email": "evelyn@transnerd.com", "idade" : 35, "cidade": "Porto Alegre", "estado" : "RS" }, "4":{ "nome" : "Esmeralda", "sobrenome" : "Silva", "email": "esmeralda@email.com", "idade" : 44, "cidade": "Florianópolis", "estado" : "SC" }
5 – Pesquisar os usuários que tem idade menor que 50 anos
SQL
select * from usuario where idade < 50;
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
1 | Evelyn | Mendes | evelyn@transnerd.com | 35 | Porto Alegre | RS |
2 | John | Doe | john@doe.com | 20 | Rio de Janeiro | RJ |
4 | Esmeralda | Silva | esmeralda@email.com | 44 | Florianópolis | SC |
Firebase
const idade50Ref = rootRef.child('usuario') .orderByChild('idade') .endAt(49);
Resultado
"1" :{ "nome" : "Evelyn", "sobrenome" : "Mendes", "email": "evelyn@transnerd.com.br", "idade" : 35, "cidade": "Porto Alegre", "estado" : "RS" }, "2":{ "nome" : "Jonh", "sobrenome" : "Doe", "email": "jonh@email.com", "idade" : 20, "cidade": "Rio de Janeiro", "estado" : "RS" }, "4":{ "nome" : "Esmeralda", "sobrenome" : "Silva", "email": "esmeralda@email.com", "idade" : 44, "cidade": "Florianópolis", "estado" : "SC" }
6 – Pesquisar os usuários que tem idade maior que 50 anos
SQL
select * from usuario where idade > 50;
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
5 | João | Silva | joao@email.com | 66 | Bagé | RS |
Firebase
const idadeRef = rootRef.child('usuario') .orderByChild('idade') .startAt(51);
Resultado
"5":{ "nome" : "João", "sobrenome" : "Silva", "email": "joao@email.com", "idade" : 66, "cidade": "Bagé", "estado" : "RS" }
7 – Agora vamos pesquisar os usuários que tem idade entre 21 e 50 anos
SQL
select * from usuario where idade >= 21 and idade <=50;
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
1 | Evelyn | Mendes | evelyn@transnerd.com | 35 | Porto Alegre | RS |
4 | Esmeralda | Silva | esmeralda@email.com | 44 | Florianópolis | SC |
Firebase
const idadesRef = rootRef.child('usuario') .orderByChild('idade') .startAt(21) .endAt(50);
Resultado
"1" :{ "nome" : "Evelyn", "sobrenome" : "Mendes", "email": "evelyn@transnerd.com", "idade" : 35, "cidade": "Porto Alegre", "estado" : "RS" } "4":{ "nome" : "Esmeralda", "sobrenome" : "Silva", "email": "esmeralda@email.com", "idade" : 44, "cidade": "Florianópolis", "estado" : "SC" }
8 -Vamos complicar um pouco as coisas?
Vamos selecionar todos os usuários que têm 66 anos e moram no estado RS. No SQL é fácil.
SQL
select * from usuario where idade = 66 and estado ='RS';
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
5 | João | Silva | joao@email.com | 66 | Bagé | RS |
Como eu havia dito, no sql é facil ,e no firebase?
Vamos tentar. Parece fácil, né? Apenas adicionar mais e mais orders e equals. Porém:
const erroRef = rootRef.child('usuario') .orderByChild('idade').equalTo(66) .orderByChild('estado').equalTo('RS')
Porém, isso dará um erro, pois o Firebase só aceita uma função de ordenação por instrução. Ops.
Como vamos resolver isso?
Calma. Pra tudo tem um jeito!
Na realidade, uma das premissas de uma base NoSQL como o Firebase, é pensar na forma que você irá pesquisar ao imputar dados. Vamos fazer uma modificação no json e você entenderá. Insira essa chave no registro 5:
"idade_estado" : "66_RS"
Note que foi inserida uma nova chave concatenando dois valores, idade e estado. Isso você pode fazer usando o Firebase Functions, a cada iteração de insert e update na base de dados. Feito isso a instrução ficará simplificada.
Firebase
const idadeEstadoRef = rootRef.child('usuario') .orderByChild('idade_estado') .equalTo('66_RS');
Resultado
"5":{ "nome" : "João", "sobrenome" : "Silva", "email": "joao@email.com", "idade" : 66, "cidade": "Bagé", "estado" : "RS", "idade_estado" : "66_RS" }
9 – Agora digamos que além da idade e do estado, eu também quero pesquisar pelo nome do usuário, mas de forma incremental
Novamente no SQL, é fácil.
SQL
select * from usuario where idade = 66 and estado ='RS' and nome like 'Jo%';
Resultado
Id | Nome | Sobrenome | Idade | Cidade | Estado | |
5 | João | Silva | joao@email.com | 66 | Bagé | RS |
E agora no Firebase?
Vamos começar aproveitando o que fizemos no registro 5 e acrescentar uma nova chave.
"idade_estado_nome" : "66_RS_João Silva"
Imagine três campos; Um input de idade, um select de estado e um input de nome. Você quer a idade de 66 anos, então no campo idade, você digita 66, procura por quem mora no estado do Rio Grande do Sul, e então escolhe RS no campo select. E além disso, você quer todos os nomes que comecem por J, Jo, Joã, João. Vamos criar uma variável para ficar mais descritivo. A forma como estou escrevendo é meramente ilustrativa.
let idade_estado_nome = idade.value + '-' + estado.value + '-' + nome.value; const idadeEstadoNomeRef = rootRef.child('usuario') .orderByChild('idade_estado_nome') .startAt(idade_estado_nome);
Note que idade já estará no valor e estado também. A função startAt() que irá trabalhar será somente o nome conforme for sendo pesquisado. Ficaria algo assim:
const idadeEstadoNomeRef = rootRef.child('usuario') .orderByChild('idade_estado_nome') .startAt('66_RS_J'); //---------------------------------------------------------------- const idadeEstadoNomeRef = rootRef.child('usuario') .orderByChild('idade_estado_nome') .startAt('66_RS_Jo'); //---------------------------------------------------------------- const idadeEstadoNomeRef = rootRef.child('usuario') .orderByChild('idade_estado_nome') .startAt('66_RS_Joã'); //---------------------------------------------------------------- const idadeEstadoNomeRef = rootRef.child('usuario') .orderByChild('idade_estado_nome') .startAt('66_RS_João');
Como temos apenas um João na nossa base de dados, o resultado será sempre o mesmo.
Resultado
"5":{ "nome" : "João", "sobrenome" : "Silva", "email": "joao@email.com", "idade" : 66, "cidade": "Bagé", "estado" : "RS", "idade_estado" : "66_RS", "idade_estado_nome" : "66_RS_João Silva" }
Ponto de alerta: Eu não sei o custo que isso irá ter para o Firebase em bases muito grandes e com muitos registros. Outra questão é o unicode, que deve ser levado em consideração nas pesquisas por texto. À primeira vista parece que não se pode fazer muita coisa no Firebase, não se tem joins bem declarados, por exemplo, mas você tende a não precisar deles e demais funcionalidades de bancos relacionais, pois além do conceito de chaves compostas, ainda temos a desnormalização, onde os dados são replicados. Um exemplo seria um usuário inteiro, não somente seu uid estar dentro de registros de eventos que ele participará, e também estar dentro de registros de reuniões para esse usuário. Isso é normal e plenamente aceito no Firebase.
Gente, muito obrigada. No próximo artigo irei falar sobre regras (rules) da base de dados. Vocês irão se surpreender com o poder que o Firebase oferece para validações, tipagens, e pasmem, até “FKs”. Sim, tem. Porém, reparem que é entre aspas.
Beijos!