#ruby #mongodb #mongoid #date #range
#ruby #mongodb #mongoid #Дата #диапазон
Вопрос:
Как мне запросить диапазон дат (скажем, за последние 30 дней) с помощью Mongoid и Ruby?
Мне нужно получить массив или хэш, подобный следующему:
{
15 => 300,
14 => 23,
13 => 23
...
30 => 20 # Goes over into previous month
28 => 2
}
В настоящее время я сохраняю каждый документ с экземпляром DateTime, а также с целочисленным полем метки времени unix.
Ключи в приведенном выше хэше — это дни, а значения — сумма всех продаж за эти дни.
Есть идеи?
Ответ №1:
Есть более простой способ:
Sale.where(created_at: (30.days.ago..Time.now))
Отрегулируйте временной диапазон в соответствии с требованиями.
Комментарии:
1. Для этого требуется ActiveSupport для части .days.ago
2. Я не думаю, что вы можете добавить несколько параметров с помощью синтаксиса хэша таким образом. Хотя это может быть неправильно.
3. @AshBlue — это один аргумент типа Range. ruby-doc.org/core-2.0.0/Range.html
4.
Range
в ruby это очень дорого . Несмотря на хороший синтаксис, я бы выбрал выполнениеgte
иlte
5. @oma — не могли бы вы дать некоторую информацию / ссылку, почему диапазоны ruby являются дорогостоящими, особенно в этом контексте (запрос mongodb)?
Ответ №2:
Вот как сделать все это в rubyland:
sales_by_date = Hash.new(0)
Sale.where(:created_at.gte => (Date.today - 30)).order_by(:created_at, :desc).each do |s|
sales_by_date[s.created_at.strftime("%m-%d")] = 1
end
Это создаст хэш с ключами «месяц-день», причина в том, что в некоторых месяцах меньше 30 дней, и это приведет к столкновению ключей, если запрос всегда равен 30.
Если вам нужен другой диапазон, измените запрос:
# Between 10 and 20 days ago
start_day = 10
end_day = 20
Sale.where(:created_at.gte => (Date.today - end_day), :created_at.lte => (Date.today - start_day))
Измените created_at
на любое имя вашего поля даты и времени.
Комментарии:
1. Если использовать
2012-06-16
вместо(Date.today - 30)
, то как написать запрос?2. @harsh4u: сначала преобразуйте строку в объект даты.
Date.parse("2012-06-16")
3. это далеко не идеальное решение, приведенные ниже ответы намного лучше.
4. Sale.where(:end_date.gte => (Дата.сегодня), :begin_date.lte => (Дата.сегодня)).order(begin_date: :desc)
Ответ №3:
Вы также можете написать запрос, используя between
метод, подобный:
Sale.between(created_at: (30.days.ago..Time.now))
Ответ №4:
Что, если вы забыли указать временные метки в своей модели? 🙁
Нет проблем! Просто используйте временную метку в BSON: ObjectId
Получить продажи за последние 30 дней.
Sale.where(:id.gte => Moped::BSON::ObjectId.from_time((Date.today - 30).to_time))
Поскольку id
поле является индексом, это вполне может быть самым быстрым запросом.
Нужен диапазон дат? Торт.
Получите продажи за последний месяц.
Sale.and(
{:id.gte => Moped::BSON::ObjectId.from_time((Date.today.prev_month.beginning_of_month).to_time)},
{:id.lte => Moped::BSON::ObjectId.from_time((Date.today.prev_month.end_of_month).to_time)}
)
Конечно, в этом примере предполагается, что помощники даты Rails…
Комментарии:
1. Я думаю, что это более лучший ответ. Это сложнее, но у него хорошая производительность. Спасибо!
Ответ №5:
Вы можете добиться этого, например, выполнив map_reduce
вызов над своей коллекцией с помощью функции map, которая выдает только соответствующие записи (те, значение даты которых больше любого условия, которое вы ему задаете).
Попробуйте что-то вроде этого:
map = "function () { emit( {this.date.getDate(), {}} )}"
reduce = "function (key, values) { return {date: key, count: values.length } }"
collection.map_reduce(map, reduce, {query: {"date": {"$gt": 30.days.ago } } })
Это может сработать.