Banco de Dados

27 mai, 2019

Consultas SQL no Firebase

Publicidade

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!

Links