2012-10-22 26 views
1

我在:users和:books之间有一个has_and_belongs_to_many关系。他们加入了:owned_books模型。我的应用程序的一个功能是连接阅读相同书籍的用户。目前,我在我的模型中效率低下:Rails 3:为HABTM关系编写联接声明

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

作为说明,通常使用'{}'来代替'Hash.new','']'优先于'Array.new'。调用构造函数仅用于定制哈希或数组如何操作。 – tadman

回答

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条件添加到查询中的原因。

+0

这似乎工作,虽然没有命令用户。我的意图是通过与current_user共享多少本书来订购用户。我尝试重做你的代码来做这样的事情:User.select('users。*,COUNT(owned_books WHERE book_id IN(SELECT book_id FROM owned_books WHERE user_id =?)AS shared_books)',current_user.id).order(“shared_books DESC“)。limit(100) – nullnullnull

+0

不幸的是,它抛出了一个错误:错误的参数数量(2代表1) – nullnullnull

+0

'select'方法只接受一个参数,所以除非您在查询中执行'sanitize_sql' 。如果您创建一个接受'user_id'参数并返回结果的类方法,您将可以轻松访问该方法。 – tadman