[Solved] MongoDB: And not working

Hey guys,
I’m having a little problem in one of my template helpers. Just for example, I try to get all stats that are smaller than the current month:

pastStats: function () {
    var month = moment().month() + 1; //to get the current month
    var year = moment().year();  //to get the current year

   //show me all stats that are smaller than current month and year
    return Stats.find({
        userId: Meteor.userId(),
        $and: [{month: {$ne: month}}, {year: {$ne: year}}]
    }, {sort: {createdAt: 1}, limit: 6});
}

This isn’t working. If I remove “{year: {$ne: year}}”, it works. I’m just wondering, because it seems like Mongo handles it like an OR condition, f.e.: “Find all stats that are not having the current month OR the current year” - but I need “Find all stats that are not having the current month AND the current year”. So what is wrong in my query?

So this: var month = moment().month() + 1; will evaluate to 1 (January) as of this month. However, you are calculating the year as 2015 - I would guess that’s not what you want?

Also, your query (pseudo code): a != b && c != d is, by DeMorgan’s theorum the same as !(a == b || c == d). In your case !(month == 1 || year == 2015) - which seems equally unlikely.

Well, 2015 is correct, so for this month I want to get all stats that are smaller than December 2015. moment().month() +1 returns 12, because moment.js begins with 0 for the months.

Problem is, that he doesn’t execute the query as a != b && c != d. I’ve just set plain text values to this function (month:12, year:2015) and he still shows 0 results. If I remove the year, I get all stats.

You’re right about 12 - I misread the code - my bad.

Have you confirmed that the year is stored as a number (not a string) in your db?

Yes, I’ve read the values via console.log() and also tried plain values, just for example, this here works fine and gives me the stats for the current month:

 return Stats.find({
        userId: Meteor.userId(),
        $and: [{month: 12}, {year: 2015}]
    }, {sort: {createdAt: 1}, limit: 6});

Now I want all that don’t have the current month and the current year:

 return Stats.find({
        userId: Meteor.userId(),
        $and: [{month: {$ne:12}}, {year: {$ne:2015}}]
    }, {sort: {createdAt: 1}, limit: 6});

This returns 0 results. If I do this:

 return Stats.find({
        userId: Meteor.userId(),
        $and: [{month: {$ne:12}}]
    }, {sort: {createdAt: 1}, limit: 6});

…I get the results for November 2015.

This are my collection records:

{
"_id": "bY8bB2xivdmiH5Yqe",
"year": 2015,
"month": 11,
"userId": "6niDsS5RXDKqWLpoB",
"likes": 25,
"createdAt": ISODate('2015-11-19T18:03:29.752Z'),
"posts": 16
}

{
"_id": "Wm9JiFNmTR8PtrJWC",
"year": 2015,
"month": 12,
"userId": "6niDsS5RXDKqWLpoB",
"posts": 9,
"createdAt": ISODate('2015-12-01T15:36:52.910Z'),
"likes": 2
}

I think we’re back to DeMorgan:

you want docs which are not (November and 2015) - the current month.

So, you want !(month == 11 && year == 2015) or, by DeMorgan: month != 11 || year != 2015.

I think you need $or: [{month: {$ne:11}}, {year: {$ne:2015}}] instead of $and ....

(you said November was stored as 11 in the db).

Yeah you are right. After your first post I was still thinking about that $ne maybe reversed my $and logic. So yes, $or is the correct way. Thank you very much.

1 Like