Amplify AppSync:自定义排序和分页过滤

Amplify AppSync: custom sorting and filtering with pagination

我正在尝试编写一个架构,以便我可以查询按多个键过滤、按自定义键排序并分页的模型。

我的模型示例:

type Article {
  id: ID!
  category: String!
  area: String!
  publishOn: AWSDate!
}

我想做的查询示例是:检索属于给定 categoryarea、return 的所有 ArticlespublishOn 降序排列,每块 10 个项目(以实现服务器端分页,并具有轻量级 UI)。 响应还应包括 nextToken 属性,该属性可用于加载已过滤文章列表的“下一页”。

我在使用自动生成的模式可以做什么时遇到了多个问题,并且无法找到一种方法来手动实施适用于我想做的所有事情的解决方案。我尝试列出问题所在:

  1. 过滤

假设我要查询属于“假期”类别的 10 篇文章:

listArticles(filter: {category: {eq: "Holiday} }, limit: 10)

我不会获得与该类别匹配的前 10 篇文章,相反,AppSync 似乎会选择 table 中的前 10 篇文章,然后按筛选条件筛选这 10 篇文章. 换句话说,似乎应用过滤和排序的顺序与预期相反。预期:首先通过过滤条件过滤 table,然后 return 过滤结果集的前 10 项。

  1. 正在排序

我找不到使用 AppSync 添加排序的方法,所以我添加了可搜索:

type Article (
  @searchable
) {
  id: ID!
  category: String!
  area: String!
  publishOn: AWSDate!
}

现在,如果我按日期排序,该键将用作 nextToken 并停止分页。这是一个已知问题:https://github.com/aws-amplify/amplify-cli/issues/4434

关于如何找到解决这些错误的方法,您有什么好的建议吗?我仔细研究了文档和几个问题,但没有想出一个行之有效的解决方案...

提前致谢,

马泰奥

  1. 过滤

您将需要一个 Global Secondary Index in DynamoDB to achieve such a behaviour. You can create them with the @key 注释。根据您的情况,我会创建一个复合键,其中包含分区键的 categoryareapublishOn 作为排序键。

type Article 
@model
@key(fields: ["id"])
@key(name: "byCategory", fields: ["category", "publishOn"])
@key(name: "byCategoryArea", fields: ["category", "area", "publishOn"])
{
  id: ID!
  category: String!
  area: String!
  publishOn: AWSDate!
}
  1. 排序

排序由 sortDirection 属性 完成,它是 DESCASC,并且只能在排序键上完成。

@searchable 指令在 table 上启用 elasticsearch,这是一个全文搜索引擎,对于小型应用程序来说可能有点贵,除非您想基于查询,否则这里不需要例如文章描述文字。

listArticles(filter: {category: {eq: "Holiday"} }, limit: 10, sortDirection: DESC)

增强 AppSync:使用分页过滤

let allClubsList = async (sport) => {
  try {
     let clubsList;
     let clubsInfoList = [];
    let nextTokenInfo = null;
    do{ 
    let clubs = await client.query({
      query: gql(clubBySportStatus),
      variables: {
        sport: sport,
        eq: { status: "ACTIVE" },
      },
      limit: 100,
      nextToken: nextTokenInfo,
      fetchPolicy: "network-only",
    });
    clubsList = clubs.data.clubBySportStatus.items;
     clubsList.forEach((item) => clubsInfoList.push(item));
      nextTokenInfo = clubs.data.clubBySportStatus.nextToken;
    } while (Boolean(nextTokenInfo));
    if (clubsInfoList && clubsInfoList.length) {
      return {
        success: true,
        data: clubsInfoList,
      };
    }
  } catch (eX) {
    console.error(`Error in allClubsList: ${JSON.stringify(eX)}`);
    return {
      success: false,
      message: eX.message,
    };
  }
};