Search code examples
node.jsmongodbnode-mongodb-native

MongoDB: Find items where two specified key values are equal


I have a mongoDB collection and an item in the collection looks like below:

{
"_id": "52f535b56268a019bd11cc2a",
"description": "Some description",
"entry_date": "2014-02-07T19:36:21.430Z",
"last_update": "2014-02-07T19:36:21.430Z",
"r": "samestring",
"s": "samestring"
}

Dates are ISODate objects.

This query returns items correctly

db.myCollection.find({$where : "this.entry_date < this.last_update"});

Below query returns nothing (I expect that it returns the above item):

db.myCollection.find({$where : "this.entry_date == this.last_update"});

And this query returns all items (I expected again it returns the above item):

 db.myCollection.find({$where :"this.r == this.s"});

What am I doing wrong? Thanks!!

---EDIT----

So I tried to test with a small data like below:

> db.myCollection.find({},{ _id: 0, start_date: 1, end_date: 1});

{ 
   "start_date" : ISODate("2014-02-07T19:36:21.430Z"),
   "end_date" : ISODate("2014-02-07T19:36:21.430Z") 
}
{  
   "start_date" : ISODate("2014-02-07T19:36:21.430Z"),
   "end_date" : ISODate("2014-02-07T22:39:02.114Z")
}

It didn't work for Date as you can see:

> db.myCollection.find(
    {$where: "Date(this.start_date) == Date(this.end_date)"},
    { _id: 0, start_date: 1, end_date: 1 }
  );



{
    "start_date" : ISODate("2014-02-07T19:36:21.430Z"),
    "end_date" : ISODate("2014-02-07T19:36:21.430Z") 
}
{
    "start_date" : ISODate("2014-02-07T19:36:21.430Z"),
    "end_date" : ISODate("2014-02-07T22:39:02.114Z")
}

Works for string values:

> db.myCollection.find({$where: "this.title == this.description"},{ _id: 0, title: 1 });

{ "title" : "samedescription" }

Solution

  • You have to be really careful when comparing dates in JavaScript - use valueOf() or getTime():

    > db.myCollection.find({$where: "this.start_date.getTime() == this.end_date.getTime()"});
    
    { "_id" : ObjectId("52f5b7e316d795f0a076fbdf"), "description" : "a description", "title" : "a title", "start_date" : ISODate("2014-02-07T19:36:21.430Z"), "end_date" : ISODate("2014-02-07T19:36:21.430Z") }
    

    Here is why your other queries didn't work.

    db.myCollection.find({$where: "Date(this.start_date) == Date(this.end_date)"});
    

    This didn't work because you didn't use new when initializing the dates. This generally has hilarious results with all dates being equal to each other:

    > Date(2014,2,8) == Date(1941,12,7)
    true
    > Date(2000,1,1) == Date(1995,2,8)
    true
    

    But even if you properly instantiate the date using new, you still get hilarious results when comparing dates using ==, as demonstrated in this gist:

    var dateValue = 504001800000; // Saturday, December 21st, 1985 at 3:30am EST
    var date1 = new Date(dateValue);
    var date2 = new Date(dateValue);
    
    console.log(date1 == date2);  // false (different instances)
    console.log(date1 === date2); // false (different instances)
    console.log(date1 > date2);   // false (date1 is not later than date2)
    console.log(date1 < date2);   // false (date1 is not earlier than date2)
    console.log(date1 >= date2);  // true (rofl)
    console.log(date1 <= date2);  // true (ahahahaha)
    

    As for your other query:

    It didn't work if I consider them as strings either:

    db.myCollection.find({$where: "this.start_date == this.end_date"});

    You're not actually comparing them as strings, you're comparing ISODate objects, which is how they're stored. For ISODate, similar to Date, the == operator will return false unless you're comparing the exact same instance. Using getTime should work, however, which is what I did up above.

    Hopefully, none of this makes any sense whatsoever, because if it does, I'm worried about your sanity.