Запрос Rails 3 в нескольких диапазонах дат

Предположим, у нас есть несколько диапазонов дат, например:

ranges = [
          [(12.months.ago)..(8.months.ago)],
          [(7.months.ago)..(6.months.ago)],
          [(5.months.ago)..(4.months.ago)],
          [(3.months.ago)..(2.months.ago)],
          [(1.month.ago)..(15.days.ago)]
         ]

и модель Post с атрибутом :created_at.

Я хочу найти сообщения, в которых значение created_at находится в этом диапазоне, поэтому цель состоит в том, чтобы создать запрос, например:

SELECT * FROM posts WHERE created_at 
    BETWEEN '2011-04-06' AND '2011-08-06' OR
    BETWEEN '2011-09-06' AND '2011-10-06' OR
    BETWEEN '2011-11-06' AND '2011-12-06' OR
    BETWEEN '2012-01-06' AND '2012-02-06' OR
    BETWEEN '2012-02-06' AND '2012-03-23';

Если у вас есть только один диапазон, подобный этому:

range = (12.months.ago)..(8.months.ago)

мы можем сделать этот запрос:

Post.where(:created_at => range)

и запрос должен быть:

SELECT * FROM posts WHERE created_at 
    BETWEEN '2011-04-06' AND '2011-08-06';

Есть ли способ сделать этот запрос, используя такую ​​запись Post.where(:created_at => range)?

И как правильно построить этот запрос?

Спасибо


person Israel    schedule 06.04.2012    source источник


Ответы (3)


Он становится немного агрессивным с пареном, но приготовьтесь нырнуть в кроличью нору ареля.

ranges = [
          ((12.months.ago)..(8.months.ago)),
          ((7.months.ago)..(6.months.ago)),
          ((5.months.ago)..(4.months.ago)),
          ((3.months.ago)..(2.months.ago)),
          ((1.month.ago)..(15.days.ago))
         ]
table = Post.arel_table
query = ranges.inject(table) do |sum, range|
  condition = table[:created_at].in(range)
  sum.class == Arel::Table ? condition : sum.or(condition)
end

Тогда query.to_sql должен равняться

(((("sessions"."created_at" BETWEEN '2011-06-05 12:23:32.442238' AND '2011-10-05 12:23:32.442575' OR "sessions"."created_at" BETWEEN '2011-11-05 12:23:32.442772' AND '2011-12-05 12:23:32.442926') OR "sessions"."created_at" BETWEEN '2012-01-05 12:23:32.443112' AND '2012-02-05 12:23:32.443266') OR "sessions"."created_at" BETWEEN '2012-03-05 12:23:32.443449' AND '2012-04-05 12:23:32.443598') OR "sessions"."created_at" BETWEEN '2012-05-05 12:23:32.443783' AND '2012-05-21 12:23:32.443938')

И вы должны просто выполнить Post.where(query)

РЕДАКТИРОВАТЬ


Вы также можете сделать что-то вроде:

range_conditions = ranges.map{|r| table[:created_at].in(r)}
query = range_conditions.inject(range_conditions.shift, &:or)

чтобы было немного короче

person Mike Auclair    schedule 05.06.2012

Я предлагаю вам попробовать чистую строковую форму:

# e.g. querying those in (12.months.ago .. 8.months.ago) or in (7.months.ago .. 6.months.ago)
Post.where("(created_at <= #{12.months.ago} AND created_at >= #{8.months.ago} ) OR " + 
  "(created_at <= #{7.months.ago} AND created_at >= #{6.months.ago} )" )
person Siwei    schedule 06.04.2012
comment
Нам не нужно писать sql с Arel вокруг. - person Snuggs; 11.02.2015

В вашем случае я бы предложил использовать предложение mysql IN

Model.where('created_at IN (?)', ranges)
person amjad    schedule 14.10.2012