Mongoose / MongoDB find()$或/ $并在$ elemMatch内?

时间:2015-03-29 11:29:02

标签: node.js mongodb mongoose

我们目前有一个查询电影收藏,以便返回'汇编'与用户生成的编辑相匹配的文档'。

用户可以应用一些过滤器:受众群体提供商格式

目前,此find()组合会返回以下文档,即使匹配的'标题为'不适合过滤器,而是编译中的第二部电影'匹配过滤器..

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

如何重写,以便$或+ $和查询专门与$ 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};

1 个答案:

答案 0 :(得分:1)

考虑使用 aggregation framework ,您可以在管道阶段开始时利用$match操作进行早期过滤,以限制进入管道的文档。放置在管道的开头时, $match 操作使用合适的索引来仅扫描集合中的匹配文档。您的第二个管道阶段将涉及在compilation数组上使用 $unwind 操作,以便后续应用 $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
}