O desempenho é um dos aspectos mais importantes a ter em conta no desenvolvimento de aplicações Web. Analisar a forma como os dados são obtidos a partir de uma base de dados é um bom ponto de partida para procurar melhorias. Neste artigo, encontrará exemplos de como melhorar o desempenho através da utilização de funções agregadas e da filtragem de dados ao nível da base de dados.
Para começar, um pouco de contexto
Este artigo é inspirado num problema real que já me foi colocado. Lidar com ele ensinou-me muito, e ainda o tenho como referência na minha mente. Penso que os exemplos são um bom recurso de aprendizagem, pois podem clarificar muita coisa. Neste artigo, gostaria de partilhar consigo alguns exemplos de utilização dos métodos de consulta do Active Record.
Para não introduzir pormenores específicos do domínio, utilizarei uma aplicação de amostra para uma biblioteca para ilustrar os exemplos. É tudo bastante simples, como mostra o diagrama abaixo. Temos quatro tabelas: autores, livros, utilizadores e alugueres. Um utilizador pode pedir emprestado muitos livros e um livro pode ser emprestado por muitos utilizadores, pelo que precisamos de uma tabela de junção para modelar relações de muitos para muitos. No nosso caso, é a tabela de alugueres. Também guardamos aí algumas informações adicionais, que são as datas de empréstimo e devolução. Um autor pode ter muitos livros atribuídos ao seu nome. Um livro também tem um atributo que define o seu género.
Estatísticas de leitura do utilizador
A tarefa era preparar estatísticas para um único utilizador, de modo a podermos saber quantos livros de cada género foram emprestados. A minha primeira ideia foi ir buscar todos os livros que foram emprestados pelo utilizador, agrupá-los por género e depois fazer o mapeamento, para que cada género tivesse um número de livros atribuído em vez de uma lista. Eis o que me ocorreu:
Embora esta abordagem funcione e tenha um aspeto limpo, não utiliza todas as possibilidades que os métodos de consulta Active Record oferecem. Graças a eles, podemos filtrar e agregar dados a nível da base de dados sem utilizar SQL em bruto diretamente no nosso código. Operar a um nível db também aumenta a nossa eficiência.
No exemplo acima, podemos utilizar o método group em vez do group do Rubypor método. Aplicar-se-á a GRUPOBY na consulta tSQL. Além disso, o método de mapeamento e tamanho pode ser substituído por uma função de agregação de contagem. No final, ficamos com uma consulta que se parece com isto:
Em seguida, encontrará outras formas de utilizar os métodos de consulta que considero que vale a pena conhecer.
Convite para utilizadores inactivos
TAREFA: Filtrar os utilizadores que nunca pediram um livro emprestado ou que o fizeram há mais de um ano.
Poderíamos ir buscar todos os utilizadores, incluindo os alugueres associados, e depois filtrá-los utilizando o método select.
User.includes(:rentals).select do |user|
utilizador.alugueres.vazio? || utilizador.alugueres.nenhum? { |rental| rental.start_date >= Date.today - 1.year }
end
Mas, como é óbvio, não é necessário ir buscar tudo. Utilizando os métodos de consulta, podemos filtrar tudo ao nível da base de dados. Primeiro, vamos selecionar os utilizadores que pediram alguns livros emprestados no último ano e depois excluí-los da seleção final.
TAREFA: Obter autores com um ou zero livros emprestados
Fazê-lo com o método select é super simples, mas mais uma vez - não há necessidade de operar num conjunto tão grande de dados, uma vez que a base de dados pode filtrá-los para nós:
É importante lembrar-se de uma coisa ao utilizar left_joins (e outer joins em geral). Se existirem registos na tabela da esquerda (aqui: autores) que não tenham registos correspondentes na tabela da direita (aqui: livros), então, em resultado, as colunas da tabela da direita serão preenchidas com valores nulos.
Como também precisamos de autores com um livro atribuído no sistema, há mais algumas operações a efetuar. Teremos de efetuar o agrupamento, a contagem e a adição de uma condição. Eis como juntar tudo isto:
A condição vem depois da função de agregação, pelo que temos de utilizar a cláusula HAVING, em vez da cláusula WHERE, para a especificar.
Vale a pena verificar os métodos de consulta do Active Record quando se pensa no desempenho da aplicação. Podem simplificar o seu código e torná-lo mais rápido. Espero que os exemplos partilhados o ajudem a explorar as possibilidades que os métodos de consulta têm para oferecer.