使用$ unwind时可以避免两次使用相同的$ match条件吗?

时间:2019-04-12 08:12:51

标签: mongodb mongodb-query aggregation-framework

以以下数据为例:

{
  _id: 1,
  item: "abc",
  stock: [
    { size: "S", color: "red", quantity: 25 },
    { size: "S", color: "blue", quantity: 10 },
    { size: "M", color: "blue", quantity: 50 }
  ]
}
{
  _id: 2,
  item: "def",
  stock: [
    { size: "S", color: "blue", quantity: 20 },
    { size: "M", color: "blue", quantity: 5 },
    { size: "M", color: "black", quantity: 10 },
    { size: "L", color: "red", quantity: 2 }
  ]
}
{
  _id: 3,
  item: "ijk",
  stock: [
    { size: "M", color: "blue", quantity: 15 },
    { size: "L", color: "blue", quantity: 100 },
    { size: "L", color: "red", quantity: 25 }
  ]
}

说我要过滤掉符合条件stock的{​​{1}}。我已经在size = 'L'字段上有了一个多键索引。

在聚合管道中,如果我使用以下两个操作:

stock.size

我会得到理想的结果,但是当数据库很大时,[{$unwind: {path: "$stock"}}, {$match: {"stock.size": "L"}}] 步骤将不得不扫描整个集合,而无需利用现有索引,这是非常低效的。

如果我颠倒$unwind$unwind操作的顺序,$match将利用索引来应用早期过滤,但最终结果将不是所希望的:它将获取额外的$match,这些stock的大小不是L,但是具有同级stock的L大小的同级item

我是否必须使用相同的$match操作两次,即在$unwind之前和之后都要使用索引并返回正确的结果?

1 个答案:

答案 0 :(得分:2)

是的,您可以在聚合管道中两次使用$match阶段,但是这里只有第一个$match阶段将使用索引,第二个阶段将使用collscan。

[
    { "$match": { "stock.size": "L" }},
    { "$unwind": { "path": "$stock" }},
    { "$match": { "stock.size": "L" }}
]

如果您想两次避免使用$match,请使用$filter聚合

[
  { "$match": { "stock.size": "L" } },
  { "$addFields": {
    "stock": {
      "$filter": {
        "input": "$stock",
        "as": "st",
        "cond": { "$eq": ["$stock.size", "L"] }
      }
    }
  }}
]