【发布时间】:2019-02-22 10:34:06
【问题描述】:
我目前正在努力重构一些向经理显示仪表板的旧代码。
用户发送了许多与订餐相关的电子邮件 - 这些电子邮件主要用于审核目的。我们跟踪每个用户发送的电子邮件数量和该预订的价值,按 email_type 分组
在 ActiveRecord 获取每封电子邮件的那一刻,使用 where 子句在 created_at 上进行过滤,rails 然后将其添加到一个数组中,然后将其输出到一个表中。这看起来效率很低,而且 Nginx 正在超时,所以我们看不到结果。
我觉得对某些组使用主要是 ActiveRecord 会使这一切变得简单得多。
我刚刚向 user.rb 添加了一个关联,如下所示 - 因为没有一个关联 (!),而且它目前没有被使用:
has_many :emails, :foreign_key => "triggered_by_id"
目前 MVC 看起来是这样的:
模型 - email.rb:
scope :sent_between, -> ( start_date, end_date ) { where("emails.created_at >= ? AND emails.created_at <= ?", start_date, end_date) }
def self.sent_today
sent_between(DateTime.now.beginning_of_day, DateTime.now.end_of_day)
end
def self.metric_hash
{
"venue_confirmation" => [0, 0],
"enquiry_confirmation" => [0, 0],
"menu_verification" => [0, 0],
"amendment_information" => [0, 0],
"amendment_confirmation" => [0, 0],
"booking_confirmation_to_venue" => [0, 0],
"released_confirmation_to_venue" => [0, 0],
"cancellation_confirmation_to_venue" => [0, 0],
"transfer" => [0, 0],
"total" => [0, 0]
}
end
def self.type_for_metrics(email)
if %w(released_confirmation_to_venue cancellation_confirmation_to_venue).include?(email.email_type)
return "transfer" if email.booking.transferred_at
end
email.email_type
end
def self.metrics(start_date, end_date)
metrics = {}
totals = metric_hash
observed_bookings = Set.new
Email.includes(:booking).select(:id, :email_type, :triggered_by_id, :booking_id).sent_between(start_date, end_date).references(:booking).select(:booking_total).find_each do |email|
email_type = type_for_metrics(email)
if totals.has_key?(email_type)
metrics[email.triggered_by_id] ||= metric_hash
metrics[email.triggered_by_id][email_type][0] += 1
metrics[email.triggered_by_id][email_type][1] += email.booking.booking_total
metrics[email.triggered_by_id]["total"][0] += 1
metrics[email.triggered_by_id]["total"][1] += email.booking.booking_total
totals[email_type][0] += 1
totals[email_type][1] += email.booking.booking_total
totals["total"][0] += 1
# Only count the each booking once for the total value
unless observed_bookings.include?(email.booking_id)
totals["total"][1] += email.booking.booking_total
observed_bookings << email.booking_id
end
end
end
results = metrics.inject({}) do |memo, row|
if row[1]["total"][0] > 0
if row[0]
user = User.find(row[0])
memo[user.name] = row[1]
else
memo["Sent Before Tracking"] = row[1]
end
memo
end
end
results["Total"] = totals
results
end
index_controller.rb:
def metrics
@start = ( params[:start] && Time.zone.parse(params[:start]) ) || DateTime.now.start_of_period
@end = ( params[:end] && Time.zone.parse(params[:end]) ) || DateTime.now.end_of_period
@email_metrics = Email.metrics(@start, @end)
end
_metrics.html.erb
<h2>Emails Sent</h2>
<table>
<thead>
<tr>
<th></th>
<th title="New Enquiry">New</th>
<th title="Menu Confirmation">Menu</th>
<th title="Operator Confirmation">Confirm</th>
<th title="Released Enquiry">Released</th>
<th title="Cancelled Booking">Cancelled</th>
<th title="Amendment Information">Amend Info</th>
<th title="Amendment Confirmation">Amend Confirm</th>
<th title="Transferred">Transfer</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<% @email_metrics.each do |key, metrics| %>
<tr>
<th rowspan="2"><%= key %></th>
<td><%= metrics["venue_confirmation"][0] %></td>
<td><%= metrics["menu_verification"][0] %></td>
<td><%= metrics["booking_confirmation_to_venue"][0] %></td>
<td><%= metrics["released_confirmation_to_venue"][0] %></td>
<td><%= metrics["cancellation_confirmation_to_venue"][0] %></td>
<td><%= metrics["amendment_information"][0] %></td>
<td><%= metrics["amendment_confirmation"][0] %></td>
<td><%= metrics["transfer"][0] %></td>
<td><%= metrics["total"][0] %></td>
</tr>
<tr>
<td><%= number_to_currency metrics["venue_confirmation"][1] %></td>
<td><%= number_to_currency metrics["menu_verification"][1] %></td>
<td><%= number_to_currency metrics["booking_confirmation_to_venue"][1] %></td>
<td><%= number_to_currency metrics["released_confirmation_to_venue"][1] %></td>
<td><%= number_to_currency metrics["cancellation_confirmation_to_venue"][1] %></td>
<td><%= number_to_currency metrics["amendment_information"][1] %></td>
<td><%= number_to_currency metrics["amendment_confirmation"][1] %></td>
<td><%= number_to_currency metrics["transfer"][1] %></td>
<td>N/A</td>
</tr>
<% end %>
</tbody>
</table>
</div>
我的感觉是我会从 User.emails.etc 开始......但我被多个 group_by 和当前存在的大量复杂数组所困。
这是它的外观截图。开发环境目前只有一个用户。
【问题讨论】:
-
在给定的时间范围内有多少电子邮件?
-
@zeitnot - 该表在生产时有 500K 行。在页面加载/范围搜索时一次调用多达 10,000 个的任何内容
-
今天我将检查这段代码并尝试找出如何优化它。
标签: mysql ruby-on-rails ruby nginx activerecord