使用带有条件

时间:2018-06-14 14:05:25

标签: mongodb mongodb-query aggregation-framework

我正在努力寻找特定孩子的祖先。在某些时候,那个孩子的祖先姓氏改变了。我想找到这个具有给定姓氏的孩子的最后一个父母。例如:

{
    "_id":1,
    "parent":null
    "first":"Bob",
    "last":"Sagget"
},
{
    "_id":2,
    "parent":1,
    "first":"Jane",
    "last":"Dor"
},
{
    "_id":3,
    "parent":2,
    "first":"Crane",
    "last":"Dor"
},
{
    "_id":4,
    "parent":3,
    "first":"Ho",
    "last":"Dor"
},
{
    "_id":5,
    "parent":4,
    "first":"Mor",
    "last":"Dor"
}

我想查询_id 5 并获取姓氏为 Dor 的祖先。在这个数据库中会有其他名为Dor的人,我不想看到,所以我不能只查询“Dor”的姓氏。

这是我当前的聚合查询 - 这是给我每个祖先一直到_id的1.我怎么能停在_id的2?:

db.PeopleDb.aggregate(
[
    {
        $graphLookup: {
                "from": "PeopleDb", 
                "startWith": "$parent", 
                "connectFromField": "parent", 
                "connectToField": "_id", 
                "as": "linearAncestors"
            }
        },
        {
        $match: {
        "_id":5
        }
    },
]);

1 个答案:

答案 0 :(得分:2)

您正在寻找的基本查询实际上是#34;启动"限制仅将单数文档与_id: 5匹配,然后执行$graphLookup以查找祖先。

关于"条件",涉及一些步骤,因此最好遍历整个过程并了解发生的事情:

db.PeopleDb.aggregate([
  { "$match": { "_id": 5 } },
  { "$graphLookup": {
    "from": "PeopleDb",
    "startWith": "$parent",
    "connectFromField": "parent",
    "connectToField": "_id",
    "as": "people",
    "depthField": "depth"
  }}
])

然后返回将parent连接到_id的所有递归链,并注意"可选" "depthField"设置为包含实际的"深度"返回结果中的匹配项:

{
    "_id" : 5,
    "parent" : 4,
    "first" : "Mor",
    "last" : "Dor",
    "people" : [
        {
            "_id" : 1,
            "parent" : null,
            "first" : "Bob",
            "last" : "Sagget",
            "depth" : NumberLong(3)
        },
        {
            "_id" : 2,
            "parent" : 1,
            "first" : "Jane",
            "last" : "Dor",
            "depth" : NumberLong(2)
        },
        {
            "_id" : 3,
            "parent" : 2,
            "first" : "Crane",
            "last" : "Dor",
            "depth" : NumberLong(1)
        },
        {
            "_id" : 4,
            "parent" : 3,
            "first" : "Ho",
            "last" : "Dor",
            "depth" : NumberLong(0)
        }
    ]
}

因此,请注意4已返回,因为它是初始文档的父级,然后3为其父级,然后是2,因为它是父级等等。

您可以限制"深度"使用"可选"进行匹配"maxDepth"参数指向管道阶段,或者在"特定情况下除了ROOT"您可以使用"restrictSearchWithMatch"选项简单地使用null父级排除结果:

db.PeopleDb.aggregate([
  { "$match": { "_id": 5 } },
  { "$graphLookup": {
    "from": "PeopleDb",
    "startWith": "$parent",
    "connectFromField": "parent",
    "connectToField": "_id",
    "as": "people",
    "depthField": "depth",
    "restrictSearchWithMatch": { "parent": { "$ne": null } }
  }}
])

返回相同的结果,但不包括" ROOT"记录"parent"字段为null的位置:

{
    "_id" : 5,
    "parent" : 4,
    "first" : "Mor",
    "last" : "Dor",
    "people" : [
        {
            "_id" : 2,
            "parent" : 1,
            "first" : "Jane",
            "last" : "Dor",
            "depth" : NumberLong(2)
        },
        {
            "_id" : 3,
            "parent" : 2,
            "first" : "Crane",
            "last" : "Dor",
            "depth" : NumberLong(1)
        },
        {
            "_id" : 4,
            "parent" : 3,
            "first" : "Ho",
            "last" : "Dor",
            "depth" : NumberLong(0)
        }
    ]
}

当然,同样的原则适用于您的"last"条件,该条件只能匹配条件为真的文档。在这里,我将显示两个条件,但$or是可选的:

db.PeopleDb.aggregate([
  { "$match": { "_id": 5 } },
  { "$graphLookup": {
    "from": "PeopleDb",
    "startWith": "$parent",
    "connectFromField": "parent",
    "connectToField": "_id",
    "as": "people",
    "depthField": "depth",
    "restrictSearchWithMatch": {
      "$or": [
        { "parent": { "$ne": null }},
        { "last": "Dor" }
      ]
    }
  }}
])

注意然而"restrictSearchWithMatch"是"递归"条件,因此,如果有任何"祖先"在链中不符合"last"条件,则链已断开,并且不会检索到更多祖先。为了得到所有的祖先"但只显示匹配"last"然后您$filter生成的数组内容的人:

db.PeopleDb.aggregate([
  { "$match": { "_id": 5 } },
  { "$graphLookup": {
    "from": "PeopleDb",
    "startWith": "$parent",
    "connectFromField": "parent",
    "connectToField": "_id",
    "as": "people",
    "depthField": "depth",
  }},
  { "$addFields": {
    "people": {
      "$filter": {
        "input": "$people",
        "cond": { "$eq": [ "$$this.last", "Dor" ] }
      }
    }
  }}
])

或实际上"动态"通过比较"祖先"的初始匹配文档的值。与使用字段值表达式而不是硬编码值相关:

db.PeopleDb.aggregate([
  { "$match": { "_id": 5 } },
  { "$graphLookup": {
    "from": "PeopleDb",
    "startWith": "$parent",
    "connectFromField": "parent",
    "connectToField": "_id",
    "as": "people",
    "depthField": "depth",
  }},
  { "$addFields": {
    "people": {
      "$filter": {
        "input": "$people",
        "cond": { "$eq": [ "$$this.last", "$last" ] }
      }
    }
  }}
])

在这种情况下,它是相同的2,3,4祖先,但如果说祖先3实际上具有"last"的不同值,则使用$filter实际上会返回2,4,而"restrictSearchWithMatch"只返回4,因为3会打破链#34;这是主要区别。

  

N.B 使用$graphLookup你不能做的一件事是"字段比较表达"不被允许。如果你想要沿着这些方向做某些事情,那么你可以使用$filter进行进一步的操作,如果你的实际意图确实是为了打破链条,那么可能会进行$indexOfArray等其他操作。在那个"字段比较"在递归搜索中没有得到满足。