首页 > 解决方案 > 在 graphQL 服务器中使用 SQL datasarouce 实现嵌套游标分页的最佳方法是什么?

问题描述

您如何管理使用 relay-esk 模式(使用 SQL 数据源)中的嵌套游标的有效数据获取?

  1. 您是否尝试使用“LIMIT args_first”、“ORDER BY args_orderby”和“WHERE cursor > :args_after”进行单个复杂的 SQL 查询来解决 N+1 问题
  2. 您是否对数据库运行 2 个查询并使用 facebook 数据加载器?

例如,我有一个架构如下:

enum BookSortKeys {
    ID,
    TITLE,
    PRICE,
    UPDATED_AT,
    CREATED_AT
}
enum ReviewSortKeys {
    ID,
    REVIEW,
    UPDATED_AT,
    CREATED_AT
}
type Book {
  id: ID!
  title: String!
  description: String
  price: Float!
  updatedAt: String!
  createdAt: String!
  reviews("""
    Returns the elements that come after the specified cursor.
    """
    after: String
    """
    Returns the elements that come before the specified cursor.
    """
    before: String
    """
    Returns up to the first `n` elements from the list.
    """
    first: Int
    """
    Returns up to the last `n` elements from the list.
    """
    last: Int
    """
    Reverse the order of the underlying list.
    """
    reverse: Boolean = false
    """
    Sort the underlying list by the given key.
    """
    sortKey: ReviewSortKeys = ID): ReviewConnection!
}
type Query {
  books("""
    Returns the elements that come after the specified cursor.
    """
    after: String
    """
    Returns the elements that come before the specified cursor.
    """
    before: String
    """
    Returns up to the first `n` elements from the list.
    """
    first: Int
    """
    Returns up to the last `n` elements from the list.
    """
    last: Int
    """
    Supported filter parameters:
     - `title`
     - `id`
     - `price`
     - `description`
     - `created_at`
     - `updated_at`
    """
    query: String
    """
    Reverse the order of the underlying list.
    """
    reverse: Boolean = false
    """
    Sort the underlying list by the given key.
    """
    sortKey: BookSortKeys = ID): BookConnection!
}
type ReviewConnection {
    pageInfo: PageInfo!
    edges: [ReviewEdge!]!
}
type ReviewEdge {
    cursor: String!
    node: Review!
}
type BookConnection {
    pageInfo: PageInfo!
    edges: [BookEdge!]!
}
type BookEdge {
    cursor: String!
    node: Book!
}
type PageInfo {
    hasNextPage: Boolean!
    hasPreviousPage: Boolean!
}
type Review {
    review: String!
    id: ID!
    updatedAt: String!
    createdAt: String!
}
type Mutation {
}
schema {
  query: Query
  mutation: Mutation
}

我想执行如下查询并以最有效的方式检索数据。

query GET_BOOKS {
  books(first:10, sortKey: PRICE, reverse: true) {
       pageInfo {
      hasNextPage
      hasPreviousPage
    } 
    edges {
      cursor
      node {
        id
        title
        description
        reviews(after:"base64-cursor" first: 5, sortKey: CREATED_AT) {
          edges {
            node{
              review
            }
          }
        }
      }
    }
  }
}

我可以很容易地将顶部查询(书)的所有分页参数转换为 sql 语句,但是使用嵌套游标,我只能看到 2 个选项(上面提到过)......在实现这些选项之前我面临的当前问题是:

  1. 如果我采用纯 SQL 方法 - 是否有一种干净的方法来运行单个查询并在嵌套 (JOIN) 级别应用LIMITandWHERE createdAt > :after_cursor_val
  2. 如果上述是可能的,它是否比大规模的数据加载器性能更高?由于查询似乎如果实施它将非常冗长和复杂。
  3. 如果嵌套分页树增长(即具有 4 个嵌套分页的请求)会发生什么?一个纯 Query 对象级别的 sql 命令在这里就足够了吗?或者在每个关系上添加解析器是否更具可扩展性(即 book -> reviews 有一个 sql 查询来提取这本书的所有特定评论,reviews -> Publications 有一个查询来提取它所在的所有评论的特定出版物等等在数据加载器中对它们进行批处理)
  4. 如果你走数据加载器路线,批处理似乎使用“WHERE IN”子句,(即SELECT * FROM reviews "reviews" WHERE "reviews".bookId IN (...list of book ids batched)- 会添加LIMITORDER BYWHERE createdAt > :cursor提供意外结果,因为我的结果集是多个“书籍 ID”的条目混合?
  5. 从长远来看,我个人的感觉是,从代码的角度来看,纯 sql 方法会很混乱,对此有何想法?

标签: sqlgraphqlgraphql-jsrelay

解决方案


推荐阅读