代码之家  ›  专栏  ›  技术社区  ›  Marcel Overdijk

如何在GraphQL/Relay中使用(不透明的)游标(当使用filter参数和order by时)

  •  0
  • Marcel Overdijk  · 技术社区  · 8 年前

    想象一下下面的GraphQL请求:

    {
      books(
        first:10,
        filter: [{field: TITLE, contains: "Potter"}],
        orderBy: [{sort: PRICE, direction: DESC}, {sort: TITLE}]
      )
    }
    

    结果将返回与中继光标信息的连接。

    光标是否应包含 filter orderBy 细节?

    {
      books(first:10, after:"opaque-cursor")
    }
    

    或者应该 订货人

    在后一种情况下,用户可以指定不同的 滤波器 订货人 使不透明光标无效的详细信息。

    我在继电器规格里找不到任何关于这个的东西。

    2 回复  |  直到 8 年前
        1
  •  1
  •   Dan Crews    7 年前

    我已经看到这种方法有很多种,但是我发现使用基于游标的分页,游标只存在于数据集中,更改过滤器会更改数据集,使其无效。

    如果他们将“after”和“filter/orderBy”一起发送,则必须抛出错误。您可以选择检查参数是否与光标中的参数相同,以防用户出错,但根本没有获得不同数据集的“第2页”的用例。

        2
  •  0
  •   Benjamin M    7 年前

    LIMIT .

    当您的初始查询是

    SELECT *
    FROM DataTable
    WHERE filterField = 42
    ORDER BY sortingField,ASC
    LIMIT 10
    -- with implicit OFFSET 0
    

    ( 不要 在一个真正的应用程序中这样做,因为SQL注入!) LIMIT x 并附加 OFFSET y 对于每个节点。

    {
      edges: [
        {
          cursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 0",
          node: { ... }
        },
        {
          cursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 1",
          node: { ... }
        },
        ...,
        {
          cursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 9",
          node: { ... }
        }
      ]
      pageInfo: {
        startCursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 0"
        endCursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 9"
      }
    }
    

    下一个请求将使用 after: CURSOR, first: 10 . 那你就拿 after 限制 OFFSET

    • LIMIT = first
    • OFFSET = OFFSET + 1

    after = endCursor :

    SELECT *
    FROM DataTable
    WHERE filterField = 42
    ORDER BY sortingField,ASC
    LIMIT 10
    OFFSET 10
    

    如上所述:


    在现实世界的应用程序中,您可以简单地对提供的 filter orderBy 游标中的参数,并添加 offset 也:

    function handleGraphQLRequest(first, after, filter, orderBy) {
      let offset = 0; // initial offset, if after isn't provided
    
      if(after != null) {
        // combination of after + filter/orderBy is not allowed!
        if(filter != null || orderBy != null) {
          throw new Error("You can't combine after with filter and/or orderBy");
        }
    
        // parse filter, orderBy, offset from after cursor
        cursorData = fromBase64String(after);
        filter = cursorData.filter;
        orderBy = cursorData.orderBy;
        offset = cursorData.offset;
      }
    
      const databaseResult = executeDatabaseQuery(
        filter,  // = WHERE ...
        orderBy, // = ORDER BY ...
        first,   // = LIMIT ...
        offset   // = OFFSET ...
      );
    
      const edges = []; // this is the resulting edges array
      let currentOffset = offset; // this is used to calc the offset for each node
      for(let node of databaseResult.nodes) { // iterate over the database results
        currentOffset++;
        const currentCursor = createCursorForNode(filter, orderBy, currentOffset);
        edges.push({
          cursor = currentCursor,
          node = node
        });
      }
    
      return {
        edges: edges,
        pageInfo: buildPageInfo(edges, totalCount, offset) // instead of
            // of providing totalCount, you could also fetch (limit+1) from
            // database to check if there is a next page available
      }
    }
    
    // this function returns the cursor string
    function createCursorForNode(filter, orderBy, offset) {
      return toBase64String({
        filter: filter,
        orderBy: orderBy,
        offset: offset
      });
    }
    
    // function to build pageInfo object
    function buildPageInfo(edges, totalCount, offset) {
      return {
        startCursor: edges.length ? edges[0].cursor : null,
        endCursor: edges.length ? edges[edges.length - 1].cursor : null,
        hasPreviousPage: offset > 0 && totalCount > 0,
        hasNextPage: offset + edges.length < totalCount
      }
    }
    

    cursor 主要取决于数据库和数据库布局。

    推荐文章