Rails 3 - 列出当前用户尚未成为其成员的组

时间:2012-12-04 20:24:58

标签: database ruby-on-rails-3 postgresql activerecord

我正在将应用程序从php转换为rails,而我仍然在学习围绕rails和AR的方式。

简单:我想列出当前用户尚未加入的群组。

方法失败

  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. 更多......
  4. 感谢您的帮助!

    有关模型的一些信息

    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 (< - 可能不是我最好的型号名称)

        # == 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个查询结果,一个包含我打算使用的地理编码器gem,另一个不包含。抱歉,如果它有点乱。)

    在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)
    

    在本地主机上使用Geocoder:

    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)

1 个答案:

答案 0 :(得分:2)

在rails中,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") 

<强>已更新

内部联接将阻止创建一组没有映射到给定用户的Cause。例如:

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为null)。