

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

# 在 中結合 GraphQL 解析程式 AWS AppSync
<a name="tutorial-combining-graphql-resolvers"></a>

**注意**  
我們現在主要支援 APPSYNC\$1JS 執行期及其文件。請考慮[在此處](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)使用 APPSYNC\$1JS 執行期及其指南。

GraphQL 結構描述中的解析程式與欄位具有一對一的關係，以及高度的彈性。由於資料來源是在解析程式上與結構描述個別獨立設定，因此您可以透過不同的資料來源來解析或操縱 GraphQL 的類型，對結構描述進行混合和匹配，最大程度地滿足您的需求。

下列範例案例示範如何在結構描述中混合和比對資料來源。開始之前，我們建議您熟悉設定 Amazon DynamoDB 和 Amazon OpenSearch Service AWS Lambda的資料來源和解析程式，如先前教學所述。

## 範例結構描述
<a name="example-schema"></a>

下列結構描述的類型為 `Post`，已定義 3 個`Query`操作和 3 個`Mutation`操作：

```
type Post {
    id: ID!
    author: String!
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    version: Int!
}

type Query {
    allPost: [Post]
    getPost(id: ID!): Post
    searchPosts: [Post]
}

type Mutation {
    addPost(
        id: ID!,
        author: String!,
        title: String,
        content: String,
        url: String
    ): Post
    updatePost(
        id: ID!,
        author: String!,
        title: String,
        content: String,
        url: String,
        ups: Int!,
        downs: Int!,
        expectedVersion: Int!
    ): Post
    deletePost(id: ID!): Post
}
```

在此範例中，總共要連結 6 個解析程式。其中一種可能的方法是讓所有這些都來自稱為 的 Amazon DynamoDB 資料表`Posts`，其中 `AllPosts`會執行掃描並`searchPosts`執行查詢，如 [DynamoDB 解析程式映射範本參考](resolver-mapping-template-reference-dynamodb.md#aws-appsync-resolver-mapping-template-reference-dynamodb)中所述。不過，還有其他方法可以滿足您的業務需求，例如從 Lambda 或 OpenSearch Service 解析這些 GraphQL 查詢。

## 透過解析程式來修改資料
<a name="alter-data-through-resolvers"></a>

您可能需要將資料庫的結果傳回給用戶端，例如 DynamoDB （或 Amazon Aurora)，其中某些屬性已變更。這可能是資料類型的格式所造成 (例如用戶端上時間戳記的差異)，或是為了處理回溯相容性的問題。基於說明目的，在下列範例中， AWS Lambda 函數會在每次叫用 GraphQL 解析程式時指派隨機數字，藉此操控部落格文章的向上和向下投票：

```
'use strict';
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();

exports.handler = (event, context, callback) => {
    const payload = {
        TableName: 'Posts',
        Limit: 50,
        Select: 'ALL_ATTRIBUTES',
    };

    dynamo.scan(payload, (err, data) => {
        const result = { data: data.Items.map(item =>{
            item.ups = parseInt(Math.random() * (50 - 10) + 10, 10);
            item.downs = parseInt(Math.random() * (20 - 0) + 0, 10);
            return item;
        }) };
        callback(err, result.data);
    });
};
```

這是一個非常有效的 Lambda 函式，可以連結至 GraphQL 結構描述中的 `AllPosts` 欄位，如此，任何傳回所有結果的查詢，都會取得支持票和不支持票票數的隨機數量。

## DynamoDB 和 OpenSearch Service
<a name="ddb-and-es"></a>

對於某些應用程式，您可以對 DynamoDB 執行變動或簡單查詢，並具有背景程序傳輸文件到 OpenSearch Service。然後，您只需將 `searchPosts` Resolver 連接至 OpenSearch Service 資料來源，並使用 GraphQL 查詢傳回搜尋結果 （來自源自 DynamoDB 的資料）。在為您的應用程式加入進階的搜尋操作時 (例如關鍵字詞、模糊字詞比對或甚至地理空間查詢)，這可以是非常強大的功能。從 DynamoDB 傳輸資料可以透過 ETL 程序完成，或者您也可以使用 Lambda 從 DynamoDB 串流。您可以使用您 AWS 帳戶中美國西部 2 （奧勒岡） 區域的下列 AWS CloudFormation 堆疊來啟動完整的範例：

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/multipledatasource/appsyncesdbstream.yml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/multipledatasource/appsyncesdbstream.yml) 

此範例中的結構描述可讓您使用 DynamoDB 解析程式新增文章，如下所示：

```
mutation add {
    putPost(author:"Nadia"
        title:"My first post"
        content:"This is some test content"
        url:"https://aws.amazon.com/appsync/"
    ){
        id
        title
    }
}
```

這會將資料寫入 DynamoDB，然後透過 Lambda 將資料串流至 Amazon OpenSearch Service，您可以依不同的欄位搜尋所有文章。例如，由於資料位於 Amazon OpenSearch Service 中，因此您可以使用自由格式文字搜尋作者或內容欄位，即使是空格，如下所示：

```
query searchName{
    searchAuthor(name:"   Nadia   "){
        id
        title
        content
    }
}

query searchContent{
    searchContent(text:"test"){
        id
        title
        content
    }
}
```

由於資料是直接寫入 DynamoDB，因此您仍然可以使用 `allPosts{...}`和 `singlePost{...}`查詢對資料表執行有效的清單或項目查詢操作。此堆疊針對 DynamoDB 串流使用以下範例程式碼：

 **注意**：這些程式碼僅為示範。

```
var AWS = require('aws-sdk');
var path = require('path');
var stream = require('stream');

var esDomain = {
    endpoint: 'https://opensearch-domain-name.REGION.es.amazonaws.com',
    region: 'REGION',
    index: 'id',
    doctype: 'post'
};

var endpoint = new AWS.Endpoint(esDomain.endpoint)
var creds = new AWS.EnvironmentCredentials('AWS');

function postDocumentToES(doc, context) {
    var req = new AWS.HttpRequest(endpoint);

    req.method = 'POST';
    req.path = '/_bulk';
    req.region = esDomain.region;
    req.body = doc;
    req.headers['presigned-expires'] = false;
    req.headers['Host'] = endpoint.host;

    // Sign the request (Sigv4)
    var signer = new AWS.Signers.V4(req, 'es');
    signer.addAuthorization(creds, new Date());

    // Post document to ES
    var send = new AWS.NodeHttpClient();
    send.handleRequest(req, null, function (httpResp) {
        var body = '';
        httpResp.on('data', function (chunk) {
            body += chunk;
        });
        httpResp.on('end', function (chunk) {
            console.log('Successful', body);
            context.succeed();
        });
    }, function (err) {
        console.log('Error: ' + err);
        context.fail();
    });
}

exports.handler = (event, context, callback) => {
    console.log("event => " + JSON.stringify(event));
    var posts = '';

    for (var i = 0; i < event.Records.length; i++) {
        var eventName = event.Records[i].eventName;
        var actionType = '';
        var image;
        var noDoc = false;
        switch (eventName) {
            case 'INSERT':
                actionType = 'create';
                image = event.Records[i].dynamodb.NewImage;
                break;
            case 'MODIFY':
                actionType = 'update';
                image = event.Records[i].dynamodb.NewImage;
                break;
            case 'REMOVE':
            actionType = 'delete';
                image = event.Records[i].dynamodb.OldImage;
                noDoc = true;
                break;
        }

        if (typeof image !== "undefined") {
            var postData = {};
            for (var key in image) {
                if (image.hasOwnProperty(key)) {
                    if (key === 'postId') {
                        postData['id'] = image[key].S;
                    } else {
                        var val = image[key];
                        if (val.hasOwnProperty('S')) {
                            postData[key] = val.S;
                        } else if (val.hasOwnProperty('N')) {
                            postData[key] = val.N;
                        }
                    }
                }
            }

            var action = {};
            action[actionType] = {};
            action[actionType]._index = 'id';
            action[actionType]._type = 'post';
            action[actionType]._id = postData['id'];
            posts += [
                JSON.stringify(action),
            ].concat(noDoc?[]:[JSON.stringify(postData)]).join('\n') + '\n';
        }
    }
    console.log('posts:',posts);
    postDocumentToES(posts, context);
};
```

然後，您可以使用 DynamoDB 串流將其連接到主索引鍵為 的 DynamoDB 資料表`id`，而 DynamoDB 來源的任何變更都會串流到您的 OpenSearch Service 網域。如需進行這項設定的詳細資訊，請參閱 [DynamoDB 串流文件](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.Lambda.html)。