4

我想创建迷你图来说明过去 2 周在我的博客上创建的帖子数量。为此,我需要首先生成一个数组,其中包含相关期间每天创建的帖子数。

例如,这个数组:

[40, 18, 0, 2, 39, 37, 22, 25, 30, 60, 36, 5, 2, 2]

生成这个迷你图:(我在 Google Charts API周围使用 Googlecharts 包装器)

我的问题是如何创建这些数组。这是我现在正在做的事情:(我正在使用Searchlogic进行查询,但即使您从未使用过它也应该可以理解)

  history = []
  14.downto(1) do |days_ago|
    history.push(Post.created_at_after((days_ago + 1).day.ago.beginning_of_day).created_at_before((days_ago - 1).days.ago.beginning_of_day).size)
  end

这种方法既丑陋又缓慢——一定有更好的方法!

4

5 回答 5

7

这将为您提供哈希映射日期到帖子计数:

counts = Post.count(
  :conditions => ["created_at >= ?", 14.days.ago],
  :group => "DATE(created_at)"
)

然后你可以把它变成一个数组:

counts_array = []
14.downto(1) do |d|
  counts_array << (counts[d.days.ago.to_date.to_s] || 0)
end
于 2010-03-02T21:49:54.403 回答
1

您需要正确索引您的数据,否则这将永远无法有效地工作。如果您使用“天”的粒度,那么拥有一个日期列是值得的。然后,您可以使用标准 SQL GROUP BY 操作直接获取您需要的值。

例如,可以像这样进行迁移:

self.up
  add_column :posts, :created_on_date
  add_index :posts, :created_on_date

  execute "UPDATE posts SET created_on_date=created_at"
end

然后检索真的很快,因为它可以锻炼索引:

def sparkline_data
  self.class.connection.select_values("
    SELECT created_on_date, COUNT(id) FROM posts
      WHERE created_on_date>DATE_SUB(UTC_TIMESTAMP(), INTERVAL 14 DAY)
      GROUP BY created_on_date
  ").collect(&:to_i)
end

请记住,如果您可能错过一天,则必须通过在结果中插入零值来解决这一问题。此处返回日期,因此您应该能够计算缺失值并填写它们。通常这是通过使用 collect 迭代一组天来完成的。

当您需要快速检索一小块数据时,加载模型实例将始终是一个巨大的瓶颈。如果没有简单的方法来获取您需要的内容,您通常需要直接访问 SQL。

于 2010-03-02T21:36:30.680 回答
1

试试这个:

n_days_ago, today = (Date.today-days_ago), Date.today

# get the count by date from the database  
post_count_hash = Post.count(:group => "DATE(created_at)", 
             :conditions => ["created_at BETWEEN ? AND ? ", n_days_ago, today])

# now fill the missing date with 0   
(n_days_ago..today).each{ |date| post_count_hash[date.to_s] ||=0 }

post_count_hash.sort.collect{|kv| kv[0]}

注意 1:如果您在created_at此方法上添加索引应该可以很好地扩展。如果您每天遇到数百万条记录,那么您最好将每天的帖子计数存储在另一个表中。

注意 2:您可以缓存和老化结果以提高性能。在我的系统中,我通常将 TTL 设置为 10-15 分钟。

于 2010-03-02T21:41:00.747 回答
0

除了 tadman 的回答之外,如果您具有所需的管理员访问权限,您可能需要调查基于日期的分区,尤其是当您每天收到大量帖子时。

于 2010-03-02T21:40:03.137 回答
0

大部分时间花在执行 14 个数据库查询上,每个查询都需要扫描表中的每一行以检查日期(假设您没有按 created_at 进行索引)。

为了尽量减少这种情况,我们可以执行单个数据库查询来获取相关行,然后对它们进行排序。

history = []
14.times { history << 0 }
recent_posts = Post.created_at_after(14.days.ago.beginning_of_day)
recent_posts.each do |post|
  history[(Date.today - post.created_at.to_date).to_i] += 1
end

我还建议您添加一个索引,就像 tadman 推荐的那样,但在本例中添加到 posts 表中的 created_at 字段。

于 2010-03-02T21:59:46.077 回答