

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

# GraphQL 中的界面和聯集
<a name="interfaces-and-unions"></a>

GraphQL 類型系統支援[界面](https://graphql.org/learn/schema/#interfaces)。界面會公開特定的欄位組合，類型必須包含該組合以實作界面。

GraphQL 類型系統也支援 [Unions](https://graphql.org/learn/schema/#union-types)。聯集與界面相同，但它們不會定義一組常用的欄位。當可能的類型不共用邏輯階層時，使用者通常偏好使用聯集而非界面。

下節是結構描述類型參考。

## 介面範例
<a name="interfaces"></a>

我們可以代表代表任何類型的活動或人員集合的`Event`界面。有些可能的事件類型為 `Concert`、 `Conference`和 `Festival`。這些類型都具有常見的特性，包含名稱、事件進行的地點，以及開始和結束日期。這些類型也有差異； `Conference`提供發言者和研討會的清單，而 `Concert`具有執行頻帶。

在結構描述定義語言 (SDL) 中，`Event`界面的定義如下：

```
interface Event {
        id: ID!
        name : String!
        startsAt: String
        endsAt: String
        venue: Venue
        minAgeRestriction: Int
}
```

每個類型都會實作`Event`界面，如下所示：

```
type Concert implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performingBand: String
}

type Festival implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performers: [String]
}

type Conference implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    speakers: [String]
    workshops: [String]
}
```

界面可用來代表元素，而該元素可能有幾個類型。例如，我們可以搜尋在特定會場發生的所有事件。現在讓我們在結構描述上新增 `findEventsByVenue` 欄位，如下所示：

```
schema {
    query: Query
}

type Query {
    # Retrieve Events at a specific Venue
    findEventsAtVenue(venueId: ID!): [Event]
}

type Venue {
    id: ID!
    name: String
    address: String
    maxOccupancy: Int
}

type Concert implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performingBand: String
}

interface Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
}

type Festival implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performers: [String]
}

type Conference implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    speakers: [String]
    workshops: [String]
}
```

`findEventsByVenue` 會傳回 的清單`Event`。由於 GraphQL 界面欄位對於所有實作類型而言都是共通的，因此您可以在 `Event` 界面 (`id`、`name`、`startsAt`、`endsAt`、`venue` 和 `minAgeRestriction`) 選取任何欄位。此外，您可以使用 GraphQL [片段](https://graphql.org/learn/queries/#fragments)存取任何實作類型上的欄位，只是您必須指定該類型。

讓我們檢查使用 界面的 GraphQL 查詢範例。

```
query {
  findEventsAtVenue(venueId: "Madison Square Garden") {
    id
    name
    minAgeRestriction
    startsAt

    ... on Festival {
      performers
    }

    ... on Concert {
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

上述查詢會產生一個結果清單，伺服器預設會依據開始日期排序事件。

```
{
  "data": {
    "findEventsAtVenue": [
      {
        "id": "Festival-2",
        "name": "Festival 2",
        "minAgeRestriction": 21,
        "startsAt": "2018-10-05T14:48:00.000Z",
        "performers": [
          "The Singers",
          "The Screamers"
        ]
      },
      {
        "id": "Concert-3",
        "name": "Concert 3",
        "minAgeRestriction": 18,
        "startsAt": "2018-10-07T14:48:00.000Z",
        "performingBand": "The Jumpers"
      },
      {
        "id": "Conference-4",
        "name": "Conference 4",
        "minAgeRestriction": null,
        "startsAt": "2018-10-09T14:48:00.000Z",
        "speakers": [
          "The Storytellers"
        ],
        "workshops": [
          "Writing",
          "Reading"
        ]
      }
    ]
  }
}
```

由於結果會以單一事件集合的形式傳回，因此使用界面來表示常見特性非常有助於排序結果。

## 聯集範例
<a name="unions"></a>

如前所述，聯集不會定義常見的欄位集。搜尋結果可能代表許多不同的類型。使用 `Event` 結構描述，您可以定義 `SearchResult` 聯集，如下所示：

```
type Query {
    # Retrieve Events at a specific Venue
    findEventsAtVenue(venueId: ID!): [Event]
    # Search across all content
    search(query: String!): [SearchResult]
}

union SearchResult = Conference | Festival | Concert | Venue
```

在此情況下，若要查詢聯`SearchResult`集上的任何欄位，您必須使用片段：

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
      id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

## 類型解析 in AWS AppSync
<a name="type-resolution-in-appsynclong"></a>

類型解析是一種機制，GraphQL 引擎會透過這個機制將解析值識別為特定物件類型。

返回聯集搜尋範例，假設查詢產生結果，結果清單中的每個項目都必須顯示為`SearchResult`聯集定義的可能類型之一 （即 `Conference`、`Concert`、 `Festival`或 `Venue`)。

由於識別 `Festival`、`Venue` 或 `Conference` 的邏輯取決於應用程式要求，因此必須為 GraphQL 引擎提供提示，以便從原始結果中識別我們的類型。

With AWS AppSync，此提示由名為 的中繼欄位表示`__typename`，其值對應於識別的物件類型名稱。作為界面或聯集的傳回類型`__typename`需要 。

## 類型解析範例
<a name="type-resolution-example"></a>

讓我們重複使用之前的結構描述。您可以透過瀏覽到主控台並將以下項目新增至 **Schema (結構描述)** 頁面下來執行此動作：

```
schema {
    query: Query
}

type Query {
    # Retrieve Events at a specific Venue
    findEventsAtVenue(venueId: ID!): [Event]
    # Search across all content
    search(query: String!): [SearchResult]
}

union SearchResult = Conference | Festival | Concert | Venue

type Venue {
    id: ID!
    name: String!
    address: String
    maxOccupancy: Int
}

interface Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
}

type Festival implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performers: [String]
}

type Conference implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    speakers: [String]
    workshops: [String]
}

type Concert implements Event {
    id: ID!
    name: String!
    startsAt: String
    endsAt: String
    venue: Venue
    minAgeRestriction: Int
    performingBand: String
}
```

現在將解析程式附加到 `Query.search` 欄位。在 `Resolvers`區段中，選擇**連接**，建立新的 *NONE *類型的**資料來源**，然後命名為 *StubDataSource*。在此範例中，我們將假設從外部來源擷取結果，並將擷取結果硬式編碼至請求映射範本中。

在請求映射範本窗格中，輸入以下內容：

```
{
    "version" : "2018-05-29",
    "payload":
    ## We are effectively mocking our search results for this example
    [
        {
            "id": "Venue-1",
            "name": "Venue 1",
            "address": "2121 7th Ave, Seattle, WA 98121",
            "maxOccupancy": 1000
        },
        {
            "id": "Festival-2",
            "name": "Festival 2",
            "performers": ["The Singers", "The Screamers"]
        },
        {
            "id": "Concert-3",
            "name": "Concert 3",
            "performingBand": "The Jumpers"
        },
        {
            "id": "Conference-4",
            "name": "Conference 4",
            "speakers": ["The Storytellers"],
            "workshops": ["Writing", "Reading"]
        }
    ]
}
```

如果應用程式傳回類型名稱做為 `id` 欄位的一部分，則類型解析邏輯必須剖析 `id` 欄位以擷取類型名稱，然後將 `__typename` 欄位新增至每個結果。您可以在回應映射範本中執行該邏輯，如下所示：

**注意**  
如果您使用 Lambda 資料來源，也可以在 Lambda 函數中執行此任務。

```
#foreach ($result in $context.result)
    ## Extract type name from the id field.
    #set( $typeName = $result.id.split("-")[0] )
    #set( $ignore = $result.put("__typename", $typeName))
#end
$util.toJson($context.result)
```

執行下列查詢：

```
query {
  search(query: "Madison") {
    ... on Venue {
      id
      name
      address
    }

    ... on Festival {
        id
      name
      performers
    }

    ... on Concert {
      id
      name
      performingBand
    }

    ... on Conference {
      speakers
      workshops
    }
  }
}
```

此查詢會產生下列結果：

```
{
  "data": {
    "search": [
      {
        "id": "Venue-1",
        "name": "Venue 1",
        "address": "2121 7th Ave, Seattle, WA 98121"
      },
      {
        "id": "Festival-2",
        "name": "Festival 2",
        "performers": [
          "The Singers",
          "The Screamers"
        ]
      },
      {
        "id": "Concert-3",
        "name": "Concert 3",
        "performingBand": "The Jumpers"
      },
      {
        "speakers": [
          "The Storytellers"
        ],
        "workshops": [
          "Writing",
          "Reading"
        ]
      }
    ]
  }
}
```

類型解析邏輯會依據應用程式而有不同。例如，您可以有不同的識別邏輯，以檢查特定欄位或甚至結合欄位是否存在。也就是說，您可以偵測 `performers` 欄位是否存在以識別 `Festival`，或偵測 `speakers` 與 `workshops` 欄位的組合是否存在以識別 `Conference`。最終，由您決定要使用的邏輯。