1

我在 :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 语句是否会起作用,尽管一旦我有一个可靠的 join 语句,我可能会弄清楚这一点。

4

1 回答 1

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 :through的方法有许多优点has_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条件添加到查询中的原因。

于 2012-10-22T14:30:28.973 回答