【问题标题】:How to group and count a model by a has_one association's attribute?如何通过 has_one 关联的属性对模型进行分组和计数?
【发布时间】:2017-03-24 16:53:43
【问题描述】:

我有一个工作站模型,它可以有多个设备,而每个设备都属于一个工作站。每个站都有一个地址,这是一个多态模型:

/app/models/station.rb

class Station < ApplicationRecord
  has_one :address, as: :addressable, dependent: :destroy
  has_many :devices
end

/app/models/device.rb

class Device < ApplicationRecord
  belongs_to :station
  has_one :address, through: :station
end

/app/models/address.rb

# @attr [String] city City name of address
class Address < ApplicationRecord
  belongs_to :addressable, polymorphic: true
end

现在我的图表需要两组数据:

  • 各城市的车站数量
  • 按城市划分的设备数量

有效的是按城市获取车站数量:

def stations_by_city
  # collect data
  tmp_result = Address.where(addressable_type: 'Station').group(:city).count
  # sort and return top five cities
  result = Hash[tmp_result.sort_by { |_k, v| -v }[0..4]]
  # result = {"City-1"=>17, "City-2"=>14, "City-3"=>14, "City-4"=>12, "City-5=>11} 
end

按城市对设备执行相同操作并没有按预期工作。现在我是这样做的:

def devices_by_city
  stations = Station.all.includes(:address)
  tmp_result = {}
  # for each unique city
  list_of_cities.uniq.each do |city|
   number_of_devices = 0
   # check all stations
   stations.each do |station|         
     address = station.address
     # check if station is in city
     if address.city == city
       # and add to counter
       number_of_devices += station.devices.count
     end
    end
    # append to resulting hash
    tmp_result[city] = number_of_devices
  end
  result = Hash[tmp_result.sort_by { |_k, v| -v }[0..4]]
end

def list_of_cities
  cities = []
  stations = Station.all.includes(:address)
  stations.each do |station|
    address = station.address
    cities << address.city
  end
    cities
  end
end

我有重复的数据库查找,而且通常非常难看。如何以更好的方式编写此查询?尝试了各种 [.joins, .where, .group] 组合,但没有一个有效。将through: :station 添加到设备模型在其他地方有所帮助,但并没有简化我的问题......

从答案更新

# start join from station model
tmp_result = Station.joins(:address, :devices).group(:city).count

# start join from device model
tmp_result = Device.joins(station: :address).group(:city).count

从设备模型开始加入是最快的:

Timing for old query
  0.530000   0.050000   0.580000 (  0.668664)
Timing for query starting from station model
  0.020000   0.000000   0.020000 (  0.024881)
Timing for query starting from device model
  0.010000   0.000000   0.010000 (  0.009616)

【问题讨论】:

    标签: ruby-on-rails ruby activerecord active-record-query


    【解决方案1】:

    您可以在StationAddressDevice 模型和group_by 之间选择joins,然后按城市应用count

    def devices_by_city_updated
      temp_result = Station.joins(:address, :devices).group(:city).count
      result = Hash[tmp_result.sort_by { |_k, v| -v }[0..4]]
    end
    

    此查询将执行单个数据库查找以获取所有信息。

    您也可以从 Device 模型开始加入。但是您必须加入嵌套关联才能使其正常工作:

    def self.devices_by_city_another
      tmp_result = Device.joins(station: :address).group(:city).count
      result = Hash[tmp_result.sort_by { |_k, v| -v }[0..4]]
    end
    

    您可以在the docs查看更多信息

    【讨论】:

    • 有效。我必须从站开始这个加入,因为这是连接链接。我在寻找...设备时不知何故从地址或设备开始;-)
    • @MarkTowd 我更新了答案以展示如何从Device 模型开始:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-24
    • 2021-08-09
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多