我有一个带有Postgres数据库的Rails应用,该数据库具有一个带jsonb genres列的Artists表。

有成千上万的行。

该行中的每个类型列都有一个类似["rock", "indie", "seen live", "alternative", "indie rock"]的数组,具有不同类型。

我想做的是在所有行中输出JSON中每个流派的计数。

类似于:{"rock": 532, "power metal": 328, "indie": 862}
有没有办法有效地做到这一点?

更新...这是我目前所拥有的...

genres = Artist.all.pluck(:genres).flatten.delete_if &:empty?
output = Hash[genres.group_by {|x| x}.map {|k,v| [k,v.count]}]
final = output.sort_by{|k,v| v}.to_h

输出是哈希而不是JSON,这很好。

但是已经感觉很慢了,所以我想知道是否有更好的方法可以做到这一点。

最佳答案

如果仅使用体面的关系数据库设计,这是一项极其琐碎的任务:

class Artist < ApplicationRecord
  has_many :artist_genres
  has_many :genres, through: :artist_genres
end

class Genre < ApplicationRecord
  has_many :artist_genres
  has_many :artists, through: :artist_genres
end

class ArtistGenre < ApplicationRecord
  belongs_to :artist
  belongs_to :genre
end

然后,您可以通过以下方式获得结果:
class Genre < ApplicationRecord
  has_many :artist_genres
  has_many :genres, through: :artist_genres

  # This will instanciate a record for each row just like your average scope
  # and return a ActiveRecord::Relation object.
  def self.with_artist_counts
    self.joins(:artist_genres)
        .select('genres.name, COUNT(artist_genres.id) AS artists_count')
        .group(:id)
  end

  # This pulls the columns as raw sql results and creates a hash with the genre
  # name as keys
  def self.pluck_artist_counts
    self.connection.select_all(with_artist_counts.to_sql).inject({}) do |hash, row|
      hash.merge(row["name"] => row["artists_count"])
    end
  end
end

10-07 12:25