【问题标题】:MongoDB $lookup (aggregation): Put multiple matches into array rather than create multiple documents?MongoDB $lookup(聚合):将多个匹配项放入数组而不是创建多个文档?
【发布时间】:2022-01-13 20:06:40
【问题描述】:

在 Python 3.9 中使用 Ubuntu 21.04、MongoDB Community 4.4.9、pymongo:

我正在将两个集合中的数据合并到一个共享密钥 membershipNumber 上。 membershipNumber 与另一个集合中的不同用户级标识符 an_user_id 相关联,并且应该是唯一的。但是,在许多情况下,单个 membershipNumber 有 n 个 an_user_ids。现在,这意味着我有很多重复的 membershipNumbers,导致出现重复的文档,其中除了 an_user_id 之外的所有内容在我新创建的集合中都是相同的。

为了规避这个问题,我希望发生以下情况:

  • 只要有 >1 个an_user_ids 与给定的membershipNumber 匹配,我想创建一个数组,其中包含与新创建的membershipNumber 匹配的ALL an_user_ids收集(使用$out
  • 这样,集合中的每个membershipNumber 都是唯一的。

关于其实用性的一个问题仍然存在:这是否意味着我可以将通过an_user_id 链接的$merge$insert 数据以及从不同的集合/聚合链接到这个新创建的集合?

任何帮助将不胜感激。谢谢!

我拥有的工作代码(但不能防止重复):

p = [
    {
        '$project' : {
            '_id' : 0,
            'membershipNumber' : 1,
            'address' : 1,
            'joinDate' : 1,
            'membershipType' : 1
        }
    },
    # THE JOIN!!    
    {
        '$lookup': {
            'from': "an_users", # the other collection
            'localField' :  'membershipNumber',
            'foreignField' : 'memref',
            'as': "details"
        }
    },
    # retain unmatchable cases    
    {
        '$unwind' : {
            'path' : '$details',
            'preserveNullAndEmptyArrays' : True
        } 
    },
    {
        '$project' : {
            '_id' : 0,
            'membershipNumber' : 1,
            'home' : 1,
            'joinDate' : 1,
            'membershipType' : 1,
            'an_user_id' : '$details.user_id',
        }
    },
    {
        '$out' : {
            'db' : 'mydb',
            'coll' : 'new_coll'
        }
    }
]

members.aggregate(pipeline=p)

这就是(不需要的)重复数据在新集合中的样子:

{
    "_id": 1,
    "membershipNumber": "123456",
    "membershipType": "STD",
    "home: "Hogwarts",
    "joinDate": {
        "$date": "2000-01-01T00:00:00.000Z"
    },
    "an_user_id": "12345"
},
{
    "_id": 2,
    "membershipNumber": "123456",
    "membershipType": "STD",
    "home": "Hogwarts"
    "joinDate": {
        "$date": "2000-01-01T00:00:00.000Z"
    },
    "an_user_id": "12346"
}

这就是我喜欢它的样子...

{
    "_id": 1,
    "membershipNumber": "123456",
    "membershipType": "STD",
    "home": "Hogwarts"
    "joinDate": {
        "$date": "2000-01-01T00:00:00.000Z"
    },
    "an_user_id": ["12345", "12346"] 
}

【问题讨论】:

  • 为什么不保持$lookup 输出不变而不是$unwind
  • 这不完全是您寻求的输出格式,但它会生成一个包含异物的对象数组并避免$unwind

标签: python mongodb join aggregation-framework pymongo


【解决方案1】:

不完全确定$out 是如何在这里有条件地发挥作用的,但给出如下两个集合:

db.foo.insert([
    {_id:1, membershipNumber: 1, type: "STD"},
    {_id:3, membershipNumber: 5, type: "STD"},
    {_id:8, membershipNumber: 8, type: "STD"}
]);

db.foo2.insert([
    {_id:1, memref: 1, an_user_id: 1},
    {_id:2, memref: 1, an_user_id: 2},
    {_id:3, memref: 1, an_user_id: 3},
    {_id:4, memref: 5, an_user_id: 5}
    // No lookup for memref 8, just to test                                                            
]);

然后这个管道产生目标输出。不需要初始的$project

db.foo.aggregate([
    // Call the join field "an_user_id" because we are going to OVERWRITE                              
    // it in the next stage.  This avoids creating extra fields that we will                           
    // want to $unset later to minimize clutter.                                                       
    {$lookup: {from: "foo2",
               localField: "membershipNumber",
               foreignField: "memref",
               as: "an_user_id"}}

    // Turn the big array of objects into an array of just an_user_id:                                 
    ,{$addFields: {an_user_id: {$map: {
                                input: "$an_user_id",
                                in: "$$this.an_user_id"
                                }}
    }}
]);

【讨论】:

  • 嗨。删除了两个旧的 cmets,因为它们都不准确......所以:这很好,我已经能够获得一个包含所有可用 an_user_ids 的数组。杰出的。但是,如果有例如2 个an_user_id 匹配给定的membershipNumber,我还剩下2 个相同的文档(_id 字段除外)。有没有办法可以在同一个聚合管道中删除这些重复的文档?
  • 我不明白给定的 membershipNumber$lookup 放入另一个集合中如何产生 2 个相同的文档。它应该是 1:n 查找。
猜你喜欢
  • 1970-01-01
  • 2017-10-08
  • 2015-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多