2012-12-04 25 views
0

我正在將應用程序從php轉換爲rails,而且我仍然在學習圍繞rails和AR的方式。我想列出當前用戶還不是成員的組。Rails 3 - 列出當前用戶還不是成員的組

失敗的方法

  1. Cause.select('causes.*').joins(:users).group('causes.id').where("cause_user_memberships.user_id NOT IN (?)", current_user.id)

  2. Cause.select('causes.*').joins(:users).group('causes.id').where("cause_user_memberships.user_id NOT IN (SELECT cause_user_memberships.cause_id FROM cause_user_memberships WHERE cause_user_memberships.user_id =(?))", current_user.id)

  3. 還有更多...

感謝您的幫助!

約車型的一些信息

User.rb(片段)

has_many :cause_user_memberships 
has_many :causes, :through => :cause_user_memberships 

Cause.rb

attr_accessible :title, :location, :description,... 
has_many :cause_user_memberships 
has_many :users, :through => :cause_user_memberships 

Cause_User_Membership.rb(< --probably不我最好的模特名字)

# == Schema Information 
# 
# Table name: cause_user_memberships 
# 
# id   :integer   not null, primary key 
# user_id :integer   not null 
# cause_id :integer   not null 
# created_at :datetime   not null 
# updated_at :datetime   not null 
# 

class CauseUserMembership < ActiveRecord::Base 
    attr_accessible :cause_id, :user_id 

    belongs_to :user 
    belongs_to :cause, :counter_cache => :users_count 
     accepts_nested_attributes_for :cause 
     validates_uniqueness_of :user_id, :scope =>[:cause_id] 

end 

更新:跟進

DERP,你是對的,它的工作!謝謝!

小跟進,查詢時間似乎很長。這是否表明存在問題?我在每張表中的記錄少於20條。 (下面是2個查詢結果,一個包含地理編碼寶石,我打算用,另一個不很抱歉,如果這是一個有些凌亂。)

在Rails控制檯:

Cause Load (1003.0ms) SELECT "causes".* FROM "causes" LEFT JOIN cause_user_memberships ON cause_user_memberships.cause_id = causes.id AND cause_user_memberships.user_id = 1 WHERE (cause_user_memberships.id IS NULL) 

EXPLAIN (34.3ms) EXPLAIN SELECT "causes".* FROM "causes" LEFT JOIN cause_user_memberships ON cause_user_memberships.cause_id = causes.id AND cause_user_memberships.user_id = 1 WHERE (cause_user_memberships.id IS NULL) 

查詢計劃

Hash Right Join (cost=10.45..37.99 rows=1 width=3168) 
    Hash Cond: (cause_user_memberships.cause_id = causes.id) 
    Filter: (cause_user_memberships.id IS NULL) 
    -> Seq Scan on cause_user_memberships (cost=0.00..27.50 rows=7 width=8) 
     Filter: (user_id = 1) 
    -> Hash (cost=10.20..10.20 rows=20 width=3168) 
     -> Seq Scan on causes (cost=0.00..10.20 rows=20 width=3168) 
(7 rows) 

隨着地理編碼本地主機上:

User Load (18.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1 
    Cause Load (49.6ms) SELECT causes.*, 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((40.714269 - causes.lat) * PI()/180/2), 2) + COS(40.714269 * PI()/180) * COS(causes.lat * PI()/180) * POWER(SIN((-74.005972 - causes.lng) * PI()/180/2), 2))) AS distance, CAST(DEGREES(ATAN2(RADIANS(causes.lng - -74.005972), RADIANS(causes.lat - 40.714269))) + 360 AS decimal) % 360 AS bearing FROM "causes" LEFT JOIN cause_user_memberships 
ON cause_user_memberships.cause_id = causes.id 
AND cause_user_memberships.user_id = 1 WHERE (causes.lat BETWEEN 36.37231550667456 AND 45.05622249332544 AND causes.lng BETWEEN -79.73435509229111 AND -68.27758890770889 AND 3958.755864232 * 2 * ASIN(SQRT(POWER(SIN((40.714269 - causes.lat) * PI()/180/2), 2) + COS(40.714269 * PI()/180) * COS(causes.lat * PI()/180) * POWER(SIN((-74.005972 - causes.lng) * PI()/180/2), 2))) <= 300) AND (cause_user_memberships.id IS NULL) ORDER BY distance ASC 

Completed 200 OK in 1068ms (Views: 49.1ms | ActiveRecord: 791.4ms)

+0

左連接通常比內連接慢。通過在連接表上的'cause_id'和'user_id'列上應用索引,您可能會看到一些改進。在表示「Seq Scan」的查詢計劃中,這意味着postgres只是遍歷表,直到找到結果。 – patrickmcgraw

+0

感謝您的建議。我今晚會做。 1000毫秒似乎不是太長?當我用php/MySQL做了幾乎相同的事情並且沒有活躍的記錄時,它將會更快。抓住我的頭爲什麼有這麼大的差異。 –

+0

它似乎有點長。但是有很多變數。 ActiveRecord在內存中構建對象的副本,因此與查詢速度沒有直接關係的開銷較大。您可以在像pgAdmin這樣的SQL控制檯中檢查查詢,以便將ActiveRecord作爲變量移除。 – patrickmcgraw

回答

2

在軌joins(:symbol)語句變成一個內連接因而不適合用於尋找不存在的關係。您可以手工編寫,以使其左聯接聯接:

Cause.joins(""" 
    LEFT JOIN cause_user_memberships 
    ON cause_user_memberships.cause_id = causes.id 
    AND cause_user_memberships.user_id = #{current_user.id} 
""").where("cause_user_memberships.id IS NULL") 

修訂

內加入將防止一組原因的創造,不具有映射給定的用戶。例如:

Causes 
id | name 
============ 
1 | Cause 1 
2 | Cause 2 

Users 
id | name 
============ 
1 | User 1 

Cause User Memberships 
id | user_id | cause_id 
======================= 
1 | 1  | 1 

SELECT * 
FROM causes 
INNER JOIN cause_user_memberships 
ON cause_user_memberships.cause_id = causes.id 
AND cause_user_memberships.user_id = 1 

將返回

causes.id | causes.name | cause_user_memberships.id | cause_user_memberships.user_id | cause_user_memberships.cause_id 
=========================================================================================================================== 
1   | Cause 1  | 1       | 1        | 1 

,你不能爲了弄清用戶不屬於原因執行任何更多的邏輯。

SELECT * 
FROM causes 
LEFT JOIN cause_user_memberships 
ON cause_user_memberships.cause_id = cause.id 
AND cause_user_memberships.user_id = 1 

將返回

causes.id | causes.name | cause_user_memberships.id | cause_user_memberships.user_id | cause_user_memberships.cause_id 
=========================================================================================================================== 
1   | Cause 1  | 1       | 1        | 1 
2   | Cause 2  | null      | null       | null 

在這種情況下,所有的原因將獲得一排它們是否匹配在cause_user_memberships表或不記錄。現在,您可以申請附加條件梳理出用戶不屬於(其中cause_user_membership.id爲空)的原因。

+0

他的關係確實存在,他擁有的has_many:用戶因此加入應該工作沒有? –

+0

@LeoCorrea的主要問題是'內join'。它不能返回不存在的'cause_user_membership'記錄。我會在我的答案中增加一些內容。 – patrickmcgraw

+0

感謝您的幫助。快速跟進上面 –