如何根据有多少关联记录匹配给定数组中的记录来订购记录列表?

时间:2015-04-17 03:05:19

标签: ruby-on-rails ruby ruby-on-rails-4

很抱歉,如果标题不清楚,我不知道如何更好地说出来。

我有一个模型“播放列表”,其中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"],则应首先列出播放列表2,因为它与@playlist共享2个用户,而播放列表3仅共享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

2 个答案:

答案 0 :(得分:1)

这将简化您的模型。您已经确定了&#34;拥有并属于许多&#34;关系,这是如何实现它。

首先,您只需要两个型号。播放列表和用户

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 = [&#34; joe&#34;,&#34; nick&#34;,&#34; bob&#34;],。users = [&#34; nick&#34 ;&#34; bob&#34;,&#34; tom&#34;]和.users = [&#34; bob&#34;,&#34; jim&#34;,&#34; rich&#34 ;],首先应列出playlist2,因为它与@playlist共享2个用户,而playlist3仅共享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

答案 1 :(得分:1)

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

或者,如果您需要访问count(*)的结果:

@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 }