2

Voici un défi auquel je suis confronté, en utilisant Rails 5 (J'utilise le service Skylight qui rapporte N + 1 requêtes, et leur solution recommandée est here, mais ce n'est pas assez dans mon cas).Rails: Optimiser la requête N + 1 avec la même table one_to_many et les conditions de portée

J'ai une table nodes et un seul Node peut avoir plusieurs nodes qui s'y rapporte (il y a une colonne appelée parent_node_id) qui me donne la possibilité de relier un à plusieurs.

class Node < ApplicationRecord 
    ... 
    belongs_to :parent_node, foreign_key: :parent_node_id, class_name: 'Node', optional: true, inverse_of: :nodes 
    has_many :nodes, foreign_key: :parent_node_id, class_name: 'Node' 
    ... 
end 

Important Le niveau de la hiérarchie est maximale 1. Cela signifie qu'un node.nodes.first.node ne se produit pas. Un node qui a un parent_node n'a plus de nodes.

Le problème est que je suis confronté à des problèmes de performance avec N + 1 car il ne suffit pas d'inclure le nodes dans la requête d'origine, car dans la boucle j'interroge chaque enregistrement avec une portée différente. Voici un exemple de code qui expose le problème:

# In a controller, including nodes so it does not query inside 
nds = Node.some_scope.include(:nodes) 
nds.each do |nd| 
    ... 
    # In a model 
    # If I loop inside, there's no extra SQL queries, all good 
    nd.nodes.each do |nd2| 
    ... 
    end 
    ... 
    # In a model 
    # Here's the N+1 issue 
    nd.nodes.another_scope.each do |nd3| 
    ... 
    end 
    # Returns a value to the controller 
    ... 
end 

qui déclenche de toute façon les requêtes SQL pour chaque variable nd3 car il y a another_scope qui modifie les valeurs nds d'origine, et je ne peux pas comprendre la condition dans les nds valeurs parce que la nodes qui ne répondent pas aux critères another_scope sont requis pour nd2.

Existe-t-il un moyen d'optimiser cela?

+0

vous pouvez ajouter une autre association 'has_many: what_nodes, -> {les conditions d'un autre scope écrites ici}, foreign_key:: parent_node_id, class_name: 'Node'' maintenant vous pouvez le faire 'nds = Node.some_scope.include (: nodes,: whatever_nodes)'. –

+1

Je pense que @DennisKrupelnik a suggéré une réponse appropriée. Juste pour clarifier, vous devriez utiliser la méthode '# includes 'au lieu de' # include' pour le traitement par lots de requêtes –

+0

@helperhelperov Oui, il l'a fait. Je signale simplement que cela peut être amélioré. Merci. – unmultimedio

Répondre

2

Remplacer another_scope avec select { |n| n.satisfies_another_scope_criteria? }

Puisque vous avez déjà tiré par les cheveux tous les enfants, il n'y a pas besoin de les filtrer dans la base de données une nouvelle fois moins qu'il y ait une clause limit dans another_scope

+0

Vous avez raison !, mais si de toute façon je répète tous ces passages (dans la section 'each'), ne vaudrait-il pas mieux que 'next sauf critère' comme première ligne dans la boucle? – unmultimedio

+0

Vous devez le référencer. Avec CRuby, j'irais avec 'select'. –