views:

348

answers:

2

I've been playing around storing tweets inside mongodb, each object looks like this:

{
"_id" : ObjectId("4c02c58de500fe1be1000005"),
"contributors" : null,
"text" : "Hello world",
"user" : {
    "following" : null,
    "followers_count" : 5,
    "utc_offset" : null,
    "location" : "",
    "profile_text_color" : "000000",
    "friends_count" : 11,
    "profile_link_color" : "0000ff",
    "verified" : false,
    "protected" : false,
    "url" : null,
    "contributors_enabled" : false,
    "created_at" : "Sun May 30 18:47:06 +0000 2010",
    "geo_enabled" : false,
    "profile_sidebar_border_color" : "87bc44",
    "statuses_count" : 13,
    "favourites_count" : 0,
    "description" : "",
    "notifications" : null,
    "profile_background_tile" : false,
    "lang" : "en",
    "id" : 149978111,
    "time_zone" : null,
    "profile_sidebar_fill_color" : "e0ff92"
},
"geo" : null,
"coordinates" : null,
"in_reply_to_user_id" : 149183152,
"place" : null,
"created_at" : "Sun May 30 20:07:35 +0000 2010",
"source" : "web",
"in_reply_to_status_id" : {
    "floatApprox" : 15061797850
},
"truncated" : false,
"favorited" : false,
"id" : {
    "floatApprox" : 15061838001
}

How would I write a query which checks the *created_at* and finds all objects between 18:47 and 19:00? Do I need to update my documents so the dates are stored in a specific format?

Thanks

+4  A: 

Querying for a Date Range (Specific Month or Day) in the MongoDB Cookbook has a very good explanation on the matter, but below is something I tried out myself and it seems to work.

items.save({
    name: "example",
    created_at: new Date(2010, 4, 30)
})
items.find({
    created_at: {
        $gte: new Date(2010, 4, 29),
        $lt: new Date(2010, 5, 1)
    }
})
=> { "_id" : ObjectId("4c0791e2b9ec877893f3363b"), "name" : "example", "created_at" : "Sun May 30 2010 00:00:00 GMT+0300 (EEST)" }

Based on my experiments you will need to serialize your dates into a format that MongoDB supports, because the following gave undesired search results.

items.save({
    name: "example",
    created_at: "Sun May 30 18.49:00 +0000 2010"
})
items.find({
    created_at: {
        $gte:"Mon May 30 18:47:00 +0000 2015",
        $lt: "Sun May 30 20:40:36 +0000 2010"
    }
})
=> { "_id" : ObjectId("4c079123b9ec877893f33638"), "name" : "example", "created_at" : "Sun May 30 18.49:00 +0000 2010" }

In the second example no results were expected, but there was still one gotten. This is because a basic string comparison is done.

ponzao
Looks interesting, but does the stored date need to be in a specific format. I've just been storing what was provided by twitter, does this need to be changed into a different format?
Tom
You've probably stored the timestamps as strings, so I am guessing MongoDB won't realize that they are in fact dates. Thus doing a range query on them would result in an alphabetical range query (e.g. "Jan Mon 01.01.2010" being before "Jan Sun 01.01.1000"). It would probably make sense to format all date data into the MongoDB format, which I think is just plain JavaScript Date.
ponzao
I just used this to convert my strings to date objects http://stackoverflow.com/questions/2900674/how-do-i-convert-a-property-in-mongodb-from-text-to-date-type
Tom
Okay cool! I'd guess the range queries mentioned in the cookbook should work then, did you try them out already?
ponzao
Yep once I was storing the dates correct the cookbook examples worked as expected.
Tom
+1  A: 

MongoDB actually stores the millis of a date as an int(64), as prescribed by http://bsonspec.org/#/specification

However, it can get pretty confusing when you retrieve dates as the client driver will instantiate a date object with its own local timezone. The JavaScript driver in the mongo console will certainly do this.

So, if you care about your timezones, then make sure you know what it's supposed to be when you get it back. This shouldn't matter so much for the queries, as it will still equate to the same int(64), regardless of what timezone your date object is in (I hope). But I'd definitely make queries with actual date objects (not strings) and let the driver do its thing.

Ben Smith
Thanks for this, it's really helpful.
Tom