Meteor db.find with Date inside a json as selector not working

I read thru a lot of resources in the internet and followed a lot of examples on how to find a collection within a specific date range and it didn’t work. My code is as below:-

var fromAge = 18;
var toAge = 22;
Meteor.users.find({:profile.birthday":{$lt: new Date(new Date().setYear(new Date().getFullYear() - fromAge)), $gt: new Date(new Date().setYear(new Date().getFullYear() - toAge))}).fetch();

The above query return an empty collection when I do a simple Meteor.users.find().fetch() it did returned one record below:-

{"_id": 123452345, "profile.birthday": ISODATE("1975-10-22T00:00:00Z")}

to debug, I also write the following query on both javascript console as well as backend server mongo shell and it did return empty collection as well.

Javascript Console

Meteor.users.find({"profile.birthday": {$lt: new Date()}})

Server MongoDB Shell

db.users.find{"profile.birthday": {$lt: new Date()}})

I have no clue now. It would be great if somebody can enlighten me.

new Date(...) returns an object. If your collection contains ISO dates, then perhaps you want:

Meteor.users.find({"profile.birthday":{$lt: (new Date(new Date().setYear(new Date().getFullYear() - fromAge))).toISOString(), $gt: (new Date(new Date().setYear(new Date().getFullYear() - toAge))).toISOString()}).fetch();

Also, check your logic. It seems that you’re asking for dates less than an older date and greater than a newer date … but I may have misread.

Edit: Also, may be worth checking out moment - it makes date checking easier!

regarding dates I get used to momentjs, so using moment().toISOString() everywhere
and also I think your toAge and fromAge are switched in that $lt and $gt or I get them wrong

I tried the below query and it did return nothing

db.users.find({"profile.birthday":{$lt: new Date(new Date().setYear(new Date().getFullYear() - 18)).toISOString()}})

I typed in the command in javascript console

new Date(new Date().setYear(new Date().getFullYear() - 18)).toISOString()

And it did tell me

"1997-11-06T16:35:28.844Z"

And it my database, the record the birthday should be “1975-10-22T00:00:00Z” and thus it should hit this record, but all I can have is an empty collection. Anyone have an idea?

Someone tells me to use ISODate(), I tried the below command it did works in MongoDB Shell, while it yields ISODate Undefined in my Meteor helper function.

db.users.find({"profile.birthday":{$lt: ISODate("1977-11-06")})

Have read thru some other resources http://stackoverflow.com/questions/21286599/inserting-and-querying-date-with-mongodb-and-nodejs/21286896#21286896 and learnt that I should use Date instead of ISODate whereby ISODate is internal to MongoDB and Meteor will help to do the Date to ISODate Conversion. I then tried to hard code the query and it did works fine:-

db.users.find({"profile.birthday": {$lt: new Date("1977-11-06T00:00:00.000Z")}})

UPDATE BEGIN (16 Nov 2015) –

It works fine, while my problem is I need to build the selector in json and then put it into the find while this wont work:-

selector={"profile.birthday": {$lt: new Date("1977-11-06T00:00:00:000Z"}};
db.users.find(selector);

The above query returned nothing at all. Any way I can put the date into a separate json variable before putting the json variable as the selector of find would work with date?

UPDATE END (16 Nov 2015) –

I am doing that because I have a dropdown of age group and am trying to build the query thru a helper event. I have a dropdown template below:-

/client/ageRangeDropdown.html

<template name="AgeRangeDropdown">
  <div class="btn-group">
    Age Group
    <select id="ageRangeDropdownList" class="form-control">
      <option >Whatever</option>
      <option >18-22</option>
      <option >23-27</option>
      <option >28-32</option>
      <option >33-37</option>
      <option >38-42</option>
      <option >43-47</option>
      <option >48-52</option>
      <option >53-57</option>
    </select> 
  </div>
  {{#each currentCriterias}}
    <span class="label label-info">{{criteria}}</span>
  {{/each}}
  {{#each myUser}}
    {{profile.firstName}} ,  {{profile.lastName}}, {{profile.birthday}}
  {{/each}}
</template>

And the helper below

/client/ageRangeDropdown.js

Template.ageRangeDropdown.events({
  'change ageRangeDropdownList': function(e) {
  for (var key in target) {
    if (!target.hasOwnProperty(key)) {
      if (key=='id') {
        id = target[key];
      }
    }  
  }
  var value = target.options[target.selectedIndex].value;
  if (value!='Whatever') {
    currentCriterias.push({id: "profile.birthday", criteriaValue: value});
  }
  var query = [];
  for (var i = 0; i < items.length; i++) {
    var itemObject = {};
    var fromAge = currentCriterias[i]['criteriaValue'].substr(0,2);
    var toAge = currentCriterias[i]['criteriaValue'].substr(3,2);
      itemObject['profile.birthday'] = {
        $gt: new Date(new Date().setYear(new Date().getFullYear() - fromAge)),
        $lt: new Date(new Date().setYear(new Date().getFullYear() - toAge))
      }
    query.push(itemObject);
  }
  var selector  = [];
  selector.push({$or: query});
  Template.instance().selector.set({$or: selector});

  return false;
})

Templage.ageRangeDropdown.created = function() {
  var instance = this;
  instance selector = new RactiveVar([]);

  instance.autorun(function() {
	var selector = instance.selector.get();
	if (typeof selector != 'undefined' && Object.keys(selector).length > 0) {
		var subscription = instance.subscribe('searchUser', selector);
	}
  });

instance.myUser = function() {
    var selector = instance.selector.get();
    if (typeof selector != 'undefined' && Object.keys(selector).length > 0) {
      return Meteor.users.find(selector, {fields: {profile: 1}});
    }
  }
}

server/serverpublish.js

Meteor.publish("searchUser", function(selector){
  if (!this.userId) {
      console.log("Cannot search User when you haven't logged in.");
      return null;
  } else {
      if (typeof selector != 'undefined' || Object.keys(selector).length > 0) {
        var user=Meteor.users.find(selector, {fields: {profile: 1}});
        return user;
      } else {
        console.log("won't publish all user profiile when no selector is given");
        return null;
      }
    }
});

The logic is like that, actually I have a lot of dropdown each of which will constitue part of the query, and I setup the query using instance reactive variable so that whenever it changed, meteor will do a meteor.find again. Every dropdown works fine except this birthday which is a date. Anyone have idea what’s going wrong?

Thank you robfallows and shocks, I have swapped the date and tried the below code snippet (using toISODate) and it didn’t work.

var fromAge = 18;
var toAge = 22;
var fromDate = new Date().setYear(new Date().getFullYear() - fromAge);
var toDate = new Date().setYear(new Date().getFullYear() - toAge);
query = {
  $lte: new Date(fromDate).toISOString(),
  $gte: new Date(toDate).toISOString()
};
Meteor.users.find(query);

What have I done wrong?

Well, you haven’t defined the field the query is operating on, like:

query = {
  date: {
    $lte: new Date(fromDate).toISOString(),
    $gte: new Date(toDate).toISOString()
  }
};

(Assuming your collection has a field called date.) That field must also be stored as an ISO string if that’s what you’re testing against.

My Bad, I have missed the important part in my question. Actually the code is:-

var fromAge = 18;
var toAge = 22;
var myDate = 'myProfile.birthday';
var fromDate = new Date().setYear(new Date().getFullYear() - fromAge);
var toDate = new Date().setYear(new Date().getFullYear() - toAge);
query[myDate] = {
  $lte: new Date(fromDate).toISOString(),
  $gte: new Date(toDate).toISOString()
}; 
Meteor.users.find(query);
console.log(JSON.stringify(query));

And when I check the console the query object looks like:-

{"myProfile.birthday":{"$lte":"1997-12-21T14:57:55.396Z","$gte":"1993-12-21T14:57:55.396Z"}}

it won’t work even I copy and past the code in meteor mongo

db.users.find({"myProfile.birthday":{"$lte":"1997-12-21T14:57:55.396Z","$gte":"1993-12-21T14:57:55.396Z"}})

I do a db.users.find and found that the representation of the date object is in ISODate. It looks like

{"myProfile": {"firstname": "test user 1", "birthday": ISODate("1996-07-04T00:00:00Z")}}

Any help is much appreciated.

Your DB is storing a MongoDB date object (ISODate("1996-07-04T00:00:00Z")), but you are testing with strings.

You either need to store the date as a string in the DB, or transform your query date strings into date objects:

db.users.find({"myProfile.birthday":{"$lte":new Date("1997-12-21T14:57:55.396Z"),"$gte":new Date("1993-12-21T14:57:55.396Z")}})
1 Like

Yeah, actually it works when I type in the below query

 db.users.find({"myProfile.birthday":{$lt: ISODate("1977-11-06")})

But the problem is I have a combo box allowing the user to select the date range and then I will help to build the query and then issue the find. I don’t know how can I build the query using ISODate Object. I tried it didn’t work as well.

library.js

builtDateQuery = function( fromDate, toDate) {
  var myDate = 'myProfile.birthday';
  var fromDate = new Date().setYear(new Date().getFullYear() - fromAge);
  var toDate = new Date().setYear(new Date().getFullYear() - toAge);
  query[myDate] = {
    $lte: new Date(fromDate).toISOString(),
    $gte: new Date(toDate).toISOString()
  }; 
  return query;
}

dropdown.html

<template name="dropdown">
  age range
  <select id="age-range">
     <option id="18-22">18-22</option>
     <option id="23-26">23-26</option>
  </select>
</template>

dropdown.js

Template.dropdown.events({
  'change #age-range': function (target) {
    var myValue = target.options[target.selectedIndex].value;
    var fromAge = myValue.substr(0,2);
    var toAge = myValue.substr(3,2);
    var query = buildQuery(fromDate, toAge);
    var myUsers = Meteor.users.find({query});
    console.log(JSON.stringify(myUsers));
  }
})

Whereas, in the console, I got no user at all but I am sure at least one matching user should be returned.

ISODate is a MongoDB helper function to wrap a Javascript Date object, so you should be able to do:

builtDateQuery = function( fromDate, toDate) {
  var myDate = 'myProfile.birthday';
  var fromDate = new Date().setYear(new Date().getFullYear() - fromAge);
  var toDate = new Date().setYear(new Date().getFullYear() - toAge);
  query[myDate] = {
    $lte: new Date(fromDate),
    $gte: new Date(toDate)
  }; 
  return query;
}

You cool, it works. Thx @robfallows .

1 Like