【问题标题】:Circular refrence AWS appsync循环参考 AWS appsync
【发布时间】:2021-12-02 15:27:06
【问题描述】:

我想知道是否可以在 AWS Appsync 中进行循环引用?我已经搜索了很多但找不到它。像这样的:

type Post {
    title: String!
    content: String!
    user: User!
}

type Query {
    allPosts: [Post!]
    singlePost(id: String!): Post!
}

type User {
    name: String!
    posts: [Post!]
}

编辑(使用此处描述的逻辑尝试 lambda https://youtu.be/bgq7FRSPDpI?list=PL55RiY5tL51rG1x02Yyj93iypUuHYXcB_&t=526

这是 allPosts 的 lambda 解析器(将调用处理函数):

import * as sdk from "aws-sdk";

declare var process: {
  env: {
    TABLE_NAME: string;
  };
};

interface Event {
  info: {
    fieldName: string;
    parentTypeName: string;
    variables: Record<string, any>;
  };
}

const client = new sdk.DynamoDB.DocumentClient();

const getUser = (user_id: string): Record<string, any> | null => {
  return client
    .query({
      TableName: process.env.TABLE_NAME,
      KeyConditionExpression: "PK = :pk AND SK = :sk",
      ExpressionAttributeValues: {
        ":pk": user_id,
        ":sk": "profile",
      },
    })
    .promise()
    .then(
      (data) =>
        data.Items?.map((item) => ({
          ...item.data,
          posts: getPost.bind(null, item.PK),
        }))[0]
    )
    .catch((err) => {
      console.log(err);
      return null;
    });
};

const getPost = (user_id: string): Record<string, any> | null => {
  return client
    .query({
      TableName: process.env.TABLE_NAME,
      KeyConditionExpression: "SK = :sk AND pk = :pk",
      ExpressionAttributeValues: {
        ":pk": user_id,
        ":sk": "profile",
      },
    })
    .promise()
    .then((data) =>
      data.Items?.map((item) => ({
        ...item.data,
        user: getUser.bind(null, item.PK),
      }))
    )
    .catch((err) => {
      console.log(err);
      return null;
    });
};

export const handler = async (event: Event) => {
  if (event.info.fieldName === "allPosts") {
    const data = await client
      .query({
        TableName: process.env.TABLE_NAME,
        KeyConditionExpression: "#t = :sk",
        IndexName: "GSI",
        ProjectionExpression: "#d, PK",
        ExpressionAttributeNames: {
          "#t": "type",
          "#d": "data",
        },
        ExpressionAttributeValues: {
          ":sk": "post",
        },
      })
      .promise();
    const result = data.Items?.map((item) => ({
      ...item.data,
      user: getUser.bind(null, item.PK),
    }));
    console.log(data, result);
    return result;
  }
  return;
  //   else if (event.fieldName === "singlePost") {

  //   }
};

用户字段有一个函数,如本视频所示:https://youtu.be/bgq7FRSPDpI?list=PL55RiY5tL51rG1x02Yyj93iypUuHYXcB_&t=526

但是 lambda 响应没有返回有界函数。

[
  {
    "title": "post by user_123",
    "content": "\n\nNew to this community. I need some help in designing the Amazon Dynamo DB table for my personal projects.\n\nOverview, this is a simple photo gallery application with following attributes.\n\nUserID\nPostID\nList item\nS3URL\nCaption\nLikes\nReports\nUploadTime\nI wish to perform the following queries:\n\nFor a given user, fetch 'N' most recent posts\nFor a given user, fetch 'N' most liked posts\nGive 'N' most recent posts (Newsfeed)\nGive 'N' most liked posts (Newsfeed)\nMy solution:"
  },
  {
    "title": "another post by user_123",
    "content": "\n\nNew to this community. I need some help in designing the Amazon Dynamo DB table for my personal projects.\n\nOverview, this is a simple photo gallery application with following attributes.\n\nUserID\nPostID\nList item\nS3URL\nCaption\nLikes\nReports\nUploadTime\nI wish to perform the following queries:\n\nFor a given user, fetch 'N' most recent posts\nFor a given user, fetch 'N' most liked posts\nGive 'N' most recent posts (Newsfeed)\nGive 'N' most liked posts (Newsfeed)\nMy solution:"
  }
]

但我可以在日志中看到有界函数:

[
  {
    title: 'post by user_123',
    content: '\n' +
      '\n' +
      'New to this community. I need some help in designing the Amazon Dynamo DB table for my personal projects.\n' +
      '\n' +
      'Overview, this is a simple photo gallery application with following attributes.\n' +
      '\n' +
      'UserID\n' +
      'PostID\n' +
      'List item\n' +
      'S3URL\n' +
      'Caption\n' +
      'Likes\n' +
      'Reports\n' +
      'UploadTime\n' +
      'I wish to perform the following queries:\n' +
      '\n' +
      "For a given user, fetch 'N' most recent posts\n" +
      "For a given user, fetch 'N' most liked posts\n" +
      "Give 'N' most recent posts (Newsfeed)\n" +
      "Give 'N' most liked posts (Newsfeed)\n" +
      'My solution:',
    user: [Function: bound getUser]
  },
  {
    title: 'another post by user_123',
    content: '\n' +
      '\n' +
      'New to this community. I need some help in designing the Amazon Dynamo DB table for my personal projects.\n' +
      '\n' +
      'Overview, this is a simple photo gallery application with following attributes.\n' +
      '\n' +
      'UserID\n' +
      'PostID\n' +
      'List item\n' +
      'S3URL\n' +
      'Caption\n' +
      'Likes\n' +
      'Reports\n' +
      'UploadTime\n' +
      'I wish to perform the following queries:\n' +
      '\n' +
      "For a given user, fetch 'N' most recent posts\n" +
      "For a given user, fetch 'N' most liked posts\n" +
      "Give 'N' most recent posts (Newsfeed)\n" +
      "Give 'N' most liked posts (Newsfeed)\n" +
      'My solution:',
    user: [Function: bound getUser]
  }
]

【问题讨论】:

标签: typescript amazon-web-services aws-appsync


【解决方案1】:

TL;DR 是的,appsync 可以轻松处理嵌套或“循环”查询。关键的见解是,解析 user 字段后面的 User 类型不是 allPosts 处理程序的工作。相反,appsync 将第二次调用 lambda 解析器以获取 user 字段的 User。我们需要在 lambda 中添加分支逻辑来处理第二次调用,其中event.info.fieldName === "user"

// a switch statement inside your lambda function handler "routes" the request

switch (event.parentTypeName) {
  case "Query":
    switch (event.fieldName) {
      case "allPosts":
        // depends on your schema
        const userID = event.arguments?.["id"]
        // handle query, return [Post!], as per your schema
      case "singlePost"
        const postID = event.arguments?["id"]
        // ditto, return Post!, as per your schema
    }
  case "Post":
    switch (event.fieldName) {
      case "user":
       // event.source is a Post, details depend on your actual schema
       const userID = event.source?.["userID"]
       // make dynamo call to get the User and return a User, type that your graphql schema is expecting
    }
  case "User":
    switch (event.fieldName) {
      case "posts":
         // event.source is a User, details depend on your actual schema
        const userID = event.source?.["id"]
        // fetch posts from dynamo, return [Post!], the type your graphql schema is expecting
    }
  default:
    throw new Error("unhandled parent type")
}

上下文:这个答案和问题一样,假设我们的数据源是direct lambda resolver,这意味着我们的函数接收full context object 作为arg,并且我们不使用VTL 模板。它还假设一般默认选择是使用带有巨大 switch 语句的单个 lambda 解析器来处理各种传入请求。

可解析字段由 appsync 单独解析。可解析字段具有解析器数据源。在我们的例子中,它是一个 lambda 函数。对于 appsync 遇到的每个可解析字段,appsync 将单独调用解析器。 (顺便说一句,我的猜测是您已经User 配置了一个数据源。这将解决allPosts 不返回用户结果的奥秘。解析用户不是它的工作,而你的lambda 当前未处理 event.info.fieldNameuser)。

设置解析器 aws 为我们提供了几种将解析器分配给字段的方法,包括在控制台(架构选项卡、附加按钮)和cdk(将LambdaDataSource 添加到@ 987654325@)。

我们如何获得 user id? Appsync 为我们的 dynamodb 调用提供了 event arg for user PK/SK 信息。 event 为我们提供了很多信息,包括 fieldNameparentTypeName,最重要的是,带有父字段值的 source 键。 console.log event 来发现你必须使用的东西。

额外积分

问:appsync 会为以下查询调用多少次我们的 lambda?

query allPosts(limit: 10) {
    title
    content
    user {
        name
    }
}

答: 11 次。 allPosts 1x,user 10x。

现有技术请参阅thisthis SO 帖子以获取类似答案。

不要在家里尝试这个理论上可以删除User类型的解析器,从而将返回的责任交给allPosts(和其他查询)用户。由于查询返回值的嵌套可能很深,对于零增益来说相当复杂。

【讨论】:

  • 非常感谢!您的答案解释清楚且非常详细。我不知道字段解析器的概念,这个答案让这个概念变得轻而易举!
猜你喜欢
  • 1970-01-01
  • 2014-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-01
  • 1970-01-01
  • 2016-02-19
相关资源
最近更新 更多