在$ graphLookup管道阶段之后过滤结果

时间:2017-01-17 06:45:17

标签: python mongodb aggregation-framework

我的字段中的父记录和子记录相同。但是,某些字段仅设置在父级别。

设置为空字符串(“”)的父字段表示记录是父记录。其他记录具有指向父级的值集,因此可以将这些值视为子记录。

现在考虑以下记录:

{"_id": 1, parent: "", "pValue": ["a", "b", "c"], fieldA: 2},
{"_id": 2, parent: 1, "pValue": [], fieldA: 2},
{"_id": 3, parent: 1, "pValue": [], fieldA: 2},
{"_id": 4, parent: "", "pValue": ["d"], fieldA: 9},
{"_id": 5, parent: 4, "pValue": [], fieldA:2},
{"_id": 6, parent: 4,"pValue": [], fieldA: 9}

以上记录包含两个父母,每个父母都有2个相关的子记录。我正在尝试执行的查询涉及匹配两个给定的参数。首先是pValue上的值。一旦我在阵列中获得具有特定pValue的所有父母。然后,我想将该父项及其所有相关的子记录与fieldA值进行匹配。

因此,如果给定pValue =“d”且fieldA = 9,我希望光标内有以下记录:

{"_id": 4, parent: "", "pValue": ["d"], fieldA: 9}
{"_id": 6, parent: 4, "pValue": [],fieldA: 9}

备注:

  1. 每位家长可以有很多孩子与他们相关联。
  2. 父级可以为子级提供不同的fieldA值,查询应该只返回子级而不是父级
  3. 我的尝试:

     cursor=self.pCollection.aggregate([
          { "$match": {"pValue":{"$in":[pCheck]}},
          {   "$graphLookup" : {
              "from": "pCollection",
              "startWith": "$_id",
              "connectFromField": "_id",
              "connectToField": "parent",
              "as" : "children"
              }
          }])
    

    然后我被困在所有与父母关联的孩子身上作为一个数组而不知道如何将它们包裹起来。

2 个答案:

答案 0 :(得分:1)

您需要使用$match选择符合查询条件的文档,并在添加$addFields阶段时过滤“children”数组。

$filter之后,您可以使用arrayElemAt 项目分配给字段。

{ "$match": { "fieldA": 9, "children.fieldA": 9 } },
{ "$addFields": { 
    "children": {
        "$arrayElemAt": [
            { "$filter": { 
                "input": "$children",
                "as": "child",
                "cond": { "$eq": [ "$$child.fieldA", 9 ] }
            }},
            0
         ]
    }
}}

您的查询将产生如下内容:

{
    "_id" : 4,
    "parent" : "",
    "pValue" : [ "d" ],
    "fieldA" : 9,
    "children" : { "_id" : 6, "parent" : 4, "pValue" : [ ], "fieldA" : 9 }
}

然而,只要有一点信心,你就可以得到预期的结果。

db.collection.aggregate([
    { "$graphLookup": {
        "from": "collection",
        "startWith": "$_id", 
        "connectFromField": "_id",           
        "connectToField": "parent",     
        "as" : "children"
    }}, 
    { "$match": { "fieldA": 9, "children.fieldA": 9 } }, 
    { "$addFields": { 
        "children": { 
            "$arrayElemAt": [ 
                { "$filter": { 
                    "input": "$children", 
                    "as": "child", 
                    "cond": { "$eq": [ "$$child.fieldA", 9 ] } 
                }}, 
                0 
            ] 
        }
    }}, 
    { "$project": { 
        "children": [
            "$children", 
            { 
                "_id": "$_id", 
                "parent": "$parent", 
                "pValue": "$pValue", 
                "fieldA": "$fieldA" 
            }
        ]
    }}, 
    { "$unwind": "$children" }, 
    { "$replaceRoot": { "newRoot": "$children" } }
])

产生类似这样的东西:

{ "_id" : 6, "parent" : 4, "pValue" : [ ], "fieldA" : 9 }
{ "_id" : 4, "parent" : "", "pValue" : [ "d" ], "fieldA" : 9 }

坦率地说,我认为这不是你应该在你的应用程序中做的事情。第一个选择是你可以而且应该忍受的东西。

如果您真的需要这个,我建议您create a view并在您的应用程序中查询该视图。

答案 1 :(得分:0)

通过在f​​ieldA上反转查询和首次匹配,然后从父级执行graphyLookup - > _id(因此将父级映射到子级)我可以将子级关联到父级的字段A.

这是我最终的查询:

     cursor=self.collection.aggregate([
          { "$match": {"fieldA":9}},
          { "$graphLookup" : {
            "from": "collection",
            "startWith": "$parent",
            "connectFromField": "parent",
            "connectToField": "_id",
            "as" : "parents"
            }
          },
          { "$project" : {
             "pValue": { "$cond":[ {"$ne":["$pValue",[]]},"$pValue",  
                                     {"$let":{"vars": {"obj": {"$arrayElemAt": ["$parents", 0]}},
                                                               "in": "$$obj.pValue"}}]}
             }
          },
          { "$match": {"pValue":{"$in":["d"]}}}
        ])