

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# $graphLookup
<a name="graphLookup"></a>

8.0.2 版的新功能。

Amazon DocumentDB `$graphLookup` 中的彙總階段會對集合執行遞迴搜尋。對於每個輸入文件， 會從一或多個值`$graphLookup`開始，並重複比對連線欄位與目標欄位，遵循相符文件鏈，直到沒有更多相符項目或達到選用的遞迴深度為止。這適用於周遊類似圖形或階層的資料，例如組織報告結構、類別樹或網路關係。

**參數**
+ `from`：遞迴搜尋的目標集合。`from` 集合必須位於相同的資料庫中。
+ `startWith`：指定用來開始遞迴搜尋之值的表達式。此值會與`from`集合中文件`connectToField`的 進行比較。如果 `startWith`評估為 陣列，則搜尋會同時從所有陣列元素執行。
+ `connectFromField`：其值用於連線到遞迴搜尋中下一個文件的欄位名稱。如果該值是陣列，則每個元素都會透過周遊個別追蹤。
+ `connectToField`：`from`集合中與 值相符的欄位名稱`connectFromField`。
+ `as`：新增至每個輸出文件的陣列欄位名稱。此欄位包含在遞迴搜尋期間找到的文件。如需這些文件順序的資訊，請參閱下列行為區段中的結果排序項目。
+ `maxDepth`：非負整數，指定初始查詢後要執行的遞迴反覆運算數目上限。此為選用參數。的值只會`0`執行初始比對，不會遞迴。如果您省略 `maxDepth`，搜尋會持續遞迴搜尋，直到沒有更多相符的文件為止。
+ `depthField`：要新增至`as`陣列中每個文件的欄位名稱，該文件包含找到文件的遞迴深度。此為選用參數。深度以 表示`NumberLong`，從零開始，因此初始查詢對應至深度 `0`。
+ `restrictSearchWithMatch`：指定套用至搜尋文件之其他比對條件的文件。此為選用參數。條件使用與`$match`階段相同的查詢語法，且不能包含彙總表達式。

**注意**  
`$graphLookup` 階段受限於 100 MB 的記憶體限制，且不支援 `allowDiskUse:true`。超過此限制的操作會傳回錯誤。如果彙總包含其他階段，則這些階段`allowDiskUse:true`的選項仍然有效。

**行為**
+ **跨集合周遊**：`from`設定為您要搜尋的集合。這可以是管道執行的相同集合，或相同資料庫中的不同集合。
+ **結果排序**：`as`陣列中的文件不會以任何保證順序傳回。

## 範例 (MongoDB Shell)
<a name="graphLookup-examples"></a>

下列範例使用 `$graphLookup`來周遊員工報告階層，並針對每個員工傳回上述的完整經理鏈。

下列命令會在 mongosh shell 中執行，插入建立報告階層的範例員工文件：

```
db.employees.insertMany([
  { _id: 1, name: "Dev", reportsTo: "Eliot" },
  { _id: 2, name: "Eliot", reportsTo: "Ron" },
  { _id: 3, name: "Ron", reportsTo: "Andrew" },
  { _id: 4, name: "Andrew", reportsTo: "Asya" },
  { _id: 5, name: "Asya" }
]);
```

下列彙總會傳回新`reportingHierarchy`陣列中每個員工的完整管理鏈：

```
db.employees.aggregate([
  {
    $graphLookup: {
      from: "employees",
      startWith: "$reportsTo",
      connectFromField: "reportsTo",
      connectToField: "name",
      as: "reportingHierarchy",
      depthField: "level"
    }
  }
]);
```

此操作會傳回下列輸出：

```
[
  {
    _id: 1,
    name: 'Dev',
    reportsTo: 'Eliot',
    reportingHierarchy: [
      { _id: 2, name: 'Eliot', reportsTo: 'Ron', level: 0 },
      { _id: 3, name: 'Ron', reportsTo: 'Andrew', level: 1 },
      { _id: 4, name: 'Andrew', reportsTo: 'Asya', level: 2 },
      { _id: 5, name: 'Asya', level: 3 }
    ]
  },
  {
    _id: 2,
    name: 'Eliot',
    reportsTo: 'Ron',
    reportingHierarchy: [
      { _id: 3, name: 'Ron', reportsTo: 'Andrew', level: 0 },
      { _id: 4, name: 'Andrew', reportsTo: 'Asya', level: 1 },
      { _id: 5, name: 'Asya', level: 2 }
    ]
  },
  {
    _id: 3,
    name: 'Ron',
    reportsTo: 'Andrew',
    reportingHierarchy: [
      { _id: 4, name: 'Andrew', reportsTo: 'Asya', level: 0 },
      { _id: 5, name: 'Asya', level: 1 }
    ]
  },
  {
    _id: 4,
    name: 'Andrew',
    reportsTo: 'Asya',
    reportingHierarchy: [
      { _id: 5, name: 'Asya', level: 0 }
    ]
  },
  {
    _id: 5,
    name: 'Asya',
    reportingHierarchy: []
  }
]
```

每個員工文件都會取得陣列`reportingHierarchy`，其中包含遞迴比對的管理員文件。`level` 欄位會記錄找到每個管理員的遞迴深度。Asya 沒有`reportsTo`值，因此她的`reportingHierarchy`陣列是空的。

## maxDepth 和 restrictSearchWithMatch (MongoDB Shell) 的範例
<a name="graphLookup-filter-depth-example"></a>

下列範例會周遊人員與 之間的連線網路`maxDepth`，以限制遞迴`restrictSearchWithMatch`，並僅包含符合其他條件的文件。搜尋會尋找起始人員的連線，最多兩個層級深度，只保留`hobbies`陣列包含 的人員`golf`。

下列命令在 mongosh shell 中執行，插入範例文件，其中每個人的`friends`點都透過單向連線鏈向前：

```
db.people.insertMany([
  { _id: 1, name: "Tanya", friends: [ "Carole", "Shirley" ], hobbies: [ "tennis", "golf" ] },
  { _id: 2, name: "Carole", friends: [ "Joseph" ], hobbies: [ "archery", "golf" ] },
  { _id: 3, name: "Joseph", friends: [ "Angelo" ], hobbies: [ "tennis", "golf" ] },
  { _id: 4, name: "Angelo", friends: [ "Miguel" ], hobbies: [ "travel", "golf" ] },
  { _id: 5, name: "Miguel", friends: [], hobbies: [ "golf" ] },
  { _id: 6, name: "Shirley", friends: [], hobbies: [ "frisbee" ] }
]);
```

下列彙總從 Tanya 開始，會周遊她的連線網路最多兩個層級深度，並僅保留列出`golf`為愛好的人員：

```
db.people.aggregate([
  { $match: { name: "Tanya" } },
  {
    $graphLookup: {
      from: "people",
      startWith: "$friends",
      connectFromField: "friends",
      connectToField: "name",
      as: "golfConnections",
      maxDepth: 2,
      restrictSearchWithMatch: { hobbies: "golf" }
    }
  },
  { $project: { name: 1, "golfConnections.name": 1 } }
]);
```

此操作會傳回下列輸出：

```
[
  {
    _id: 1,
    name: 'Tanya',
    golfConnections: [
      { name: 'Carole' },
      { name: 'Joseph' },
      { name: 'Angelo' }
    ]
  }
]
```

只有`hobbies`陣列包含 的連線`golf`才會包含在內，而周遊會在兩個遞迴層級後停止。Shirley 會被排除，因為她未列出 `golf`，而 Miguel 會被排除，因為他離 Tanya 有三個關卡，超過 `maxDepth`的 `2`。由於連線形成的單向鏈永遠不會指向 Tanya，因此開始文件在遞迴搜尋期間不相符。

## 程式碼範例
<a name="graphLookup-code"></a>

若要檢視使用 MongoDB 驅動程式從第一個範例建置員工報告階層的程式碼範例，請選擇您要使用的語言標籤：

------
#### [ Node.js ]

```
const { MongoClient } = require('mongodb');

async function example() {
  const client = new MongoClient('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false');

  try {
    await client.connect();
    const db = client.db('test');
    const employees = db.collection('employees');

    const result = await employees.aggregate([
      {
        $graphLookup: {
          from: 'employees',
          startWith: '$reportsTo',
          connectFromField: 'reportsTo',
          connectToField: 'name',
          as: 'reportingHierarchy',
          depthField: 'level'
        }
      }
    ]).toArray();

    console.log(JSON.stringify(result, null, 2));
  } catch (err) {
    console.error('Failed to run $graphLookup aggregation:', err.message);
  } finally {
    await client.close();
  }
}

example();
```

------
#### [ Python ]

```
from pymongo import MongoClient
from pymongo.errors import PyMongoError

def example():
    client = MongoClient('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false')

    try:
        db = client['test']
        employees = db['employees']

        pipeline = [
            {
                "$graphLookup": {
                    "from": "employees",
                    "startWith": "$reportsTo",
                    "connectFromField": "reportsTo",
                    "connectToField": "name",
                    "as": "reportingHierarchy",
                    "depthField": "level"
                }
            }
        ]

        result = list(employees.aggregate(pipeline))

        for doc in result:
            print(doc)
    except PyMongoError as error:
        print(f"Failed to run $graphLookup aggregation: {error}")
    finally:
        client.close()

example()
```

------