Rails 3:为HABTM关系编写联接声明

时间:2012-10-22 14:07:37

标签: mysql ruby-on-rails-3

我在:users和:books之间有一个has_and_belongs_to_many关系。他们加入了:owned_books模型。我的应用程序的一个功能是连接已阅读相同书籍的用户。目前,我在我的模型中无效地处理了这个问题:

  users_hash = Hash.new
  users = User.all
  users.each do |user|
    if user != current_user
      users_hash[user.id] = 0
      user.books.each do |book|
        if current_user.books.include?(book)
          users_hash[user.id]+=1
        end
      end
    end
  end
  users_hash = users_hash.sort {|a,b| b[1]<=>a[1]}
  @users = Array.new
  users_hash[0..max].each do |id|
    user = User.find(id[0])
    @users << user
  end
end

我相信有一种方法可以让数据库完成这项工作。我一直在玩一个看起来像这样的MySQL查询:

@users = User.select("users.*, COUNT(books) AS shared_books").joins("LEFT JOIN books ON ???").order("shared_books DESC").limit(100)

我的目的是加入current_user已阅读的书籍,然后计算所有其他用户的数量,以查看他们阅读过的书籍数量。然后,我将按此计数订购结果,并限制前100名结果。

不幸的是,我的MySQL技能不能满足要求。特别是,我不确定如何为连接的模型编写条件。我也怀疑select语句是否有效,但是一旦我有一个可靠的连接语句,我可能会想到这一点。

1 个答案:

答案 0 :(得分:0)

如果正确构建数据,通常可以在单个操作中获取所需内容。在您的情况下,您可能需要稍微调整一下这个结构:

class User < ActiveRecord::Base
  has_many :owned_books
  has_many :books,
    :through => :owned_books
end

class OwnedBook
  belongs_to :user
  belongs_to :book
end

class Book
  has_many :owned_books,
    :as => :owned_by
  has_many :users,
    :through => :owned_by
end

使用较新的has_many :throughhas_and_belongs_to_many方法有许多优点,主要是因为实现更新,但也因为使用关联的方式区分了您的连接模型和正在连接的模型。管理关联也更容易,因为每个OwnedBook实际上是一个可以使用id进行更改,更新或销毁的一流模型。

要在这种情况下使用通用图书获取用户,请尝试以下操作:

User.where('id IN (SELECT DISTINCT user_id FROM owned_books WHERE book_id IN (SELECT book_id FROM owned_books WHERE user_id=?))', @user.id)).all

这有点啰嗦,因为它可以避免重复用户。如果您只是使用通用书籍获取用户,则可能会为每个用户获得多个匹配项。这就是将DISTINCT条件添加到查询中的原因。