Hi community!
I am trying to understand the challenge proposed by this link
“Write a MongoDB query to find the restaurant Id, name, borough and cuisine for those restaurants which achieved a score which is not more than 10.”
The solution says it is:
Restaurants.find(‘grades.score’:{ $not: {$gt : 10}}}) → 340 results
which is ok, I get it.
this was my solution, which does not shield the same result count.
{‘grades.score’:{ $lt : 10}} → more than 3529 results
I am not understanding why. I have read how to query on arrays, and I thought that whenever I wrote a query on an array, it would return me any document which satified at least one element in the said array, but the proposed solution is returning all documents satifying all elements in the document’s array.
extra:
If you have any link, blog, or example explaining this, it would be really appreciated
Looking at that dataset it’s easiest to work with a single simple example:
One document has the following scores:
[14,2]
So we have two possible filters:
Documents with a score that is NOT greater than 10
Documents with a score of less than 10
In our sample we can see that it satisfies the second filter but not the first, while it does have values that are less than 10, it ALSO has values that are greater and so does not match the first.
This had me scratching my head a bit I’m afraid to say until I narrowed it down to a sample document to work through…
Thank you for taking the time to analyze the situation John.
What I don’t understand is why the 1° filter (Not greater than 10) takes into consideration every element in the array, while the 2° filter just takes into consideration one element fulfilling the condition to render it as true.
It is like if the 1° filter applies an “and” on every element in the array, while the second filter applies an ‘or’.
Then I read the problem: Documents with a score that is NOT greater than 10
and it makes it worse.
“documents with A score…” So I understand Documents with at least a single score for which the filter is true.
For it to be valid in my mind, it should read, Documents with all the scores not greater than 10. But then I would think that filtering by “Not greater than 10” tries to match every element in the array, but the 2° filter just tries to match a single element in the array, and I don’t understand why.
When you wrap the criteria in a not you are basically turning it into an AND from an OR, basically this:
So when you do something like this:
{
MyField :[1,2,3,4]
}
MyField : {$lt:3}
We’re saying find me any document where one of the values is less than 3, i.e.
(1 < 3) OR (2 < 3) OR (3 < 3) OR (4 < 3)
Which comes out to:
=> TRUE OR TRUE OR FALSE OR FALSE
=> TRUE
So with the normal condition we’re saying that it’s an OR over all elements to work out if any of them match.
Now if we wrap that in a not:
$not:{MyField : {$lt:3}}
We’re saying that we want to find a document where NONE of the items are less than 3, in effect the opposite of the above query is none of the elements can be set to less than 3. So we’re swapping from finding a single matching element to demanding that none of them match which is more restrictive.
NOT (A OR B) = (NOT A) AND (NOT B)
The negation of an AND is: NOT (A AND B) = (NOT A) OR (NOT B)
So in our above case:
(1 < 3) OR (2 < 3) OR (3 < 3) OR (4 < 3)
becomes NOT( (1 < 3) OR (2 < 3) OR (3 < 3) OR (4 < 3))
Which can be expanded to: NOT(1 < 3) ANDNOT(2 < 3) ANDNOT(3 < 3) ANDNOT(4 < 3)
Evaluates to:
=> NOT(TRUE) ANDNOT(TRUE) ANDNOT(FALSE) ANDNOT(FALSE)
=> FALSE AND FALSE AND TRUE AND TRUE
=> FALSE
Hopefully this helps explain it a bit more…it can be a bit of a mind bend to swap a condition and then NOT it when working over a range of elements in an array due to this.