MongoDB(3.0)聚合:多个匹配与一个匹配具有多个项

13

我正在处理一个项目,需要我根据很多匹配条件(可能高达100个)动态创建MongoDB查询。除了创建适当的索引之外,我想知道如何将这些匹配条件构建到管道中是否有区别。根据下面的例子,这两个示例中是否有一个表现不同或更好?

我猜测示例2会缩小结果集但调用次数更多?也许示例1在幕后正在做这件事情?

非常感谢您的帮助!

示例1:

db.Test.aggregate(
[     
   { $match: { item1: 'foo1', item2: 'foo2', item3: 'foo3' } }
])

对比

示例2

db.Test.aggregate(
[     
   { $match: { item1: 'foo1' } },
   { $match: { item2: 'foo2' } },
   { $match: { item3: 'foo3' } }
])

我怀疑这对这个问题没有影响,但如果相关的话,我将使用C#驱动程序进行实现。


有趣的问题...你找到答案了吗? - Moppo
我也很好奇想知道! - Romain G
2个回答

13

我在MongoDB文档中找到了以下信息:

$match + $match 合并

当一个$match紧随另一个$match时,这两个阶段可以合并为单个$match,使用$and将条件组合在一起。例如,一个管道包含以下序列:

{ $match: { year: 2014 } },
{ $match: { status: "A" } }

然后第二个$match阶段可以合并到第一个$match阶段中,从而产生单个的$match阶段:

{ $match: { $and: [ { "year" : 2014 }, { "status" : "A" } ] } }
从这可以得出结论,连续使用多个$match与使用单个具有多个字段的$match相同。
不过我不确定为什么优化引擎在这里添加了一个$and运算符。根据这个答案,它不应该是必要的,因此我认为可以忽略它。有人可以确认吗?

我也假设$and已经隐含了,所以它似乎是不必要的,但可能会有其他原因。 - cdimitroulas
原因是同一字段可能出现在两个或多个$match阶段中。Bson(Json,Map等)不能有重复的键,它们会被静默忽略。 - qtxo

4

今天我也在思考同样的问题,然后看到了Romain的答案。虽然我想亲自验证一下,但是可以通过在两个聚合上使用explain轻松地完成;

db.verpakking.explain().aggregate([
    { "$match": {type: "VERPAKT"} },
    { "$match": {ras: "CherryStar"} },
]);

这将导致以下输出结果:
{
  "waitedMS" : NumberLong(0),
  "stages" : [
    {
      "$cursor" : {
        "query" : {
          "$and" : [
            {
              "type" : "VERPAKT"
            },
            {
              "ras" : "CherryStar"
            }
          ]
        },
        "queryPlanner" : {
          "plannerVersion" : NumberInt(1),
          "namespace" : "denberk.verpakking",
          "indexFilterSet" : false,
          "parsedQuery" : {
            "$and" : [
              {
                "ras" : {
                  "$eq" : "CherryStar"
                }
              },
              {
                "type" : {
                  "$eq" : "VERPAKT"
                }
              }
            ]
          },
          "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
              "$and" : [
                {
                  "ras" : {
                    "$eq" : "CherryStar"
                  }
                },
                {
                  "type" : {
                    "$eq" : "VERPAKT"
                  }
                }
              ]
            },
            "direction" : "forward"
          },
          "rejectedPlans" : [

          ]
        }
      }
    }
  ],
  "ok" : NumberInt(1)
}

虽然...
db.verpakking.explain().aggregate([
{ "$match": {type: "VERPAKT", ras: "CherryStar"} },
]);

输出结果:

{
  "waitedMS" : NumberLong(0),
  "stages" : [
    {
      "$cursor" : {
        "query" : {
          "type" : "VERPAKT",
          "ras" : "CherryStar"
        },
        "queryPlanner" : {
          "plannerVersion" : NumberInt(1),
          "namespace" : "denberk.verpakking",
          "indexFilterSet" : false,
          "parsedQuery" : {
            "$and" : [
              {
                "ras" : {
                  "$eq" : "CherryStar"
                }
              },
              {
                "type" : {
                  "$eq" : "VERPAKT"
                }
              }
            ]
          },
          "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
              "$and" : [
                {
                  "ras" : {
                    "$eq" : "CherryStar"
                  }
                },
                {
                  "type" : {
                    "$eq" : "VERPAKT"
                  }
                }
              ]
            },
            "direction" : "forward"
          },
          "rejectedPlans" : [

          ]
        }
      }
    }
  ],
  "ok" : NumberInt(1)
}

如您所见,这段代码完全相同,除了“query”部分(这是正常的,因为我们的查询不同的)。这证明,无论您使用两个连续的独立的$match管道还是一个组合的$match管道,解析后的查询都将完全相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接