Rails:处理动态列导出功能的更好方法
Rails: better approach in handling dynamic column export feature
鉴于动态导出功能,用户可以 select 他们想要包含在导出中的列,有些列是 N+1,有些需要 table 的多个连接。
例如:
表格:
Customer Table: id, first_name, address_id
Address Table: id, street, city
Pet Table: id, animal_type, customer_id ---- Customer could own multiple pets
要在 UI 中导出的列:
Customer Name
、Full Address
、Pets
示例结果:
Row 1:
John Doe | Alcala st. Zone city | cat,dog,bird
在 rails 中,我可以想到两种方法来实现这一点,第一种是使用下面的 N+1 方法:
方法一:
excel_sheet << customer.pets.pluck(:breed).join(',')
问题:Pets
列中的 N+1 个查询。
方法二:
要生成具有多个连接和子查询的单个原始复杂查询 pets
table。
问题:查询可能会 complicated/dirty 并且将来很难维护。
给出的数据库结构仅用于表示目的,实际项目由更多 table 组成。还考虑使用此 Batchloader gem https://github.com/exAspArk/batch-loader 来减少方法 1 的 N+1。
将此视为 returns 数百万条记录的导出功能。
像往常一样使用 includes
有什么问题?你可以有类似下面的东西:
# prepares the query with eager-loaded associations, but doesn't execute it
customers = Customer.includes(:address, :pets)
# `find_each` retrieves customers in batches of 1000 from the database.
# When using it with `includes`, the associations are batched as well
customers.find_each do |customer|
row = [
customer.full_name,
"#{address.street} #{city}",
pets.pluck(:breed).join(',') # this pluck is on the pre-loaded association
].join('|')
# whatever your export logic is - like a CSV stream
export(row)
end
如果您想避免 eager-loading 每个关联,您可以像这样动态构建 includes
:
includes = [:address]
includes << :pet if selected_columns.include?(:pet_breed)
customers = Customer.includes(*includes)
鉴于动态导出功能,用户可以 select 他们想要包含在导出中的列,有些列是 N+1,有些需要 table 的多个连接。
例如:
表格:
Customer Table: id, first_name, address_id
Address Table: id, street, city
Pet Table: id, animal_type, customer_id ---- Customer could own multiple pets
要在 UI 中导出的列:
Customer Name
、Full Address
、Pets
示例结果:
Row 1:
John Doe | Alcala st. Zone city | cat,dog,bird
在 rails 中,我可以想到两种方法来实现这一点,第一种是使用下面的 N+1 方法:
方法一:
excel_sheet << customer.pets.pluck(:breed).join(',')
问题:Pets
列中的 N+1 个查询。
方法二:
要生成具有多个连接和子查询的单个原始复杂查询 pets
table。
问题:查询可能会 complicated/dirty 并且将来很难维护。
给出的数据库结构仅用于表示目的,实际项目由更多 table 组成。还考虑使用此 Batchloader gem https://github.com/exAspArk/batch-loader 来减少方法 1 的 N+1。
将此视为 returns 数百万条记录的导出功能。
像往常一样使用 includes
有什么问题?你可以有类似下面的东西:
# prepares the query with eager-loaded associations, but doesn't execute it
customers = Customer.includes(:address, :pets)
# `find_each` retrieves customers in batches of 1000 from the database.
# When using it with `includes`, the associations are batched as well
customers.find_each do |customer|
row = [
customer.full_name,
"#{address.street} #{city}",
pets.pluck(:breed).join(',') # this pluck is on the pre-loaded association
].join('|')
# whatever your export logic is - like a CSV stream
export(row)
end
如果您想避免 eager-loading 每个关联,您可以像这样动态构建 includes
:
includes = [:address]
includes << :pet if selected_columns.include?(:pet_breed)
customers = Customer.includes(*includes)