如何根据 has_one 关联的属性对模型进行分组和计数?
How to group and count a model by a has_one association's attribute?
我有一个站模型,它可以有多个设备,而每个设备 belongs_to 一个站。每个站都有一个地址,这是一个多态模型:
/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)
您可以 joins
Station
、Address
和 Device
模型和 group_by
城市的结果,然后应用 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
中查看更多信息
我有一个站模型,它可以有多个设备,而每个设备 belongs_to 一个站。每个站都有一个地址,这是一个多态模型:
/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)
您可以 joins
Station
、Address
和 Device
模型和 group_by
城市的结果,然后应用 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
中查看更多信息