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 NameFull AddressPets

示例结果:

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)