Entre os recursos que a plataforma de serviços Firebase disponibiliza, está o Cloud Firestore. Trata-se de um banco de dados NoSQL hospedado nas nuvens, onde armazenamos os dados em documentos.
A consulta de dados deste banco é eficiente e flexível, mas há diferenças se você já desenvolve com SQL.
Neste artigo apresento o FireSQL, uma biblioteca criada com base no SDK oficial do Firebase, que permite consultar coleções no Cloud Firestore usando a sintaxe SQL.
Criaremos três coleções e alguns documentos para criar consultas SQL. Um projeto em Angular 7 foi criado e as funções de exemplos estão todas no componente principal. No final do artigo colocarei o link do projeto no GitHub.
Acesse o console do Firebase com suas credenciais Google e crie um projeto. Para mais detalhes, confira este meu outro artigo.
Vamos criar as coleções para executar as consultas.
Criei três coleções nomeando como: cidades, estados e pessoas.
Para estados eu criei alguns documentos com os campos nome e sigla.
Para cidades, foram três campos, nome, uf como string e populacao (sem ~ e ç) como number. Também criei alguns documentos.
Finalmente, para a coleção pessoas, desenvolvi os seguintes campos:
Indiquei o nome como string, idade como number, tags como array e um map endereco com os campos bairro, numero e rua. Assim como as coleções anteriores, também criei mais documentos para a realização das consultas.
Finalizado a inclusão de valores, o próximo passo é instalar a biblioteca.
Após criar um projeto com o Angular CLI, no terminal do projeto vamos informar:
npm install firesql firebase
Assim, adicionamos as bibliotecas do Firebase e o FireSql.
No componente principal vamos iniciar realizando os seguintes imports:
import { Component, OnInit } from ‘@angular/core’;
import { FireSQL } from ‘firesql’;
import * as firebase from ‘firebase/app’;
import ‘firebase/firestore’;
Dessa forma podemos declarar os objetos necessários para a realização das consultas.
Na sequência podemos declarar um objeto que realizará as consultas, definindo também as configurações para conectar no Firebase:
fireSQL;
constructor() {
let firebaseConfig = {
apiKey: '<your-api-key>',
authDomain: '<your-auth-domain>',
databaseURL: '<your-database-url>',
storageBucket: '<your-storage-bucket>',
messagingSenderId: 'seus-dados',
appId: 'seus-dados'
};
firebase.initializeApp(firebaseConfig);
this.fireSQL = new FireSQL(firebase.firestore());
}
No método construtor definimos as configurações do projeto obtidas na página do projeto no Firebase, inicializando o app e instanciando o objeto fireSQL.
A partir desse momento já podemos escrever as consultas.
Começaremos testando operador WHERE na coleção cidades. O objetivo é apresentar todos os documentos que tem a uf do Paraná e a população superior a 10 mil.
A função ficou assim:
consultaComWhere() {
const cidades = this.fireSQL.query(`
SELECT nome as cidade, uf, populacao
FROM cidades
Where uf = 'PR' and populacao > 10000
`);
cidades.then(lista => {
for (let cidade of lista) {
console.log(`${cidade.cidade} - UF: ${cidade.uf} - População: ${cidade.populacao}`)
}
})
}
Utilizamos a sintaxe clássica do SQL utilizando SELECT FROM e ainda usamos um alias para o campo nome.
O método query retorna uma promise pela qual podemos recuperar no callback o array do resultado.
Realizamos um loop para exibir no console as propriedades cidade, uf e populacao.
Para testar, chamamos a função no método ngOnInit().
ngOnInit(): void {
this.consultaComWhere()
}
O resultado no console:
Podemos utilizar o operador LIKE para campos do tipo string, usando o curinga %.
consultaComLike() {
const estados = this.fireSQL.query(`
SELECT * FROM estados
WHERE nome LIKE 'P%'
`);
estados.then(lista => {
for (let uf of lista) {
console.log(`${uf.nome} - UF: ${uf.sigla} `)
}
})
}
No console, a relação de todos os estados iniciados com a letra P:
Podemos definir um conjunto de valores no parâmetro utilizando o IN:
consultaComIN() {
const cidades = this.fireSQL.query(`
SELECT nome as cidade, uf, populacao
FROM cidades
WHERE uf IN ('PR', 'SP')
`);
cidades.then(lista => {
for (let cidade of lista) {
console.log(`${cidade.cidade} - UF: ${cidade.uf} - População: ${cidade.populacao}`)
}
})
}
E o resultado será o seguinte:
Cidades de São Paulo e do Paraná. Uma forma comum de modelar em NoSQL é a utilização de objetos aninhados. Utilizamos essa abordagem na propriedade endereco da pessoa.
Para fazer consultas, devemos usar as propriedades entre acentos graves:
consultaComObjetoAninhado() {
const pessoas = this.fireSQL.query(`
SELECT nome, endereco FROM pessoas
WHERE \`endereco.bairro\` = 'Centro'
`);
pessoas.then(lista => {
console.log(lista)
for (let pessoa of lista) {
console.log(`${pessoa.nome} - Endereco: ${pessoa.endereco.rua} Bairro: ${pessoa.endereco.bairro} `)
}
})
}
Definimos uma consulta que retorne todos os registros de pessoas do Centro. Observe que devemos usar as barras invertidas, pois já estamos usando o “`” no início da consulta.
Na próxima função vamos implementar verificando o conteúdo de um campo do tipo array. Usaremos o operador CONTAINS:
consultaArrays() {
const pessoas = this.fireSQL.query(`
SELECT nome, endereco, tags FROM pessoas
WHERE tags CONTAINS 'saúde'
`);
pessoas.then(lista => {
console.log(lista)
for (let pessoa of lista) {
console.log(`${pessoa.nome} - Interesses: ${JSON.stringify(pessoa.tags)}`)
}
})
}
Resultado:
Verificamos no campo tags o valor saúde. Para exibir, utilizamos a função JSON.stringify.
Podemos também realizar cálculos como média, mínimo e máximo dos valores. A função seguinte apresenta a implementação:
consultaEstatistica() {
const estatistica = this.fireSQL.query(`
SELECT MIN(idade) as minimo, AVG(idade) as media, MAX(idade) as maximo
FROM pessoas`);
estatistica.then(lista => {
console.log(lista)
for (let estatistica of lista) {
console.log(`Mínimo: ${estatistica.minimo} - Máximo: ${estatistica.maximo} - Média: ${estatistica.media}`)
}
})
}
O console ficou:
Infelizmente a biblioteca ainda tem algumas restrições e limites, como:
- Somente SELECT por enquanto. Há previsão para implementações futuras para INSERT, UPDATE e DELETE
- Não suporta JOINs nem negação NOT
- LIMIT não aceita OFFSET somente um número
- GROUP BY não pode ser utilizado com ORDER BY
De qualquer forma, é uma biblioteca muito interessante, pois é ajustada para criar uma quantidade mínima de consultas nos servidores do Firebase.
Fica a dica!