【问题标题】:How to address arrays with mongodb Set Operators如何使用 mongodb 集合运算符寻址数组
【发布时间】:2014-06-17 20:51:57
【问题描述】:

使用示例 zipcodes 集合,我有一个这样的查询:

db.zipcodes.aggregate([
{ "$match": {"state": {"$in": ["PA","NY"]}}},
{ "$group": { "_id": { "city": "$city" }, "ZipsPerCity": {"$addToSet": "$_id"}}},
{ "$match": { "ZipsPerCity" : { "$size": 2 }}},
]).pretty()

这只是一个查找具有 2 个邮政编码的城市(纽约州和宾夕法尼亚州)的示例:

{
        "_id" : {
                "city" : "BETHLEHEM"
        },
        "ZipsPerCity" : [
                "18018",
                "18015"
        ]
}
{
        "_id" : {
                "city" : "BEAVER SPRINGS"
        },
        "ZipsPerCity" : [
                "17843",
                "17812"
        ]
}

现在假设我想使用“$setDifference”集合运算符将“BEAVER SPRINGS”邮政编码与“BETHLEHEM”邮政编码进行比较?我尝试在 $project 运算符中使用“$setDifference”运算符,如下所示:

db.zipcodes.aggregate([
  { "$match": { "state": {"$in": ["PA","NY"]}}},
  { "$group": { "_id: {city : "$city"},"ZipsPerCity": {$addToSet: "$_id"}}},
  { "$match": { "ZipsPerCity" : { $size: 2 }}},
  { "$project": {
       "int": { "$setDifference":[
           "$_id.city.BETHLEHEM.ZipsPerCity",
           "$_id.city.BEAVER SPRINGS.ZipsPerCity"
       ]}
  }}
]).pretty()

这甚至看起来都不对,更不用说产生结果了。虽然没有错误。

您将如何使用 $setDifference(或任何集合运算符)引用像这样使用 $addToSet 构建的几个数组?

【问题讨论】:

    标签: mongodb aggregation-framework


    【解决方案1】:

    您在这里尝试做的第一件事是您要比较的数组实际上位于两个不同的文档中。实际上,所有聚合框架运算符一次只处理一个文档,除了用于“聚合”文档的 $group 和可能本质上将一个文档转换为多个文档的 $unwind

    为了进行比较,您需要将数据出现在一个文档中,或者至少以某种方式“配对”。所以有一种技术可以做到这一点:

    db.zipcodes.aggregate([
        { "$match": {"state": { "$in": [ "PA","NY" ] } }},
        { "$group": { 
            "_id": "$city",
            "ZipsPerCity": { "$addToSet": "$_id"}
        }},
        { "$match": { "ZipsPerCity" : { "$size": 2 } }},
        { "$group": {
             "_id": null,
             "A": { "$min": {
                 "$cond": [
                     { "$eq": [ "$_id", "BETHLEHEM" ] },
                     { "city": "$_id", "ZipsPerCity": "$ZipsPerCity" },
                     false
                 ]
             }},
             "B": { "$min": {
                 "$cond": [
                     { "$eq": [ "$_id", "BEAVER SPRINGS" ] },
                     { "city": "$_id", "ZipsPerCity": "$ZipsPerCity" },
                     false
                 ]
             }}
        }},
        { "$project": {
            "A": 1,
            "B": 1,
            "C": { "$setDifference": [ "$A.ZipsPerCity", "$B.ZipsPerCity" ] }
    
        }}
    ])
    

    这有点做作,我很清楚实际结果集有两个以上的城市,但要说明发送到“集合运算符”(例如$setDifference)的数组/集合需要是在同一个文档中。

    这里的结果将“左”数组与“右”数组进行比较,返回“左”与“右”不同的成员。这两组都是独一无二的,没有重叠,所以结果应该是预期的:

    {
        "_id" : null,
        "A" : {
                "city" : "BETHLEHEM",
                "ZipsPerCity" : [
                        "18018",
                        "18015"
                ]
        },
        "B" : {
                "city" : "BEAVER SPRINGS",
                "ZipsPerCity" : [
                        "17843",
                        "17812"
                ]
        },
        "C" : [
                "18018",
                "18015"
        ]
    }
    

    用具有共同成员的实际“集合”可以更好地说明这一点。所以这个文件:

    { "A" : [ "A", "A", "B", "C", "D" ], "B" : [ "B", "C" ] }
    

    回复$setDifference

    { "C" : [ "A", "D" ] }
    

    还有$setEquals

    { "C" : false }
    

    $setIntersection:

    { "C" : [ "B", "C" ] }
    

    $setUnion:

    { "C" : [ "B", "D", "C", "A" ] }
    

    $setIsSubSet倒序为$B$A

    { "C" : true }
    

    其他集合运算符$anyElementTrue$allElementsTrue$map 运算符一起使用可能最有用,它们可以重新调整数组的形状并针对每个元素评估条件。

    $map 的一个很好的用法是在 $setDifference 旁边,您可以在不使用 $unwind 的情况下“过滤”数组内容:

    db.arrays.aggregate([
        { "$project": {
             "A": {
                 "$setDifference": [
                     {
                         "$map": {
                             "input": "$A",
                             "as": "el",
                             "in": {
                                 "$cond": [
                                     { "$eq": [ "$$el", "A" ] },
                                     "$$el",
                                     false
                                 ]
                             }
                         }
                     },
                     [false]
                 ]
             }
        }}
    ])
    

    当您在管道中有很多结果并且您不想通过“展开”数组来“扩展”所有这些结果时,这会非常方便。但请注意,这是一个“集合”,因此只返回一个匹配“A”的元素:

    { "A" : ["A"] }
    

    所以这里要记住的是你:

    1. 一次只能在“相同”文档中操作
    2. 结果通常是“集合”,这意味着它们既是“唯一的”又是“无序的”。

    总的来说,这应该是关于set operators 是什么以及您如何使用它们的一个不错的总结。

    【讨论】:

      猜你喜欢
      • 2018-12-28
      • 1970-01-01
      • 2019-06-06
      • 2021-06-15
      • 2021-01-18
      • 1970-01-01
      • 2018-06-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多