我们当前有一个电影集合查询,以返回与用户生成的“compilations”中的标题匹配的“compilation”文档。
用户可以应用几个过滤器:访问群体、提供者和格式。
当前,此find()组合返回以下文档,即使匹配的“title”与筛选器不匹配,但“compilation”中的第二部电影与筛选器匹配。

{
  "_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
              }
            }
    ]
}

如何重写它使$or+$和查询与$elemmatch特别相关?
它非常适用于只有一部电影,但没有多部电影的“汇编”。
Models.Movies.find(
   {
        "collection": {
            "$elemMatch": {
               "title": "2001"
            }
        }
   }
)
.and([
  {$or : audienceQuery},
  {$or : providerQuery}
])
.and(formatQuery)

过滤查询如下所示:
audienceQuery == {"audience.adults":true}, {"audience.teenagers":true}, {"audience.kids":false};
providerQuery == {"compilation.provider.apple":true}, {"compilation.provider.google":true};
formatQuery == {"compilation.format.hd":true};

最佳答案

考虑使用aggregation framework可以利用管道阶段开始时的$match操作进行早期筛选,以限制进入管道的文档。当放置在管道的开头时,$match操作使用适当的索引来仅扫描集合中匹配的文档。您的第二个管道阶段将涉及对$unwind数组使用compilation操作,以便$match操作的后续应用程序将过滤解构数组:剩余的管道操作$group$project然后只传递来自以前筛选的输入文档和新计算的字段:
因此,聚合管道将如下所示:

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
        }
    }
])

结果:
/* 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
}

关于node.js - Mongoose/MongoDB find()$或/$并在$ elemMatch内?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29328525/

10-13 07:42