2012-03-10 14 views
19

Sto eseguendo Ruby on Rails 3.1. Ho letto i seguenti articoli e documentazioni su eager loading e mi piacerebbe trovare un modo giusto di fare le cose:Eager caricamento: il modo giusto di fare cose

  1. Eager Loading Associations [La documentazione ufficiale]
  2. ActiveRecord::Associations::ClassMethods (vedere la sezione "eager loading delle associazioni") [La documentazione ufficiale]
  3. Eager loading [Blog article]

Il # 2 dice:

noti che utilizzano termini come Post.includes ([: autore, commenti:]).. In cui ([ '? Comments.approved =', true]) tutti possono avere conseguenze impreviste.

Il # 3 dice che queste conseguenze non intenzionali sono (nota: esempi sono piuttosto gli stessi in modo cito il testo esatto di questo articolo del blog, ma si deve tenere a mente la soluzione, non il specifica implementazione):

Questa query, poiché userebbe un SINISTRA SINISTRO, eliminerebbe anche tutti i post senza un commento con la parola "prima" su uno qualsiasi dei suoi commenti.

In altre parole, se non ci sono oggetti "associati", l'oggetto "associato principale" non verrà caricato. Questo è ciò che accade quando provo a usare il caricamento desideroso da aggiungendo alcune condizioni come .where(:category_relationships => {:user_id => @current_user.id}) in un mio previous question, ma non voglio che ciò accada.

Così (disfattista perché probabilmente non è possibile utilizzare il eager loading, nel mio caso in cui condizione non può essere impostato nella dichiarazione has_many - notare che nel codice sopra la @current_user.id viene "dinamicamente" a differenza negli esempi presenti in siti menzionati), Vorrei sapere se ci sono pratiques/tecniche/strategie in modo da limitare le query del database poiché ho un "problema N + 1".

Forse quelle Pratiques/tecniche/strategie sono implementabili utilizzando il framework Ruby on Rails a tutti ... il # 1 dice:

Anche se attivo Record consente di specificare le condizioni sul ansioso associazioni caricate, proprio come i join, il metodo consigliato è quello di utilizzare i join .

Cosa e come risolvere questo problema nel modo giusto?


Forse una soluzione è quella di recuperare e costruire me stesso ciò che deve essere caricato eseguendo query di database specifici e separati, ma poi il problema sarebbe come "pass"/"socio"/"interpolate" quelli hanno recuperato oggetti "associati" all'oggetto "principale associato" in modo che quelli possano essere usati come "un carico di caricamento"? Cioè, come rendere possibile (vedere lo mentioned question per ulteriori informazioni) per utilizzare il codice come @article.comments e ottenere solo commenti che mi sono caricato da solo? Dopo il il mio caricamento Eger, è possibile/corretto per fare qualcosa come @article.comments = my_eager_loaded_comments così per "passare"/"associare"/"interpolare" i commenti agli articoli?

risposta

2

Da # 2 ActiveRecord::Associations::ClassMethods arriva questo:

se si vuole carico desiderosi solo alcuni membri di un'associazione che è di solito più naturale per includere un'associazione che ha le condizioni definite su di esso:

class Post < ActiveRecord::Base 
    has_many :approved_comments, 
    :class_name => 'Comment', 
    :conditions => ['approved = ?', true] 
end 

Post.includes(:approved_comments) 

Questo caricherà messaggi e carico ansiosi dell'associazione approved_comments, che contai ns solo quei commenti che sono stati approvati.

Se ho capito bene nel contesto, c'è un'ambiguità quando .dove() si applica alla tua richiesta principale con un include(), e AR applica la dove l'intera query limitare le principali risultati a quelli che avere predicati associati qualificanti. Ma se si limita il predicato solo all'associazione come sopra, AR comprende e fornisce tutti i risultati principali e quegli oggetti associati che corrispondono alla loro condizione predicativa.

+3

Non posso usare l'associazione 'has_many' mentre scrivi poiché nel mio caso dovrei indicare qualcosa come' has_many,: conditions => ['user_id =?', @ Current_user.id] '(* non è possibile ! * ... ovviamente per la mia conoscenza). Quindi mi piacerebbe conoscere una soluzione a questo problema per quanto riguarda la funzionalità di caricamento ansioso. – Backo

6

Dopo il mio eager loading, è possibile/corretto di fare qualcosa di simile @ article.comments = my_eager_loaded_comments così a "passare"/"associati"/"interpolare" commenti agli articoli?

Sì, è possibile. Lo faccio regolarmente.

Si noti che la mia soluzione recupera TUTTI gli oggetti associati dal DB. Non penso ci sia alcuna soluzione per recuperare solo gli oggetti di associazione filtrati se la tua condizione è dinamica. La mia soluzione si concentra sul filtraggio degli oggetti dell'associazione richiamati.

Sono supponendo che il requisito sia get a list of articles e in ciascun articolo, eager load the comments of only one particular user.

Nel modello Article:

def self.get_articles_with_comments_of_user(article_ids, user_id) 
    articles = Article.where(id: article_ids).includes(:comments) 

    articles.each do |article| 
    filtered_comments = article.comments.select { |comment| comment.user_id == user_id } 
    article.association("comments").target = filtered_comments 
    end 

    articles 
end 

Nella collezione di articoli restituiti dal metodo di cui sopra, l'associazione commenti avrà solo i commenti di quel particolare utente.

Spero che questo sia quello che stai chiedendo.

+0

"Non penso ci sia alcuna soluzione per recuperare solo gli oggetti di associazione filtrati se la tua condizione è dinamica." - Forse [questo] (http://stackoverflow.com/a/15543228/867305) è la soluzione? – thejaz

+0

Nella risposta collegata, non vedo alcuna condizione dinamica passata per filtrare il carico di caricamento. Potrei capirlo male. Questa risposta descrive solo un'ordinaria associazione "has_many: through'. Come si potrebbe usare questa soluzione nel contesto di questa domanda? – Anjan

Problemi correlati