pouchdb

Consultas rápidas no PouchDB

Como uma continuidade do último post sobre PouchDB, nesse artigo iremos aprofundar nas formas de realizar queries no banco de dados. Basicamente existem três formas de realizarmos consultas no PouchDB, uma é através da API allDocs() e as outras duas são através da API query(), onde temos as queries temporárias e as queries persistentes.

allDocs()

Utilizando a API allDocs() sem passar parâmetros nós basicamente estamos buscando por todos os registros de nosso banco de dados. Porém, embora pareça simples, essa API é bastante poderosa, pois ela nos fornece algumas opções para realizar nossas consultas. Dentre elas, as que eu mais utilizo são:

  • include_docs: caso essa propriedade não seja especificada todos os documentos serão retornados apenas com _id e _rev;
  • startkey & endkey: busca os documentos cujo IDs estão inclusos no intervalo especificado por essas opções;
  • limit: número máximo de documentos a serem retornados;
  • skip: número de documentos que iremos “pular” em nossa consulta;
  • descending: retorna os documentos em ordem inversa;
  • para outras opções acesse a documentação do allDocs().

Mas como podemos usar essas opções em nosso favor? Bom, para fazer uma paginação é bem claro que podemos usar as opções limit e skip, mas e se eu não quiser que todos os documentos do meu banco de dados sejam retornados, é possível filtrá-los?

Utilizando o startkey e o endkey nós podemos buscar apenas os documentos que estão em um determinado intervalo de IDs, certo? Imagine uma situação onde precisamos cadastrar “pessoas” e “empresas” em nosso banco de dados e queremos poder filtrar por cada um desses dois tipos de documentos.Utilizando as opções startkey e endkey nós podemos fazer isso desde que a gente defina os IDs de nossos documentos ao invés de deixar que o PouchDB faça isso.

var db = new PouchDB('mydb');

db.put({
    _id: 'pessoa-' + new Date().getTime(),
    nome: 'Jean Lucas de Carvalho',
    empresa: 'Futuring'
});

db.put({
    _id: 'pessoa-' + new Date().getTime(),
    nome: 'Michael Villander',
    empresa: 'Front In Brazil'
});

db.put({
    _id: 'empresa-' + new Date().getTime(),
    nome: 'Futuring',
    cidade: 'Goiânia'
});

No exemplo acima nós criamos nosso banco de dados e cadastramos três documentos nele: dois do tipo “pessoa” e um do tipo “empresa”. Agora realizaremos uma consulta utilizando o allDocs() buscando apenas os documentos com o tipo “pessoa”:

db.allDocs({
    include_docs: true,
    startkey: 'pessoa-',
    endkey: 'pessoa-\uffff'
}).then(function (result) {
    console.dir(result);
});

O exemplo acima nos retornará todos os documentos cujo _id começa com pessoa-. Essa consulta funciona graças ao caractere Unicode \uffff, pois é através dele que nós fazemos buscas por prefixo, ou seja, estamos dizendo ao PouchDB "me retorne todos os documentos cujo _id começa com pessoa-".

Essa é uma das formas de definir tipos de documentos no PouchDB, mas as vezes nós não podemos alterar a forma padrão como o banco de dados trata os IDs e pra esses casos uma outra forma bastante usada para definir tipos de documentos é atribuir uma propriedade ao documento especificando seu tipo como veremos na sessão a seguir.

query()

As vezes o allDocs() não é o suficiente para realizar consultas em nossas aplicações e aí nós precisamos de uma outra API que nos dê mais possibilidades, como fazer um Map/Reduce por exemplo. No PouchDB essa API é a query() e ela possui algumas peculiaridades.

Temporary queries

Como o próprio nome diz queries temporárias são temporárias, ou seja, elas são criadas no momento da consulta e depois deixam de existir. Por esse motivo elas não são indexadas o que acaba tornando esse tipo de query bem lenta se compararmos às Persistent Queries ou ao allDocs().

Para implementar o exemplo anterior utilizando a API query() nós não precisamos alterar a forma como o PouchDB trabalha com IDs. Na verdade nós devemos adicionar a todos os documentos uma propriedade que o identifique, como no exemplo a seguir, onde definimos uma propriedade type para cada documento e depois fazemos uma consulta que nos retorna todos os documentos cujo valor da propriedade type é igual a pessoa.

var db = new PouchDB('mydb');

db.post({
    nome: 'Jean Lucas de Carvalho',
    empresa: 'Futuring',
    type: 'pessoa'
});

db.post({
    nome: 'Michael Villander',
    empresa: 'Front In Brazil',
    type: 'pessoa'
});

db.post({
    nome: 'Futuring',
    cidade: 'Goiânia',
    type: 'empresa'
});

db.query(function (doc, emit) {
    emit(doc.type);
}, {key: 'pessoa', include_docs: true}).then(function (result) {
    // retorna todos os documentos cujo valor da propriedade "type" é igual a "pessoa"
});

A função emit faz parte da API de map/reduce do CouchDB. O que essa função basicamente faz é emitir, para cada documento, doc.type como key.

Persistent queries

Persistente queries são a forma recomendada de se usar a API query() segundo a documentação do PouchDB. E a diferença básica desse tipo de queries para as Temporary queries é o fato de que elas são salvas no banco e indexadas. Pelo fato de serem indexadas elas são bem mais performáticas que as queries temporárias. Para criarmos uma query persistente nós devemos seguir 2 passos:

  1. Criar um design document
var ddoc = {
    _id: '_design/typeIndex',
    views: {
        by_type: {
            map: function (doc) { emit(doc.type); }.toString()
        }
    }
};

db.put(ddoc).then(function () {
    // Persistent query criada com sucesso
});

O método .toString() ao final de nossa função map é necessário para transformar o objeto em um JSON válido.

  1. E então nós podemos usá-la de forma semelhante às queries temporárias:
db.query('typeIndex/by_type', {key: 'pessoa'}).then(function (result) {
    // retorna todos os documentos cujo valor da propriedade "type" é igual a "pessoa"
});

É interessante frisar que nossa primeira consulta realizada será relativamente lenta pois os índices só são criados quando nós realizamos uma consulta. A documentação recomenda a realização de uma consulta com limit: 0 para criar os índices sem retornar os documentos.

db.query('typeIndex/by_type', {limit: 0}).then(function (result) {
    // índices criados
});

Extra: Pouchdb-find

Agora que já conhecemos as principais formas de realizarmos consultas no Pouchdb é interessante conhecermos o Pouchdb-find, plugin que busca trazer uma forma de fazer consultas inspiradas na API do MongoDB. O plugin faz a mesma coisa que os métodos anteriormente apresentados, porém com menos código.

Para reproduzirmos o exemplo anterior utilizando o Pouchdb-find, nós precisamos criar um índice utilizando o método createIndex e então podemos utilizar o método find para realizar nossas consultas.

db.createIndex({
    index: {fields: ['type']}
}).then(function () {
    return db.find({
        selector: {type: 'pessoa'}
    });
}).then(function(result){
    // retorna todos os documentos cujo valor da propriedade "type" é igual a "pessoa"
});

O Pouchdb-find ainda não possui todos os métodos que necessitamos em muitas ocasiões (eu mesmo já precisei utilizar o $in), mas já é possível utilizá-lo. Para maiores informações acesse a documentação do plugin.

Conclusão

Existem muitas formas de realizarmos consultas no PouchDB e cabe ao desenvolvedor ter a sensibilidade de saber qual a melhor opção para cada caso, mas para sabermos qual a melhor opção é necessário conhecermos cada uma delas e espero ter conseguido apresentar o tema e esclarecer possíveis dúvidas.

Agora que já conhecemos as Persistent queries no próximo artigo a respeito do PouchDB irei apresentar as formas de sincronizar apenas determinados documentos no nosso banco de dados.

Jean Lucas de Carvalho

Instrutor at Futuring
Desenvolvedor Front-end na HE:labs e instrutor na Futuring, especialista em aplicativos híbridos com Cordova e Ionic Framework, faxineiro do Frontend Goiás, ex-subcelebridade do Google Developer Bus Brazil, sedentário e o lindão ali da foto. Tudo isso quando não está com preguiça demais pra fazer alguma coisa.

Latest posts by Jean Lucas de Carvalho (see all)

Comentários