Interface d'interrogation des enregistrements actifs
Marta Polec
La performance est l'un des aspects les plus importants à prendre en compte lors du développement d'applications web. L'analyse de la manière dont les données sont extraites d'une base de données est un bon point de départ pour rechercher des améliorations. Dans cet article, vous trouverez des exemples sur la manière d'améliorer les performances en utilisant des fonctions agrégées et en filtrant les données au niveau de la base de données.
Un peu de contexte pour commencer
Cet article s'inspire d'un problème réel que j'ai eu à résoudre. La résolution de ce problème m'a beaucoup appris, et je l'ai toujours en tête comme référence. Je pense que les exemples sont une bonne ressource d'apprentissage, ils peuvent clarifier beaucoup de choses. Dans cet article, j'aimerais partager avec vous quelques exemples d'utilisation des méthodes de requête Active Record.
Afin de ne pas introduire de détails spécifiques au domaine, j'utiliserai un exemple d'application pour une bibliothèque afin d'illustrer les exemples. Tout est assez simple, comme le montre le diagramme ci-dessous. Nous avons quatre tables : auteurs, livres, utilisateurs et locations. Un utilisateur peut emprunter plusieurs livres et un livre peut être emprunté par plusieurs utilisateurs, nous avons donc besoin d'une table de jonction pour modéliser les relations de plusieurs à plusieurs. Dans notre cas, il s'agit de la table des locations. Nous y stockons également des informations supplémentaires, à savoir les dates d'emprunt et de retour. Un auteur peut avoir plusieurs livres à son nom. Un livre possède également un attribut définissant son genre.
Statistiques de lecture des utilisateurs
La tâche consistait à préparer des statistiques pour un utilisateur unique, afin de pouvoir déterminer le nombre de livres de chaque genre qui ont été empruntés. Ma première idée a été de récupérer tous les livres qui ont été empruntés par l'utilisateur, de les regrouper par genre et ensuite de faire le mapping, de sorte que chaque genre ait un nombre de livres assignés au lieu d'une liste. Voici ce que j'ai trouvé :
Hash[Book.joins(:locations).where(locations : { user : user }).group_by(&:genre).map { |genre, livres| [genre, livres.taille] }]
Bien que cette approche fonctionne et ait l'air propre, elle n'utilise pas toutes les possibilités offertes par les méthodes de requête d'Active Record. Grâce à elles, nous sommes en mesure de filtrer et d'agréger des données au niveau de la base de données sans utiliser le langage SQL brut directement dans notre formulaire de demande d'information. code. Le fait d'opérer au niveau de la db accroît également notre efficacité.
Dans l'exemple ci-dessus, nous pouvons utiliser la méthode group au lieu de la méthode group de Rubypar méthode. Il appliquera le GROUPEBY à la requête tSQL. En outre, la méthode de mappage et de taille peut être remplacée par une fonction d'agrégation de comptage. Au final, nous nous retrouvons avec une requête qui ressemble à ceci :
Book.joins(:locations).where(locations : { user : user }).group(:genre).count(:livres)
Cela semble encore plus simple !
Autres exemples utiles
Vous trouverez ci-dessous d'autres façons d'utiliser les méthodes d'interrogation que je trouve intéressantes à connaître.
Invitation pour les utilisateurs inactifs
TÂCHE : Filtrer les utilisateurs qui n'ont jamais emprunté de livre ou qui l'ont fait il y a plus d'un an.
Nous pourrions récupérer tous les utilisateurs en incluant les locations associées, puis les filtrer à l'aide de la méthode select.
User.includes(:rentals).select do |user|
user.rentals.empty ? || user.rentals.none ? {rental| rental.start_date >= Date.today - 1.year }
end
Mais, bien sûr, il n'est pas nécessaire de tout récupérer. En utilisant les méthodes de requête, nous pouvons filtrer au niveau de la base de données. Tout d'abord, sélectionnons les utilisateurs qui ont emprunté des livres au cours de l'année écoulée et excluons-les de la sélection finale.
TÂCHE : Obtenir des auteurs ayant un ou zéro livre emprunté
Le faire avec la méthode select est très simple, mais encore une fois, il n'est pas nécessaire d'opérer sur un tel ensemble de données puisque la base de données peut les filtrer pour nous :
Ensuite, il est facile et rapide de filtrer les auteurs qui n'ont aucun livre attribué :
Author.left_joins(:books).where(books : { id : nil })
Il est important de se souvenir d'une chose lors de l'utilisation des jointures gauches (et des jointures externes en général). S'il y a des enregistrements dans la table de gauche (ici : auteurs) qui n'ont pas d'enregistrements correspondants dans la table de droite (ici : livres), alors les colonnes de la table de droite seront remplies avec des valeurs nulles.
Comme nous avons également besoin d'auteurs ayant un livre attribué dans le système, il y a quelques opérations supplémentaires à effectuer. Nous devrons procéder au regroupement, au comptage et à l'ajout d'une condition. Voici comment assembler le tout :
La condition vient après la fonction d'agrégation, nous devons donc utiliser la clause HAVING au lieu de la clause WHERE pour la spécifier.
Les méthodes d'interrogation d'Active Record méritent d'être vérifiées lorsque l'on pense aux performances d'une application. Elles peuvent simplifier votre code et le rendre plus rapide. J'espère que les exemples présentés vous aideront à explorer les possibilités offertes par les méthodes de requête.