2015-04-17 15 views
1

對不起,如果標題不清楚,我不知道怎麼說更好。如何基於多少個關聯記錄匹配給定數組中的記錄來對記錄列表進行排序?

我有一個模型「播放列表」has_many和belongs_to另一個模型「用戶」,通過中介模型「PlaylistUser」。

假設我在給定播放列表(@playlist)和@users = @playlist.users的頁面上。我如何列出所有其他播放列表,由他們與@playlist共享多少用戶訂購?

所以,如果@playlist.users = ["joe","nick","bob"]<playlist2>.users = ["nick","bob","tom"]<playlist 3>.users = ["bob","jim","rich"],playlist2應該首先列出,因爲它共享2個用戶@playlist,而playlist3只股票1個用戶。

我希望我做了我想要做的足夠清楚,但讓我知道是否需要額外的說明。

關聯設定:

class Playlist < ActiveRecord::Base 
    has_many :playlist_users 
    has_many :users, :through => :playlist_users 
end 

class PlaylistUser < ActiveRecord::Base 
    belongs_to :playlist 
    belongs_to :user 
end 

class User < ActiveRecord::Base 
    has_many :playlist_users 
    has_many :playlists, :through => :playlist_users 
end 

回答

1
Playlist.joins(:users) 
    .where.not(id: @playlist.id) 
    .where(users: {id: @playlist.user_ids}) 
    .group(:id) 
    .order('count(*) desc') 

或者,如果你需要訪問計數的結果(*):

@playlists = Playlist.select('playlists.*, count(*) as shared_users_count') 
    .joins(...) 
    ... 
    .order('shared_users_count desc') 

# print out shared users count 
@playlists.each { |pl| puts pl.shared_users_count } 
+0

感謝偉大的答案!如果我打印一個代碼塊:'Playlist。(...)。each do | p |',怎樣才能打印出「count(*)」的值? 'p.count(*)'返回一個錯誤。 –

+0

沒問題。查看更新的答案。 –

1

這將簡化你的模型一點點。您已經確定了「擁有並且屬於許多」關係,這是如何實施它的。

首先你只需要兩個模型。播放列表和用戶

class Playlist < ActiveRecord::Base 
    has_and_belongs_to_many :users 
end 

class User < ActiveRecord::Base 
    has_and_belongs_to_many :playlists 
end 

接下來你需要3個表格。

class SampleMigration < ActiveRecord::Migration 
    def change 
    create_table :users do |t| 
     t.string :name 
    end 

    create_table :playlists do |t| 
     t.string :name 
    end 

    create_table :playlists_users, id: false do |t| 
     t.belongs_to :playlist 
     t.belongs_to :user 
    end 
    end 
end 

需要注意的是複數是集合命名重要的,例如has_and_belongs_to_many :users併爲你的表create_table :users名。

連接表不需要一個模型,但確實需要按字母順序命名並命名爲複數形式。

現在您可以使用user.playlists返回連接到此用戶的播放列表數組。作爲一個陣列,您可以撥打user.playlists[5]以獲得列表中的第5首歌曲,或者您可以遍歷它user.playlists.each do |list|

因此,如果@ playlist.users = [「joe」,「nick」,「bob」],.users = [「nick」,「bob」,「tom」]和.users = [「bob 「,」jim「,」rich「],播放列表2應首先列出,因爲它與@playlist共享2個用戶,而播放列表3只共享1個用戶。

有兩種方法可以實現此目的。通過幫助程序方法,您可以檢索所有列表並將它們在Rails中或通過SQL查詢進行聯合。 SQL更快,但需要SQL知識。起初,我會使用Rails,並在獲得收益時處理SQl。一個實現可能是(注意:將其視爲僞代碼,我沒有測試過);

# get all the lists 
newPlaylist = array.new 
users = @playlist.users 
users.each do |user| 
    newPlaylist = newPlaylist + user.playlists 
end 

# count all the lists 
countPlaylists = Hash.new 
newPlaylist.each do |list| 
    if list.in? countPlaylists.keys 
     countPlaylists[list] = countPlaylists[list] + 1 
    else 
     countPlaylists[list] = 1 
    end 
end 

# sort the list - I'm not sure if it sorts on keys or values, 
# but either way you should be able to figure it out 
countPlaylists.sort 
相關問題