【问题标题】:How to include un-matched documents in the mongodb aggregation output?如何在 mongodb 聚合输出中包含不匹配的文档?
【发布时间】:2020-06-14 01:08:54
【问题描述】:

我在下面写了聚合,它在集合中展开一个数组,然后检查每个展开的文档是否与给定对象 id 数组中的任何元素匹配。投影时,生成的集合通过条件进一步过滤。

  db.collectionName.aggregate([
    { $unwind: { 
          path: "$field5", 
          preserveNullAndEmptyArrays: true 
        } 
    },
    { $match: {  field5: { $in: 

       [
          ObjectId("id1"),
          ObjectId("id2"),
          ObjectId("id3")
      ]

        } } },
    { $project: { 
        _id: 0,
        field0: "$field0",
        field1: "$field1",
        field2: "$field2",
        customField: { 
            $or:[{
                  $and: [ 
                      { /*...some  condition*/ } ,  
                      { /*...some condition*/  }   
                    ],
                },
                { 
                    $and: [ 
                      { /*...some  condition*/ } , 
                      {  /*...some  condition*/ }   
                    ]
              }]
           }
       } 
     }
  ])     

这按预期工作。

结果

/* 1 */
{
    "field0" : "processing",
    "field5" : ObjectId("id1"),
    "field1" : "val",
    "field2" : "val",
    "customField" : false
}

/* 2 */
{
    "field0" : "processing",
    "field5" : ObjectId("id2"),
    "field1" : "val",
    "field2" : "val",
    "customField" : false
}

如果我理解正确,$match 阶段会删除不匹配的 id(比如 id3)。我怎样才能通过创建一个包含为其投影的所有字段的单独文档来在最终输出中获得它?

预期结果

/* 1 */
{
    "field0" : "processing",
    "field5" : ObjectId("id1"),
    "field1" : "val",
    "field2" : "val",
    "customField" : false
}

/* 2 */
{
    "field0" : "processing",
    "field5" : ObjectId("id2"),
    "field1" : "val",
    "field2" : "val",
    "customField" : true
}


/* 3 */
/* Note that id3 is unmatched (excluded by $match in result) */
{
    "field0" : "processing",
    "field5" : ObjectId("id3"),
    "field1" : "val",
    "field2" : "val",
    "customField" : true
}

有没有办法做到这一点?

【问题讨论】:

  • 该查询真的不包括第三个文档吗?根据$match,它也应该包括它!无论如何,您是否需要在同一输出中匹配和不匹配的文档?如果是 - 那么这两种类型之间的区别是什么(就像任何表示匹配文档为真而为不匹配为假的字段)?如果没有这样的事情,那么做$match是没有用的,如果是这种情况,您实际上发布了您的原始要求,以便有人可以帮助您..
  • @whoami 感谢您的回复。抱歉,我也应该解释一下用例。 collectionName 保留要匹配的 id 的临时条目(在 field5 中,它是一个数组)。 $match 阶段将展开文档与传递给它的数组进行比较以寻找匹配项。这是我看不到 id3 并且不确定它是否正确的地方。我想保留无与伦比的价值并将其保存在某个地方。不匹配的值也不应根据 $project 中的条件进行检查,因此不会进一步过滤,应包含在最终结果中。
  • 所以你只需要在输出中保留不匹配的文档,同时你需要对匹配的文档做更多的事情吗?所以确实希望在最后为每个文档返回多个文档(正如你正在做的$unwind),如果是 - 那么放松是有意义的,否则不需要它(所以你正在做的是拆分一个doc 到 field5 上的多个并将它们分类为匹配与不匹配,最终它是具有不同 field5 的同一个文档)?? 无论如何,在您给定的示例文档和查询中,我认为您应该获得带有 ObjectId("id3") 的文档,不确定您为什么没有获得它
  • @whoami,感谢您的回复。现在我将匹配的结果与临时条目(field5 数组)进行比较,并只添加缺失或不匹配的项目。这似乎是一个肮脏的黑客,但工作。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

是的,你可以做到这一点,但它有点棘手。

$facet 将有助于实现这一目标。

db.collection.aggregate([
  {
  $facet:{
    "matched":[
      { 
        $unwind:{ 
          path: "$field5", 
          preserveNullAndEmptyArrays: true 
        } 
      },
      { 
        $match:{  
          field5: {
            $in:
             [
                ObjectId("id1"),
                ObjectId("id2"),
                ObjectId("id3")
             ]
          } 
        } 
      },
      { 
        $project: { 
          _id: 0,
          field0: "$field0",
          field1: "$field1",
          field2: "$field2",
          customField: { 
              $or:[{
                    $and: [ 
                        { /*...some  condition*/ } ,  
                        { /*...some condition*/  }   
                      ],
                  },
                  { 
                      $and: [ 
                        { /*...some  condition*/ } , 
                        {  /*...some  condition*/ }   
                      ]
                }]
             }
         } 
       }
    ],
    "unmatched":[
    { 
      $unwind:{ 
        path: "$field5", 
        preserveNullAndEmptyArrays: true 
      } 
    },
    { 
      $match:{  
        field5: {
          $nin:
           [
              ObjectId("id1"),
              ObjectId("id2"),
              ObjectId("id3")
           ]
        } 
      } 
    },
    { 
      $project: { 
        _id: 0,
        field0: "$field0",
        field1: "$field1",
        field2: "$field2",
        customField: { 
            $or:[{
                  $and: [ 
                      { /*...some  condition*/ } ,  
                      { /*...some condition*/  }   
                    ],
                },
                { 
                    $and: [ 
                      { /*...some  condition*/ } , 
                      {  /*...some  condition*/ }   
                    ]
              }]
           }
       } 
     }
    ]
  }
  },
  {
    $project:{
      "all":{
        $concatArrays:[
          "$matched",
          "$unmatched"
        ]
      }
    }
  },
  {
    $unwind:"$all"
  },
  {
    $replaceRoot:{
      newRoot:"$all"
    }
  }
]).pretty()

希望这会对您的用例有所帮助。

此外,您可以根据需要更新两个方面(“匹配”和“不匹配”)。

你可以找到更多关于 $facet here

【讨论】:

  • $facets 看起来很有趣。我一定会尝试的。截至目前,我正在将匹配的结果与临时条目(field5 数组)进行比较,并仅向其中添加缺失或不匹配的项目。谢谢。
  • 关于$facet 的另一件事是它同时运行,即;方面中的所有阶段将同时运行。
猜你喜欢
  • 2014-03-04
  • 1970-01-01
  • 2015-10-31
  • 2021-04-25
  • 2018-06-13
  • 2014-11-24
  • 2023-02-23
  • 1970-01-01
  • 2020-01-09
相关资源
最近更新 更多