size doesn't COUNT
Publié le 17 janvier 2012 à 10:00 - Quick Tips
Requêtes N+1
Lorsque vous développez avec Rails, vous devez bien sûr faire attention aux requêtes qui sont effectuées et surtout éviter les requêtes N+1.
Les requêtes N+1 sont celles qui sont lancées pour chaque élément d’une liste.
Prenons l’exemple suivant :
# Dans le contrôleur
@articles = Article.all
# SELECT articles.* FROM articles
# Dans la vue
<% @articles.each do |article| %>
...
Nombre de commentaires: <%= articles.comments.count %>
# SELECT COUNT(*) FROM comments WHERE comments.article_id = XY
...
<% end %>
Pour chaque article listé, une requête va être lancée pour compter ses commentaires.
Heureusement pour nous, Rails fournit, dans l’API d’ActiveRecord un moyen d’éviter ces requêtes N+1. Il s’agit de la méthode includes qui s’utilise comme ceci :
@articles = Article.includes(:comments).all
# SELECT articles.* FROM articles
# SELECT comments.* FROM comments WHERE comments.article_id IN (1, 2, ..., 42)
On peut donc voir ici que l’ensemble des commentaires est récupéré d’un seul coup. Cela nous permet d’accéder aux informations des commentaires d’un article sans requête supplémentaire.
Size vs. Count
Le souci c’est que ça ne règle pas notre problème de count. En effet, si nous faisons de nouveau appel à count, les requêtes seront tout de même effectuées !
L’astuce est donc d’utiliser size plutôt que count sur l’attribut comments de nos articles, ce qui va simplement retourner la taille du tableau contenant les commentaires associés. Ces derniers étant déjà chargés, le compte est bon !
# Dans le contrôleur
@articles = Article.includes(:comments).all
# SELECT articles.* FROM articles
# SELECT comments.* FROM comments WHERE comments.article_id IN (1, 2, ..., 42)
# Dans la vue
<% @articles.each do |article| %>
...
Nombre de commentaires: <%= articles.comments.size %>
...
<% end %>
blog comments powered by Disqus