MongoMapper: finding all documents created on a specified date - ruby

I need to write a query that finds all documents created on a specified date.
Let's suppose that the date is today.
I tried this:
Document.all(:created_at => Date.parse(Time.now.strftime('%Y/%m/%d')))
but I got:
Cannot serialize an object of class Date into BSON.
Thanks for your help.
UPDATE
This link explains how to do so Date Range Queries With MongoMapper.
Document.count( :created_at => { '$gt' => 2.days.ago.midnight, '$lt' => 1.day.ago.midnight } )

UPDATE: This link explains how to do so Date Range Queries With MongoMapper.
Document.count( :created_at => { '$gt' => 2.days.ago.midnight, '$lt' => 1.day.ago.midnight } )

Your :created_at is a Date (as in "JavaScript-style Date with both date and time-of-day components"), right? You'll need to figure out the boundaries of the date in question in UTC, build Time instances for those boundaries, and then search for everything between those times.
Assuming that your local time zone is correctly configured and you want everything that was created on 2011-11-21, then something like this should get you there:
start_time = Time.new(2011,11,21, 0,0,0).utc
end_time = Time.new(2011,11,22, 0,0,0).utc
docs = Document.where(:created_at => { :$gte => start_time }).
where(:created_at => { :$lt => end_time })
You could also use Time.new(2011, 11, 21).utc and Time.new(2011, 11, 22).utc but I like the extra zeros as a reminder that I'm not really working with just a date.

Just use Time.now instead. The ruby driver knows how to deal with Time objects.
doc = {:created_at => Time.now}
or
doc = {:created_at => Time.utc(12,1,12)} # year, month, day
Then, you can test if the document will serialize without throwing an error in irb like this:
require 'bson'
BSON.serialize(doc)
If you get an error, try again. If it spits out a serialized bson object, you are good to go!
If you are curious, check out the source of the to_mongo method in the mongomapper driver here

Use the technique #mu-is-to-short pointed out to create the date.
But the format of the where statement can be simpler:
date = Time.new(2021,1,24, 0,0,0).utc
docs = Document.where(:updated_at.gte => date).all

Related

Bulk Insert into Mongo - Ruby

I am new to Ruby and Mongo and am working with twitter data. I'm using Ruby 1.9.3 and Mongo gems.
I am querying bulk data out of Mongo, filtering out some documents, processing the remaining documents (inserting new fields) and then writing new documents into Mongo.
The code below is working but runs relatively slow as I loop through using .each and then insert new documents into Mongo one at a time.
My Question: How can this be structured to process and insert in bulk?
cursor = raw.find({'user.screen_name' => users[cur], 'entities.urls' => []},{:fields => params})
cursor.each do |r|
if r['lang'] == "en"
score = r['retweet_count'] + r['favorite_count']
timestamp = Time.now.strftime("%d/%m/%Y %H:%M")
#Commit to Mongo
#document = {:id => r['id'],
:id_str => r['id_str'],
:retweet_count => r['retweet_count'],
:favorite_count => r['favorite_count'],
:score => score,
:created_at => r['created_at'],
:timestamp => timestamp,
:user => [{:id => r['user']['id'],
:id_str => r['user']['id_str'],
:screen_name => r['user']['screen_name'],
}
]
}
#collection.save(#document)
end #end.if
end #end.each
Any help is greatly appreciated.
In your case there is no way to make this much faster. One thing you could do is retrieve the documents in bulks, processing them and the reinserting them in bulks, but it would still be slow.
To speed this up you need to do all the processing server side, where the data already exist.
You should either use the aggregate framework of mongodb if the result document does not exceed 16mb or for more flexibility but slower execution (much faster than the potential your solution has) you can use the MapReduce framework of mongodb
What exactly are you doing? Why not going pure ruby or pure mongo (well that's ruby too) ? and Why do you really need to load every single attribute?
What I've understood from your code is you actually create a completely new document, and I think that's wrong.
You can do that with this in ruby side:
cursor = YourModel.find(params)
cursor.each do |r|
if r.lang == "en"
r.score = r.retweet_count + r.favorite_count
r.timestamp = Time.now.strftime("%d/%m/%Y %H:%M")
r.save
end #end.if
end #end.each
And ofcourse you can import include Mongoid::Timestamps in your model and it handles your created_at, and updated_at attribute (it creates them itself)
in mongoid it's a little harder
first you get your collection with use my_db then the next code will generate what you want
db.models.find({something: your_param}).forEach(function(doc){
doc.score = doc.retweet_count + doc.favorite_count
doc.timestamp = new Timestamp()
db.models.save(doc)
}
);
I don't know what was your parameters, but it's easy to create them, and also mongoid really do lazy loading, so if you don't try to use an attribute, it won't load that. You can actually save a lot of time not using every attribute.
And these methods, change the existing document, and won't create another one.

How to extract Mongoid documents based on a field value in the first or last embedded document?

I wish to find Order documents based on a field in the last embedded Notificationdocument.
In the example below I wish to find all pending orders that has one or more embedded notifications, and where the last notification has a datetime that is between 5 and 10 days old.
My suggestion here dosen't seem to do the trick...:
Order.where(status: 'pending').gte('notifications.last.datetime' => 5.days.ago).lte('notifications.last.datetime' => 10.days.ago)
Here are the two models:
class Order
include Mongoid::Document
field :datetime, type: DateTime
field :status, type: String, default: 'pending'
embeds_many :notifications, :inverse_of => :order
end
class Notification
include Mongoid::Document
field :datetime, type: DateTime
embedded_in :order, :inverse_of => :notifications
end
The main issue of the question seems to be how to refer to the LAST element of an array in the query.
Unfortunately, it is impossible as of MongoDB 2.4.
The simplest way to implement this feature is to use negative value to point to an element in an array like 'notifications.-1.datetime', but it doesn't work. (Refer to [#SERVER-5565] Handle negative array offsets consistently - MongoDB.)
To make matters worse, it also seems impossible to solve this using Aggregation Framework. There is no way to
add an array index to each element when $unwinding ([#SERVER-4588] aggregation: add option to $unwind to emit array index - MongoDB) or
select the index of an array dynamically when $projecting. ([#SERVER-4589] aggregation: need an array indexing operator - MongoDB)
Therefore, the only option you have seem to change the schema to match what you want. The simplest way is to add to Order one more field which contains datetime of the last Notification.
Update:
You can first get all candidates from the server, and then narrow down them on the client side to get the final result set. This involves no schema change. If the scale of database is relatively small or some degradation of performance is acceptable, this might be the best solution.
query = Order.where(status: 'pending').elem_match(
notifications: { datetime: { '$gte' => 10.days.ago, '$lte' => 5.days.ago } })
query.select do |order|
# datetime = order.notifications[0].datetime
datetime = order.notifications[order.notifications.size - 1].datetime
10.days.ago <= datetime && datetime <= 5.days.ago
end.each do |order|
p order # result
end
I know it comes a little late, but hey, better later than never. :P
You can use JavaScript in where:
Order.where("this.notifications[this.notifications.length - 1].datetime > new Date('#{5.days.ago}')")
Just found out that and was a huge relief having not to change my models. Hope that helps!

date function error in rails 3

I am trying to add a date conditional to my controller index action:
#events = Event.where("date" => Date.today.to_s).order("date").page(params[:page]).per_page(5)
I am trying to make my view only show events that have a date value greater than or equal to today's date. For example if an event has a date value of 2013-05-13 it should not be shown because that event has already happened and only events with a date value of today's date or later should be shown.
The problem is, the index view isn't returning any events and I have created an with a date value of 2013-05-30 which means it should work. Any ideas?
Thanks in advance
This is actually going to return everything where the date equals today:
Event.where("date" => Date.today.to_s)
What you probably want instead is:
Event.where("date >= ?", Date.today)
ActiveRecord will interpolate the date into the ?. Also, you don't need to call to_s on it, ActiveRecord will the date for you.
Try this:
In your model use a scope like so:
scope :recent_event, lambda { where('date >= ?', Date.today ) }
Then in your controller write
#events = Event.recent_event.order("date").page(params[:page]).per_page(5)

Rails ActiveRecord Find / Search by Date

I am trying to find records by 'created_at' date - the database column type is 'datetime' and
I am using the UI DatePicker from jQuery
my url look like this: "localhost:3000/users_supported?selected_date=2012-10-31"
So far i am doing good :) and my query in controller looks like this:
#support_histories = current_agent.support_histories.where(:created_at => Date.parse(params[:selected_date]))
How to properly extract records by 'date' only from the 'datetime' db column
I tried doing this in Rails Console, but no luck there:
sh = SupportHistory.where(:created_at => DateTime.parse('2012-10-31'))
sh = SupportHistory.where(:created_at => Date.parse('2012-10-31'))
sh = SupportHistory.where(:created_at => DateTime.strptime('2012-10-31', '%Y-%m-%d'))
I got records if i do like mentioned below, but that's not useful to me as i am trying to find record by 'date' not by 'DateTime'
sh = SupportHistory.where(:created_at => '2012-10-31 19:49:57')
selected_date = Date.parse(params[:selected_date])
# This will look for records on the given date between 00:00:00 and 23:59:59
sh = SupportHistory.where(
:created_at => selected_date.beginning_of_day..selected_date.end_of_day)
Time Zones may be a concern you need to look into, but this should work if all your times are in the same time zone.
A simple solution I use sometimes is to cast the date(time) field as text on the database rather than parse the string into date on application side. For your case that would be:
where('CAST(created_at AS text) LIKE ?', params[:selected_date])
Might not be the most effective on the database (depending on the context you use it in) but saves a lot of pita on the application side.
I solved this problem by creating a method in model like below,
Say, my model is ticket.rb
def created_date
self.created_at.to_date
end
then I queried like this,
selected_date = Date.parse(params[:date])
Ticket.all.map{|t| t if t.created_date == selected_date}.compact
This gives me accurate results that matches with the chosen date by the user.
Your current query doesn't work because you are querying a date on a datetime column. Those are two different data types.
For a datetime column, you'll need a datetime filter. You can do this by passing a DateTime object to the where clause. However, in your case you want records for the entire day so you'll specify a range between 00:00:00 and 23:59:59.
Before Rails 5.1:
SupportHistory.where(created_at: date.beginning_of_day..date.end_of_day)
Rails 5.1 and onwards: (this will generate the exact same range using #all_day)
SupportHistory.where(created_at: date.all_day)
If you want to parse a specific datetime
SupportHistory.where("created_at>=?", DateTime.parse('10 Dec 2021 11:54:00 PST -08:00'))

Find documents including element in Array field with mongomapper?

I am new to mongodb/mongomapper and can't find an answer to this.
I have a mongomapper class with the following fields
key :author_id, Integer
key :partecipant_ids, Array
Let's say I have a "record" with the following attributes:
{ :author_id => 10, :partecipant_ids => [10,15,201] }
I want to retrieve all the objects where the partecipant with id 15 is involved.
I did not find any mention in the documentation.
The strange thing is that previously I was doing this query
MessageThread.where :partecipant_ids => [15]
which worked, but after (maybe) some change in the gem/mongodb version it stopped working.
Unfortunately I don't know which version of mongodb and mongomapper I was using before.
In the current versions of MongoMapper, this will work:
MessageThread.where(:partecipant_ids => 15)
And this should work as well...
MessageThread.where(:partecipant_ids => [15])
...because plucky autoexpands that to:
MessageThread.where(:partecipant_ids => { :$in => [15] })
(see https://github.com/jnunemaker/plucky/blob/master/lib/plucky/criteria_hash.rb#L121)
I'd say take a look at your data and try out queries in the Mongo console to make sure you have a working query. MongoDB queries translate directly to MM queries except for the above (and a few other minor) caveats. See http://www.mongodb.org/display/DOCS/Querying

Resources