如何过滤我的数据库查询以不包括基于关联表的记录?

时间:2022-03-04 11:17:25

I have a user model where users rate other users. They do that through a many-to-many association with a table called ratings that has rater_id and ratee_id fields that correspond to user_id (a many-to-many self join on User). The models are:

我有一个用户模型,其中用户评价其他用户。他们通过与名为rating的表的多对多关联来实现这一点,该表具有与user_id(用户上的多对多自联接)对应的rater_id和ratee_id字段。模型是:

# app/models/user.rb
has_many :ratings_as_rater, class_name: "Rating", foreign_key: "rater_id"
has_many :ratings_as_ratee, class_name: "Rating", foreign_key: "ratee_id"

# app/models/rating.rb
belongs_to :rater, class_name: "User"
belongs_to :ratee, class_name: "User"

And there is a compound index on the ratings table:

评级表上有一个复合索引:

# db/schema.rb
create_table "ratings", force: :cascade do |t|
  t.integer "rater_id"
  t.integer "ratee_id"
  t.integer "rate" # Users rated on scale 1-4
  t.index ["ratee_id"], name: "index_ratings_on_ratee_id"
  t.index ["rater_id"], name: "index_ratings_on_rater_id"
  t.index ["rater_id", "ratee_id"], name: "index_ratings_on_rater_id_and_ratee_id", unique: true
end

On the user index page I only want to display the users that the current_user has not yet rated. So in the users_controller instead of:

在用户索引页面上,我只想显示current_user尚未评级的用户。所以在users_controller而不是:

def index
  @users = User.all
end

How would I only display users that the current_user has not yet rated? Current_user id is stored in the session cookie.

我如何只显示current_user尚未评级的用户? Current_user id存储在会话cookie中。

1 个解决方案

#1


1  

If you setup a an indirect association its pretty straight forward:

如果你设置一个间接关联,它非常直接:

class User < ApplicationRecord
  has_many :ratings_as_rater, class_name: "Rating", foreign_key: "rater_id"
  has_many :ratings_as_ratee, class_name: "Rating", foreign_key: "ratee_id"
  has_many :raters, through: :ratings_as_ratee,
                    source: :rater
  has_many :rated_users, through: :ratings_as_rater,
                    source: :ratee

  # class level scope to fetch users with no rating
  def self.unrated_users
    left_outer_joins(:ratings_as_rater).where(ratings: { id: nil })
  end

  # users not rated by this user
  def unrated_users
    self.class.where.not(id: self.rated_users)
  end
end

One important performance aspect is doing User.where.not(id: self.rated_users) and not User.where.not(id: self.rated_users.ids). This lets ActiveRecord construct a subselect instead of pulling out the ids from the DB into Ruby and then putting them back into a mega long query.

一个重要的性能方面是User.where.not(id:self.rated_users)而不是User.where.not(id:self.rated_users.ids)。这使得ActiveRecord可以构造一个子选择,而不是将数据库中的id拉出到Ruby中,然后将它们放回到超长查询中。

#1


1  

If you setup a an indirect association its pretty straight forward:

如果你设置一个间接关联,它非常直接:

class User < ApplicationRecord
  has_many :ratings_as_rater, class_name: "Rating", foreign_key: "rater_id"
  has_many :ratings_as_ratee, class_name: "Rating", foreign_key: "ratee_id"
  has_many :raters, through: :ratings_as_ratee,
                    source: :rater
  has_many :rated_users, through: :ratings_as_rater,
                    source: :ratee

  # class level scope to fetch users with no rating
  def self.unrated_users
    left_outer_joins(:ratings_as_rater).where(ratings: { id: nil })
  end

  # users not rated by this user
  def unrated_users
    self.class.where.not(id: self.rated_users)
  end
end

One important performance aspect is doing User.where.not(id: self.rated_users) and not User.where.not(id: self.rated_users.ids). This lets ActiveRecord construct a subselect instead of pulling out the ids from the DB into Ruby and then putting them back into a mega long query.

一个重要的性能方面是User.where.not(id:self.rated_users)而不是User.where.not(id:self.rated_users.ids)。这使得ActiveRecord可以构造一个子选择,而不是将数据库中的id拉出到Ruby中,然后将它们放回到超长查询中。