2013-01-22 9 views
9

Ich habe eine App, die eine Anzahl von Post Modellen hat, von denen jedes belongs_to ein User Modell ist. Wenn diese Beiträge veröffentlicht werden, wird ein PublishedPost Modell erstellt, das belongs_to das entsprechende Post Modell.ActiveRecord: kann `pluck` nach` where` Klausel mit eifrig geladenen Assoziationen nicht verwenden

Ich versuche eine ActiveRecord-Abfrage zu erstellen, um veröffentlichte Posts zu finden, die einem Benutzernamen entsprechen, und dann die IDs dieser veröffentlichten Posts zu erhalten, aber ich erhalte einen Fehler, wenn ich versuche, die pluck-Methode zu verwenden. Laden Sie meine Assoziationen und suchen Sie sie mit der where Methode.

Hier ist (Teil) mein Controller:

class PublishedPostsController < ApplicationController 

    def index 
    ar_query = PublishedPost.order("published_posts.created_at DESC") 

     if params[:searchQuery].present? 
     search_query = params[:searchQuery] 
     ar_query = ar_query.includes(:post => :user) 
          .where("users.name like ?", "%#{search_query}%") 
     end 

    @found_ids = ar_query.pluck(:id) 

    ... 

    end 

end 

Wenn die pluck Methode aufgerufen wird, bekomme ich diese:

ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'users.name' in 'where clause': SELECT id FROM `published_posts` WHERE (users.name like '%Andrew%') ORDER BY published_posts.created_at DESC 

ich die Ergebnisse erhalten, kann ich suche mit

@found_ids = ar_query.select(:id).map{|r| r.id} 

aber ich würde eher pluck verwenden, wie es scheint, der sauberere Weg zu gehen. Ich kann nicht herausfinden, warum es nicht funktioniert. Irgendwelche Ideen?

Antwort

10

Sie müssen und sollten joins statt includes hier tun.

Die beiden Funktionen sind ziemlich ähnlich mit der Ausnahme, dass die Daten aus joins nicht im Ergebnis der Abfrage zurückgegeben wird, während die Daten in einem includes10 ist.

In dieser Hinsicht sind includes und pluck Art von gegensätzlichen. Man sagt mir, dass du mir alle möglichen Daten zurückgeben kannst, während die andere sagt, dass du mir nur dieses ein bisschen geben sollst.

Da Sie nur eine kleine Menge der Daten möchten, möchten Sie joins tun. (Seltsamer select, die scheint auch etwas gegensätzlich noch funktioniert, aber man würde die Mehrdeutigkeit über id in diesem Fall entfernen müssen.)

Probieren Sie es in der Konsole aus und Sie werden sehen, dass includes eine Abfrage verursacht, die Art sieht wie folgt: SELECT "posts"."id" as t0_ro, "posts"."text" as t0_r1, "users"."id" as t1_r0, "users"."name" as t1_r1 ... Wenn Sie eine pluck Anweisung anheften, verschwinden alle diese verrückten tx_ry-Spalten und werden durch das ersetzt, was Sie angegeben haben.

Ich hoffe, dass hilft, aber wenn nicht vielleicht kann dieser RailsCast. Es wird um die 5-Minuten-Marke erklärt.

http://railscasts.com/episodes/181-include-vs-joins

9

Wenn Sie hier bekam durch die Suche „Schienen zupfen mehrdeutige Spalte“, Sie wissen, möchten Sie vielleicht nur query.pluck(:id) mit ersetzen:

query.pluck("table_name.id") 
+0

Yessss! Ich weiß nicht, warum das nicht in den Dokumenten ist? Zumindest habe ich es nicht gesehen. – FireDragon

0

Ihre Anfrage nicht funktionieren würde, wie es ist geschrieben, auch ohne den Zupfruf.

Grund dafür ist, dass Ihre WHERE-Klausel Literal-SQL enthält, das auf die Benutzertabelle verweist, die Rails nicht bemerkt und mehrere Abfragen verwendet und sich im Speicher (.preload()) anmeldet, anstatt auf Datenbankebene (.eager_load()):

SELECT * from published_posts WHERE users.name like "pattern" ORDER BY published_posts.created_at DESC 
SELECT * from posts WHERE id IN (a_list_of_all_post_ids_in_publised_posts_returned_above) 
SELECT * from users WHERE id IN (a_list_of_all_user_ids_in_posts_returned_above) 

Die erste der 3 Abfragen schlägt fehl und es ist der Fehler, den Sie bekommen.

Um zu erzwingen, dass Rails einen JOIN verwendet, sollten Sie entweder die explizite .eager_load() anstelle von .includes() verwenden oder eine .references() Klausel hinzufügen.

Anders als das, was @Geoff beantwortet hat, müssen Sie nicht wirklich. Includes() hier, sondern eher ein .joins().

Verwandte Themen