BanksySan
BanksySan

Reputation: 28530

MongoDB Not equal isn't the opposite of equal

If I run the following:

db.restaurants.find({'grades.grade': 'A'}, {'grades.grade':1})

Then I return all the documents that have a grade with an 'A'. That being, any document with at least one 'A' but there may be other grades. e.g:

{
    "_id" : ObjectId("56c9a7038cc7f52917313091"),
    "grades" : [ 
        {
            "grade" : "A"
        }, 
        {
            "grade" : "B"
        }, 
        {
            "grade" : "A"
        }, 
        {
            "grade" : "A"
        }
    ]
}

If I use the $ne operator:

db.restaurants.find({'grades.grade': {$ne: 'A'}}, {'grades.grade':1})

Then I return all the documents that have all grades not equal to 'A'.

e.g.

{
    "_id" : ObjectId("56c9a7038cc7f52917313245"),
    "grades" : [ 
        {
            "grade" : "B"
        }, 
        {
            "grade" : "B"
        }, 
        {
            "grade" : "C"
        }, 
        {
            "grade" : "B"
        }, 
        {
            "grade" : "B"
        }
    ]
}

Why does $ne match all grades, but equal to match any grade?

NB: The same behaviour seems to be true in $in ans $nin. $in matches any document whereas $nin matches all documents.

NB2: $not exhibits the behaviour too.

Upvotes: 1

Views: 1689

Answers (2)

JF4
JF4

Reputation: 486

This is a consequence of basic De Morgan logical negation.

NOT (A AND B) means (NOT A) OR (NOT B)

This can be directly translated to set theory, where disjunction (OR) means union and conjunction (AND) means intersection.

When you apply equals you're asking for ANY value that matches(disjunction). But when you negate that, you negate every single match as per De Morgan.

Upvotes: 0

Yaron Schwimmer
Yaron Schwimmer

Reputation: 5357

I understand your question, and I agree that from a functional perspective you are right: There should be a way to "negate" equals in a way that is logically "give me anything that contains something that is not A".

But I think the the way Mongo works here makes sense from a formal logic perspective. If you think about it logically, then the query db.restaurants.find({'grades.grade': 'A'}) returns

(grades[0].grade == 'A' || grades[1].grade == 'A' || grades[2].grade == 'A'|| grades[3].grade == 'A'...)

Basic formal logic says that the negation of the above statement is

(grades[0].grade != 'A' && grades[1].grade != 'A' && grades[2].grade != 'A'&& grades[3].grade != 'A'...)

Which is exactly what you got by using $ne.

Upvotes: 1

Related Questions