skip to Main Content

I have this document structure:

{
      "name": "ab",
      "grades": [
        {
          "grade": "A",
          "score": 1
        },
        {
          "grade": "A",
          "score": 12
        },
        {
          "grade": "A",
          "score": 7
        }
      ],
      "borough": "Manhattan2"
    }

Assignment is to write a query to find the restaurants that have all grades with a score greater than 5.

The solution to the problem is following:

db.restaurants.find({
      "grades": {
        "$not": {
          "$elemMatch": {
            "score": {
              "$lte": 5
            }
          }
        }
      }
    })

I have troubles understanding proposed solution.

So far I have only used $elemMatch to match at least one element of array or elements in array’s inner objects (grades.score), but how the heck $not is "somehow making" $elemMatch to check for all grades.score in this object?

I do understand general idea, "don’t look at score less the equal to 5, and what remains is what we need", but I cannot comprehend what does this code snippet returns:

"$not": {
      "$elemMatch": {
        "score": {
          "$lte": 5
        }
      }
    }

If was asked what does this query do before running & testing it, I would say that it find first score that is greater then 5 and takes that document, but that is wrong, and I cannot figure why. I see that order of fields and keywords plays some role but don’t see the connection.

2

Answers


  1. From the docs for $elemMatch

    The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.

    { "$elemMatch": { "score": { "$lte": 5 } } } will match documents which have at least one array element with score <= 5.

    • So $not on that will match documents which don’t meet that criteria
    • ie "none of the elements have score <= 5"
    • And that’s the same as "all of the elements have score > 5"
    Login or Signup to reply.
  2. In order to understand $elemMatch and $not, it would help if we start with $all.

    As you we all know, $not is equal to say “No”, the opposite to “Yes”. We shall come to “No”
    later. let us first start discussing $all, $elemMatch under the context of “Yes”.

    Let us have a sample collection of just one document as below.

    In Mongo shell:

    let t = db.test;
    t.find();
    [ a: [ 1, 2 ] } ]
    

    First of $all :

    Query 1 : found

    t.find({a: { $all:[1] }});
    [ { a: [ 1, 2 ] } ]
    

    Query 2: found

    t.find({a: { $all:[1,2] }});
    [ {  a: [ 1, 2 ] } ]
    

    Query 3: Not found

    t.find({a: { $all:[1,2,3] }});
    <No document>
    

    What does it mean by $all ?

    Th above three queries can be described in one line as “find all documents with the array key “a” has all elements in the given array”.

    • Query 1 : a has all elements in [1] – found
    • Query 2 : a has all elements in [1,2] – found
    • Query 2 : a does not have all elements in [1,2,3] – not found

    Now let us see $elemeMatch

    Query 4 : found

    t.find({a : { $elemMatch: {$eq: 1}}});
    [ { a: [ 1, 2 ] } ]
    

    Query 5 : found

    t.find({a : { $elemMatch: {$eq: 2}}});
    [ { a: [ 1, 2 ] } ]
    

    Query 6 : Not found

    t.find({a : { $elemMatch: {$eq: 3}}});
    <No document>
    

    What does it mean by $elemMatch ?

    The above three queries can be described in one line as “find all documents with the array key “a” has at least one element matching the given condition”.

    • Query 4 : a has one element matching the condition equal to 1 – found
    • Query 5 : a has one element matching the condition equal to 2 – found
    • Query 6 : a has no element matching the condition equal to 3 – not found

    The take-away:

    1. Although the two set of queries with $all and $elemMatch yielded the same results, the interpretation of the queries are different – as it is described above.
    2. Now all these queries are in the context of “Yes”.
    3. If we are able to understanding the basics of these six queries, we can easily guess what will be the outcome of the same 6 queries in “No” context. It would be just the opposite results.

    Please see the test results.

    t.find({a: {$not: { $all:[1] }}}); // no data
    t.find({a: {$not: { $all:[2] }}}); // no data
    t.find({a: {$not: { $all:[3] }}}); // data found
            1. [ { a: [ 1, 2 ] } ]
    t.find({a : {$not : { $elemMatch: {$eq: 1}}}}); // no data
    t.find({a : {$not : { $elemMatch: {$eq: 2}}}}); // no data
    t.find({a : {$not : { $elemMatch: {$eq: 3}}}}); / data found
            1. [ { a: [ 1, 2 ] } ]
    

    Notes:

    $elemMatch can take more than one conditionals, for brevity, the examples in this post include just one condition. When there will be more than one condition, it would be evaluated with AND logical operation which is essentially the same as with just one condition.

    Thanks

    WeDoTheBest4You

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search