Strange Behavior with mongoid date range query. - Rails 3 - ruby-on-rails

I am running in very strange date range query problem. When I use DateTime.now.utc option to query database, the query date for DateTime.now is different that my current DateTime.now(i.e. one that's returned on console.)
My mogoid.yml uses-
use_utc: true
I have my named scope like this -
scope :running_auctions, {
:where => { :end_time.gt => DateTime.now.utc },
:order_by => [:end_time, "ASC"]
}
And on console I can do this -
Loading development environment (Rails 3.0.7)
irb(main):001:0> Auction.running_auctions
=> #<Mongoid::Criteria
selector: {:end_time=>{"$gt"=>Fri Jul 22 00:42:38 UTC 2011}},
options: {:sort=>[:end_time, "ASC"]},
class: Auction,
embedded: false>
Notice that my date here is Fri Jul 22 00:42:38 UTC 2011
irb(main):002:0> DateTime.now
=> Fri, 22 Jul 2011 11:42:56 +0530
irb(main):003:0> DateTime.now.utc
=> Fri, 22 Jul 2011 06:12:59 +0000
Notice here that my datetime is Fri, 22 Jul 2011 06:12:59 +0000
What is making query date older than actual current date ? Does mogoid or mongodb doing caching there ?
Please let me know if I am missing something.
UPDATE
Loading development environment (Rails 3.0.7)
irb(main):001:0> Auction.running_auctions(DateTime.now.utc)
=> #<Mongoid::Criteria
selector: {:end_time=>{"$gt"=>Fri Jul 22 01:21:53 UTC 2011}},
options: {:sort=>[:end_time, "ASC"]},
class: Auction,
embedded: false>
irb(main):002:0> DateTime.now.utc
=> Fri, 22 Jul 2011 06:52:03 +0000

You have to use a lambda there. As of now, the DateTime.now.utc is computed at application startup and cached with application code.
You need to write your scope as:
scope :running_auctions, lambda {
{
:where => { :end_time.gt => DateTime.now.utc },
:order_by => [:end_time, "ASC"]
}
}

Related

Rails Time.zone.name is not the same Time.now.getlocal

In rails console if I run the following commands I get:
>> Time.zone.name
=> "UTC"
>> Time.now.getlocal
=> 2015-08-06 16:34:57 -0400
I'm in NYC and the utc offset is -4 hours so the getlocal seems to get the system time zone rather than the environment time zone. This becomes an issue when using the String to_time method:
>> s = "2015-08-06 16:34:57"
=> "2015-08-06 16:34:57"
>> s.to_time(:utc)
=> 2015-08-06 16:34:57 UTC
>> s.to_time(:local)
=> 2015-08-06 16:34:57 -0400
>> s.to_time
=> 2015-08-06 16:34:57 -0400
Any ideas why? Can I force it to look at the environment time zone rather than the system time zone?
My main motivation is that my tests are run locally (so system time is in NYC), but my app is hosted on heroku which has UTC server time, so this caused an issue where a bug slipped through my tests and into production =/
You can do the following:
"2015-08-06 16:23:25".to_time.in_time_zone
The default behavior of to_time is to use :local according to the API doc (either :local or :utc are valid). For whatever reason, using in_time_zone appears to respect your configured timezone.
To expand on your above posted examples (from my machine):
» Time.zone.name
=> "UTC"
» Time.now
=> "2015-08-06T13:49:05.195-07:00"
» Time.zone.now
=> Thu, 06 Aug 2015 20:49:17 UTC +00:00
» time_string = "2015-08-06 16:34:57"
=> "2015-08-06 16:34:57"
» time_string.to_time
=> "2015-08-06T16:34:57.000-07:00"
» time_string.to_time.in_time_zone
=> Thu, 06 Aug 2015 23:34:57 UTC +00:00
EDIT: Based on the comment below, you can also do the following to not have any time shift added:
» DateTime.parse(time_string)
=> Thu, 06 Aug 2015 16:34:57 +0000
This will use the configured timezone (UTC in my case)

Querying for last 7 or 30 days date range with mongodb mongoid rails

I am using Mongodb 2.4.8, Mongoid 4.0.2 and Rails 4.2.1
I have been trying to get a date range query ran through mongoid to return the right set of results on a test data with mongodb. I created just 6 documents spanning over 5 - 7 days period. For instance, the query below, ought to return 3 records for the days between 22nd - 27th, instead it returns one record :
Person.where(:person_email_clicks.elem_match => {:date.gte => 'Wed, 22 Apr 2015 22:12:00 +0000'}, :person_email_clicks.elem_match => {:date.lte => 'Mon, 27 Apr 2015 22:12:00 +0000'})
This is the entire documents for test purposes:
{"_id"=>BSON::ObjectId('55364a416461760bf1000000'),
"status"=>"premium"
"person_email_clicks"=>[
{"_id"=>BSON::ObjectId('55381d256461760b6c000000'), "name"=>"buyer", "date"=>2015-04-22 22:12:00 UTC},
{"_id"=>BSON::ObjectId('55381d536461760b6c010000'), "name"=>"seller", "date"=>2015-04-23 01:12:00 UTC},
{"_id"=>BSON::ObjectId('55381d916461760b6c020000'), "name"=>"giver", "date"=>2015-04-24 22:12:00 UTC},
{"_id"=>BSON::ObjectId('55381dac6461760b6c030000'), "name"=>"help", "date"=>2015-04-27 22:12:00 UTC},
{"_id"=>BSON::ObjectId('553824ba6461760b6c040000'), "name"=>"tt", "date"=>2015-04-22 22:12:00 UTC},
{"_id"=>BSON::ObjectId('553824bf6461760b6c050000'), "name"=>"tt", "date"=>2015-04-22 22:12:00 UTC},
{"_id"=>BSON::ObjectId('55382ad46461760b6c060000'), "name"=>"yyy", "date"=>2015-04-25 22:12:00 UTC}
]
}
Here are the models:
class Person
include Mongoid::Document
field :status, type: String
embeds_many :person_email_clicks
end
class PersonEmailClick
include Mongoid::Document
field :name, type: String
field :date, type: DateTime
embedded_in :person
end
These are the queries, I have tried so far and they all return 1 record instead of 3 records between the dates 22nd and 27th
u = Person.first.person_email_clicks.first.date
We store the datetime which is Wed, 22 Apr 2015 22:12:00 +0000
e = Person.first.person_email_clicks.last.date
We store the the 2nd date which is Mon, 27 Apr 2015 22:12:00 +0000
Now I use those variables holding the dates in my query and all 3 query below returns one record instead of 3:
f = Person.where(:person_email_clicks.elem_match => {:date.gte => u}, :person_email_clicks.elem_match => {:date.lte => e})
f = Person.where(:person_email_clicks.elem_match => {:date => {"lte" => u}}, :person_email_clicks.elem_match => {:date => {"gte" => e}})
t = Person.where('person_email_clicks.date' => u..e)
Any suggestions on how to tweak this to return the 3 records between the date range of 22nd - 27th ?
This has finally been resolved. I needed to stored the date in a variable and then query it this way:
Person.first.person_email_clicks.gt(date: u).lt(date: e).
or query it this way:
Person.first.person_email_clicks.where({:date.gt => u, :date.lt => e})
This is the full step to get the desired results:
u = Person.first.person_email_clicks.first.date
=> Wed, 22 Apr 2015 22:12:00 +0000
e = Person.first.person_email_clicks.last.date
=> Sat, 25 Apr 2015 22:12:00 +0000
h = Person.first.person_email_clicks.gt(date: u).lt(date: e).to_a
Which returned 2 records as shown below:
=> [
#<PersonEmailClick _id: 55381d536461760b6c010000, name: "seller", date: 2015-04-23 01:12:00 UTC, daily_total: 0, email_open: 0, page_views: 0, clicks: 0, visits: 0>,
#<PersonEmailClick _id: 55381d916461760b6c020000, name: "giver", date: 2015-04-24 22:12:00 UTC, daily_total: 0, email_open: 0, page_views: 0, clicks: 0, visits: 0>
]

What's wrong with Rails's Date

I ran into this non-sense problem this morning in Rails 3.2 console. I'm under MacOS 10.10, my timezone is +7.
Loading development environment (Rails 3.2.12)
irb(main):001:0> Date.today
=> Sun, 16 Nov 2014
irb(main):002:0> Date.yesterday
=> Fri, 14 Nov 2014
irb(main):003:0>
Everything is fine with original Ruby Date:
irb(main):006:0> Date.today
=> #<Date: 2014-11-16 ((2456978j,0s,0n),+0s,2299161j)>
irb(main):007:0> Date.today.prev_day
=> #<Date: 2014-11-15 ((2456977j,0s,0n),+0s,2299161j)>
irb(main):008:0>
From the bug report here: https://rails.lighthouseapp.com/projects/8994/tickets/6410#ticket-6410-8
This is a subtle one - Date.yesterday uses Date.current which will use the time zone whereas Date.today doesn't. If you set your time zone to one where it's tomorrow already (e.g. Europe/Berlin as I type this) then you can get Date.today == Date.yesterday:
Time.zone = "Europe/London"
=> "Europe/London"
Date.today == Date.yesterday
=> false
Time.zone = "Europe/Berlin"
=> "Europe/Berlin"
Date.today == Date.yesterday
=> true

Comparison between two ActiveSupport::TimeWithZone objects fails

In my test suite, I have a failing test.
expected[0]['date'] comes from SomeModel.first.created_at
In a debugging console, I have the following:
> expected[0]['date']
=> Tue, 25 Mar 2014 16:01:45 UTC +00:00
> res[0]['date']
=> Tue, 25 Mar 2014 16:01:45 UTC +00:00
> res[0]['date'] == expected[0]['date']
=> false # wtf
> res[0]['date'].class
=> ActiveSupport::TimeWithZone
> expected[0]['date'].class
=> ActiveSupport::TimeWithZone
>
How is this possible ?
I've tried to reproduce this problem (I tought maybe the == operator on TimeWithZone checks the reference, or something like this, but no...) :
> t1 = Time.zone.at(0)
=> Thu, 01 Jan 1970 00:00:00 UTC +00:00
> t2 = Time.zone.parse(t1.to_s)
=> Thu, 01 Jan 1970 00:00:00 UTC +00:00
> t1 == t2
=> true
> t1.class
=> ActiveSupport::TimeWithZone
> t2.class
=> ActiveSupport::TimeWithZone
Edit: More tests...
> res[0]['date'].eql?(expected[0]['date'])
=> false
> res[0]['date'].zone
=> "UTC"
> expected[0]['date'].zone
=> "UTC"
> expected[0]['date'].getlocal
=> 2014-03-25 16:01:45 +0000
> res[0]['date'].getlocal
=> 2014-03-25 16:01:45 +0000
> res[0]['date'].hash
=> -3455877575500291788
> expected[0]['date'].hash
=> -3819233736262144515
>
> t1.hash
=> 2279159074362527997
> t2.hash
=> 2279159074362527997
# inspect...
> expected[0]['date'].inspect
=> "Tue, 25 Mar 2014 16:39:01 UTC +00:00"
> res[0]['date'].inspect
=> "Tue, 25 Mar 2014 16:39:01 UTC +00:00"
Looks like the comparison is based on the hash object. Why res and expected have different hashes ?
Answer #1 rake db:test:prepare
First, attempt dropping the test database and recreating it, and then running rake db:test:prepare. This has resolved this issue for me in the past I know this is a bit of a lame answer, but it is worth a shot.
Answer #2 Spring + Rspec + Shoulda matchers
If having this issue after installing Spring, please checkout this Github Thread, which can cause tests to fail:
https://github.com/rails/spring/issues/209
This issue only started occurring for me after adding Spring to my project.
Adding gem 'shoulda', require: false and manually adding require 'shoulda/matchers' to my spec_helper.rb resolved the issues
Answer #3 Timecop
If still having issues, checkout the Timecop gem and freeze time around date comparisons.
https://github.com/travisjeffery/timecop

Scope Chaining in Mongoid 2.2 with Rails 3.0.9 and Ruby 1.8.7

Hi I have application running on Ruby 1.8.7 (i am presently not in a position to update this to 1.9.2) and Rails 3.0.9 running mongoid 2.2.2.
I have a couple of named scopes which I have defined as follows
scope :for_pickups, where(:state => "accepted")
scope :confirmed_shipments, where(:state => "confirmed", :created_at.lt => Date.today - 1.day).desc(:created_at).limit(10)
scope :undelivered_shipments, Proc.new{ |start_date_utc, end_date_utc|
start_date_utc = (Date.today - 7.days).to_time.utc if !start_date_utc.present?
end_date_utc = Date.today.to_time.utc if !end_date_utc.present?
any_of({:created_at.gte => start_date_utc, :created_at.lte => end_date_utc},
{:pickup_date.gte => start_date_utc, :pickup_date.lte => end_date_utc},
{:desired_arrival_date.gte => start_date_utc, :desired_arrival_date.lte => end_date_utc}).
where(:state.in => ["accepted"], :state.nin => ["delivered"]).without(:carrier_digest, :carrier_label_image_base64, :carrier_label_html_base64)
}
scope :search, Proc.new{|search_params|
regexp = Regexp.new(/.*#{search_params.strip}.*/i, true)
where(:golfer_name => regexp)
}
This seems to work well and return the expected values when I use them on their own but when I use scope chaining it gets messed up.
For instance when I run Model.undelivered_shipments.search("sid") i get an array which has :ids as the first parameter
[:ids, #[{:created_at=>{"$gte"=>Thu Sep 27 18:30:00 UTC 2012, "$lte"=>Thu Oct 04 18:30:00 UTC 2012}}, {:pickup_date=>{"$gte"=>Thu Sep 27 18:30:00 UTC 2012, "$lte"=>Thu Oct 04 18:30:00 UTC 2012}}, {:desired_arrival_date=>{"$gte"=>Thu Sep 27 18:30:00 UTC 2012, "$lte"=>Thu Oct 04 18:30:00 UTC 2012}}], :_id=>"sid", :state=>{"$nin"=>["delivered"], "$in"=>["accepted"]}},
options: {:fields=>{:carrier_digest=>0, :carrier_label_html_base64=>0, :carrier_label_image_base64=>0}},
class: Shipment,
embedded: false>
]
Not sure why it returns this and why mongoid criteria defines :_id => 'sid' However when i run the search before the undelivered_shipments method it generates the criteria correctly.
It works if run it with other scopes like for_pickups.search etc but not with the undelivered shipments. I understand I am missing something but I've been through the docs and I can't find anything that really helps. I would really appreciate any help on this
Thanks in advance.

Resources