4

Я хочу создать искровые линии, которые иллюстрируют количество сообщений, созданных в моем блоге за последние 2 недели. Для этого мне нужно сначала создать массив, который содержит количество сообщений, созданных в каждый день в течение рассматриваемого периода.Получить массив, содержащий количество сообщений, созданных за последние 2 недели

Например, этот массив:

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

генерирует этот спарклайн: (Я использую Googlecharts wrapper around the Google Charts API)

Мой вопрос заключается в том, чтобы создать эти массивы. Вот что я делаю сейчас: (я использую 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 

Этот подход некрасиво и медленно - там должны быть лучшим способом!

ответ

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 
+0

+1 для краткости. –

+0

Часть создания массива немного уродлива, хотя, если бы не возможность 0s, вы могли бы сделать все это в одной строке: 'Post.count (: conditions => ['created_at> =? ', 14.days.ago],: group => "DATE ​​(created_at)"). Sort_by {| i | . Я [0]} карта {| я | я [1]} '. Есть ли способ получить вызов 'Post.count' для сопоставления дней до нуля, когда в этот день не было создано сообщений? –

+0

Невозможно сделать это, не сообщив базе данных о последовательности дат, которые вас интересуют, и ради простоты и скорости я не думаю, что было бы разумно идти по этому маршруту. Я пытался сохранить часть вашего кода, но вы можете очистить массив-идентификацию: 'counts_array = (1..14) .to_a.reverse.map {| d | counts [d.days.ago.to_date.to_s] || 0} '. –

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 

Имейте в виду, если вы потенциально пропуская ни день вам придется учитывать это, вставив нулевое значение в ваши результаты. Дата возвращается сюда, поэтому вы должны иметь возможность вычислить недостающие значения и заполнить их. Обычно это выполняется путем итерации в течение нескольких дней с использованием сбора.

Когда вам нужно быстро получить тонкий фрагмент данных, загрузка экземпляров моделей всегда будет огромным узким местом. Часто вам нужно перейти непосредственно к SQL, если нет простого способа получить то, что вам нужно.

0

В дополнение к тадменскому ответу, если у вас есть доступ к администратору, вы можете изучить partitioning на основе даты, особенно если вы получаете чрезвычайно большой объем сообщений в день.

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мин.

0

Большинство времени, затрачиваемого делает базу данных 14 запросы, каждый из которых должен проверять каждую строку таблицы, чтобы проверить дату (если вы не индексируете create_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 

Я также рекомендую вам добавить индекс, как Тадман рекомендуемого, но в этом случае на created_at поле в таблице сообщений.

Смежные вопросы