Mongoose/MongoDB find() $or/$and within an $elemMatch?

We currently have a query for a Movies collection to return 'compilation' documents matching the title from user-generated 'compilations'.

The user can apply a few filters: audience, provider, and format.

Currently, this find() combination returns the following document, even though the matched 'title' does not fit the filters, instead the second movie in the 'compilation' matches the filters..

{
  "_id": "551781485d9d35e4720c9057",
  "name": "Consciousness",
  "audience": {
            "adults": true,
            "teenagers": true,
            "kids": false
  },
  "compilation": [
            {
              "title": "2001",
              "_id": "551781485d9d35e4720c9059",
              "provider": {
                        "amazon": false,
                        "apple": true,
                        "hulu": false,
                        "google": true,
                        "xfinity": false
              },
              "format": {
                        "hd": false,
                        "sd": false,
                        "4k": true
              }
            },
            {
              "title": "Matrix",
              "_id": "551781485d9d35e4720c9059",
              "provider": {
                        "amazon": false,
                        "apple": true,
                        "hulu": false,
                        "google": true,
                        "xfinity": false
              },
              "format": {
                        "hd": true,
                        "sd": false,
                        "4k": false
              }
            }
    ]
}

How can this be rewritten so that the $or + $and queries relate specifically to the $elemMatch? It works perfectly for 'compilations' that have a single movie, but not multiple movies..

Models.Movies.find(
   { 
        "collection": {
            "$elemMatch": {
               "title": "2001"
            }
        }
   }
)
.and([
  {$or : audienceQuery},
  {$or : providerQuery}
])
.and(formatQuery)

Where the filtering queries look like this:

audienceQuery == {"audience.adults":true}, {"audience.teenagers":true}, {"audience.kids":false};
providerQuery == {"compilation.provider.apple":true}, {"compilation.provider.google":true};
formatQuery == {"compilation.format.hd":true};

Consider using the aggregation framework where you can take advantage of early filtering with the $match operations in the beginning of the pipeline stage to restrict the documents that enter the pipeline. When placed at the beginning of a pipeline, $match operations use suitable indexes to scan only the matching documents in a collection. Your second pipeline stage will involve the use of $unwind operation on the compilation array so that a subsequent application of the $match operation will filter the deconstructed array: The remaining pipeline operations $group and $project then passes along the grouped documents with only the specified fields from the previous filtered input documents and newly computed fields:

So your aggregation pipeline will look like:

Models.Movies.aggregate([
    {
        "$match": {
            "compilation.title": "2001",
            "$and": [
              { "$or": [{"audience.adults": true}, {"audience.teenagers": true}, {"audience.kids": false}] },
              { "$or": [{"compilation.provider.apple": true}, {"compilation.provider.google": true}] }
            ],
            "compilation.format.hd": true
        }
    },
    {
        "$unwind": "$compilation"
    },
    {
        "$match": {
            "compilation.title": "2001",
            "$and": [
              { "$or": [{"audience.adults": true}, {"audience.teenagers": true}, {"audience.kids": false}] },
              { "$or": [{"compilation.provider.apple": true}, {"compilation.provider.google": true}] }
            ],
            "compilation.format.hd": true
        }
    },
    {
        "$group": {
            "_id": { 
                "_id": "$_id",
                "name": "$name",
                "audience": "$audience"
            },
            "compilation": {
                "$push": "$compilation"
            }
        }
    },
    {
        "$project": {
            "_id": "$_id._id",
            "name": "$_id.name",
            "audience": "$_id.audience",
            "compilation": 1
        }
    }
])

Result:

/* 0 */
{
    "result" : [ 
        {
            "_id" : "551781485d9d35e4720c9057",
            "compilation" : [ 
                {
                    "title" : "2001",
                    "_id" : "551781485d9d35e4720c9059",
                    "provider" : {
                        "amazon" : false,
                        "apple" : true,
                        "hulu" : false,
                        "google" : true,
                        "xfinity" : false
                    },
                    "format" : {
                        "hd" : true,
                        "sd" : false,
                        "4k" : true
                    }
                }
            ],
            "name" : "Consciousness",
            "audience" : {
                "adults" : true,
                "teenagers" : true,
                "kids" : false
            }
        }
    ],
    "ok" : 1
}