如何为具有关系的对象创建 AppSync DynamoDB 解析器

How to create AppSync DynamoDB resolver for object with relationships

我有一个 DynamoDB table,它有 GamesPlayers。我目前有以下适用于我的 AppSync getGame 查询的 Lambda 解析器。问题是,是否可以使用执行相同操作的速度模板编写 DynamoDB 解析器,这样我就可以避免 lambda 调用。

const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
    let result = null;
    let params;
    switch(event.field) {
        case "getGame":
            const id = event.arguments.id;
            if (!id) {
                throw new Error('Missing game id');
            };
            params = {
                TableName: 'games',
                KeyConditionExpression: 'pk = :pk AND sk = :sk',
                ExpressionAttributeValues: {
                  ':pk': 'game',
                  ':sk': `meta_${id}`
                }
            };
            const game = (await docClient.query(params).promise()).Items[0];

            // get players
            const gameKey = `game_${game.sk.split('_')[1]}_${game.sk.split('_')[2]}`;            
            params = {
                TableName: 'games',
                KeyConditionExpression: 'pk = :pk AND begins_with(sk, :sk)',
                ExpressionAttributeValues: {
                    ':pk': gameKey,
                    ':sk': 'player_'
                }
            };
            game.players = (await docClient.query(params).promise()).Items;
            result = game;
            break;            
    }
    return result;
};

结果看起来像

{
  "gsipk": "NEW_OPEN",
  "sk": "meta_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
  "gsisk": "level_1_players_4",
  "pk": "game",
  "players": [
    {
      "gsipk": "player_3a7bb19c-0ccd-42df-a606-acd8b1f5e288",
      "gsisk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "points": 0,
      "num": 4,
      "place": null,
      "sk": "player_3a7bb19c-0ccd-42df-a606-acd8b1f5e288",
      "pieces": [],
      "wilds": 0,
      "pk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "color": "gold",
      "pows": 0
    },
    {
      "gsipk": "player_96b772b1-4127-43da-b550-029d5c632675",
      "gsisk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "points": 0,
      "num": 2,
      "place": null,
      "sk": "player_96b772b1-4127-43da-b550-029d5c632675",
      "pieces": [],
      "wilds": 0,
      "pk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "color": "blue",
      "pows": 0
    },
    {
      "gsipk": "player_9d30c675-930f-401b-ac5f-8db32bb2acb8",
      "gsisk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "points": 0,
      "num": 3,
      "place": null,
      "sk": "player_9d30c675-930f-401b-ac5f-8db32bb2acb8",
      "pieces": [],
      "wilds": 0,
      "pk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "color": "green",
      "pows": 0
    },
    {
      "gsipk": "player_ab179ad1-a160-44f8-b438-0e93385b6c47",
      "gsisk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "points": 0,
      "num": 1,
      "place": null,
      "sk": "player_ab179ad1-a160-44f8-b438-0e93385b6c47",
      "pieces": [],
      "wilds": 0,
      "pk": "game_1578241126110_35660fcc-3cde-4d30-9ebd-09abba1aedf7",
      "color": "red",
      "pows": 0
    }
  ]
}

除非您重新映射数据以便能够在一个请求中获取所有项目,否则您将需要 Pipeline resolvers.。总而言之,管道是一些解析器,它们被包裹在 before/after 模板中。

在您的情况下,这些 before/after 模板并未真正使用,因此基本设置为:

在模板之前(不需要任何东西,所以一个空的 json 就可以了)

{}

模板之后(传递之前调用的结果)

$util.toJson($ctx.result)

那么您将拥有 2 个 DynamoDB 解析器。这些与您之前可能完成的其他 DynamoDB 解析器相同,除了在第二个中,为了访问第一个 DynamoDB 解析器结果,您将使用 $ctx.prev.result。因此,假设您在第一次调用的响应中将游戏 ID 传递为:

{
   "game_id": "$ctx.result.get('theGameId')",
    ...
}

然后可以在第二个请求模板中访问 $ctx.prev.result.game_id。您也可以使用存储代替 - $ctx.stash.put()$ctx.prev.get()。如果您需要在 BEFORE 请求中执行某些操作(我们现在留空的第一个请求)并将其传递给整个解析器,则存储很有用。

好的,感谢@cyberwombat 的评论:

Unless you remap your data to be able to fetch all items in one request

我想通了。首先,我不得不重构我的 table 一点。我将主键(哈希)更改为 game_<uuid>,然后使用 meta_<timestamp> 等排序键(范围)引用游戏详细信息,并使用 player_<uuid> 等排序键引用玩家。

一旦我这样做了,我就能够使用这个解析器来查询和 return 游戏详细信息以及所有具有此请求映射模板的玩家:

{
    "version": "2017-02-28",
    "operation": "Query",
    "query" : {
        "expression" : "pk = :pk",
        "expressionValues" : {
            ":pk": { "S":  "$ctx.arguments.pk" }
        }
    }
}

以上查询returns 5 个项目(4 个玩家和游戏元数据)。然后我使用了这样的响应映射模板:

#set($game = {})
#set($players = [])
#foreach($item in $ctx.result.items)
    #if($util.matches('meta_\d*', $item.sk))
        ## main game object
        #set($game = $item)
    #else
        ## player
        $util.qr($players.add($item))
    #end
#end

$util.qr($game.put("players", $players))
$util.toJson($game)

现在我有一个 DynamoDB 的单一查询,没有 lambda 解析器...很漂亮。