Expand my Community achievements bar.

SOLVED

Graphql cursor based pagination does not work with sorting

Avatar

Level 1

I have a headless app which uses AEM Cloud to serve the data with the following persisted graphql query:

 

query (
  $locale: String!
  $path: ID!
  $sort: String
  $first: Int
  $after: String
) {
  pagePaginated(
    _locale: $locale
    filter: {
      _path: { _expressions: [{ value: $path, _operator: STARTS_WITH }] }
    }
    sort: $sort
    first: $first
    after: $after
  ) {
    edges {
      node {
        _path
        __typename
        title
        description {
          plaintext
        }
      }
      cursor
    }
    pageInfo {
      hasPreviousPage
      hasNextPage
      startCursor
      endCursor
    }
  }
}

 

 

 

I am getting an error with cursor based pagination whenever both the sort and after variables are present. When either is used independently, the query works as expected, such as either of these examples:

WORKS (But no sorting):

{
  "locale": "en",
  "sort": "",
  "first": 2,
  "path": "/path/to/my/content/",
  "after": "bnVsbAo5YTM3ZWVlMi1hM2ZjLTQ4N2QtYmY4Yi1kZTMzMzg5YTAwNWI="
}

WORKS (But no cursor supplied for pagination):

{
  "locale": "en",
  "sort": "title ASC",
  "first": 2,
  "path": "/path/to/my/content/",
  "after": ""
}

 

 

 

THE PROBLEM CASE

When both sort and after variables are supplied.

 

THROWS ERROR:

 

{
  "locale": "en",
  "sort": "title ASC",
  "first": 2,
  "path": "/path/to/my/content/",
  "after": "bnVsbAo5YTM3ZWVlMi1hM2ZjLTQ4N2QtYmY4Yi1kZTMzMzg5YTAwNWI="
}

 

 

This will return the following error:

{
  "errors": [
    {
      "message": "Exception while fetching data (/pagePaginated) : Start cursor not found in supplied data:bnVsbAo5YTM3ZWVlMi1hM2ZjLTQ4N2QtYmY4Yi1kZTMzMzg5YTAwNWI=",
      "locations": [
        {
          "line": 13,
          "column": 3
        }
      ],
      "path": [
        "pagePaginated"
      ],
      "extensions": {
        "classification": "DataFetchingException"
      }
    }
  ],
  "data": null
}

 

Am I missing something, or is this a bug in Adobe's implementation of GraphQL?

Please advise.

 

 

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

@danieljkahn By default, paging uses the UUID of the repository node representing the fragment for ordering to ensure the order of results is always the same. When sort is used, the UUID is implicitly used to ensure a unique sort; even for two items with identical sort keys.

 

So, when we use the sort, the UUID are different.

 

Query-1:

query {
    adventurePaginated(first: 3, after: "MzIxMjljYmQtMzY5OS00MzdmLTg2YTQtZGFkNzkwM2E2M2Yz") {
        edges {
          cursor
          node {
            title
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
    }
}

 Result:

{
  "data": {
    "adventurePaginated": {
      "edges": [
        {
          "cursor": "Mzk2NzhjOTUtZDVlMC00MTYyLWFiNDktNTFiMzhiNWU4Mzg5",
          "node": {
            "title": "Riverside Camping Australia"
          }
        },
        {
          "cursor": "NTc2Mzc0YmMtZTI1Yi00MjhmLWEyNDktMWI2MTVlNWMwY2Yw",
          "node": {
            "title": "Whistler Mountain Biking Adventure"
          }
        },
        {
          "cursor": "NTkzMjEwZDAtNmZmZS00MmM5LTgxOGYtZDc4MjVlMjFkOTNk",
          "node": {
            "title": "Cycling Tuscany"
          }
        }
      ],
      "pageInfo": {
        "endCursor": "NTkzMjEwZDAtNmZmZS00MmM5LTgxOGYtZDc4MjVlMjFkOTNk",
        "hasNextPage": true
      }
    }
  }
}

 

Query-2 with Sort:

query {
    adventurePaginated(first: 3, sort: "title", after: "T3Zlcm5pZ2h0IENvbG9yYWRvIFJvY2sgQ2xpbWJpbmcKYTM0MDk1NTgtYWQwMi00MjJhLTk5NTItYTg4MGVlMTZmMDZj") {
        edges {
          cursor
          node {
            title
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
    }
}

 

Results with different UUID:

{
  "data": {
    "adventurePaginated": {
      "edges": [
        {
          "cursor": "Uml2ZXJzaWRlIENhbXBpbmcgQXVzdHJhbGlhCjM5Njc4Yzk1LWQ1ZTAtNDE2Mi1hYjQ5LTUxYjM4YjVlODM4OQ==",
          "node": {
            "title": "Riverside Camping Australia"
          }
        },
        {
          "cursor": "U2tpIFRvdXJpbmcgTW9udCBCbGFuYwo4M2I2MzgwMi1mZmIwLTQ3ZDYtOTRiMC1lZTgyMWU4ODc1Mzg=",
          "node": {
            "title": "Ski Touring Mont Blanc"
          }
        },
        {
          "cursor": "U3VyZiBDYW1wIGluIENvc3RhIFJpY2EKODg1MmMyMmEtZTAzMy00MTNjLThiMzMtZGQyMzY5ZTNjN2M1",
          "node": {
            "title": "Surf Camp in Costa Rica"
          }
        }
      ],
      "pageInfo": {
        "endCursor": "U3VyZiBDYW1wIGluIENvc3RhIFJpY2EKODg1MmMyMmEtZTAzMy00MTNjLThiMzMtZGQyMzY5ZTNjN2M1",
        "hasNextPage": true
      }
    }
  }
}

Aanchal Sikka

View solution in original post

1 Reply

Avatar

Correct answer by
Community Advisor

@danieljkahn By default, paging uses the UUID of the repository node representing the fragment for ordering to ensure the order of results is always the same. When sort is used, the UUID is implicitly used to ensure a unique sort; even for two items with identical sort keys.

 

So, when we use the sort, the UUID are different.

 

Query-1:

query {
    adventurePaginated(first: 3, after: "MzIxMjljYmQtMzY5OS00MzdmLTg2YTQtZGFkNzkwM2E2M2Yz") {
        edges {
          cursor
          node {
            title
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
    }
}

 Result:

{
  "data": {
    "adventurePaginated": {
      "edges": [
        {
          "cursor": "Mzk2NzhjOTUtZDVlMC00MTYyLWFiNDktNTFiMzhiNWU4Mzg5",
          "node": {
            "title": "Riverside Camping Australia"
          }
        },
        {
          "cursor": "NTc2Mzc0YmMtZTI1Yi00MjhmLWEyNDktMWI2MTVlNWMwY2Yw",
          "node": {
            "title": "Whistler Mountain Biking Adventure"
          }
        },
        {
          "cursor": "NTkzMjEwZDAtNmZmZS00MmM5LTgxOGYtZDc4MjVlMjFkOTNk",
          "node": {
            "title": "Cycling Tuscany"
          }
        }
      ],
      "pageInfo": {
        "endCursor": "NTkzMjEwZDAtNmZmZS00MmM5LTgxOGYtZDc4MjVlMjFkOTNk",
        "hasNextPage": true
      }
    }
  }
}

 

Query-2 with Sort:

query {
    adventurePaginated(first: 3, sort: "title", after: "T3Zlcm5pZ2h0IENvbG9yYWRvIFJvY2sgQ2xpbWJpbmcKYTM0MDk1NTgtYWQwMi00MjJhLTk5NTItYTg4MGVlMTZmMDZj") {
        edges {
          cursor
          node {
            title
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
    }
}

 

Results with different UUID:

{
  "data": {
    "adventurePaginated": {
      "edges": [
        {
          "cursor": "Uml2ZXJzaWRlIENhbXBpbmcgQXVzdHJhbGlhCjM5Njc4Yzk1LWQ1ZTAtNDE2Mi1hYjQ5LTUxYjM4YjVlODM4OQ==",
          "node": {
            "title": "Riverside Camping Australia"
          }
        },
        {
          "cursor": "U2tpIFRvdXJpbmcgTW9udCBCbGFuYwo4M2I2MzgwMi1mZmIwLTQ3ZDYtOTRiMC1lZTgyMWU4ODc1Mzg=",
          "node": {
            "title": "Ski Touring Mont Blanc"
          }
        },
        {
          "cursor": "U3VyZiBDYW1wIGluIENvc3RhIFJpY2EKODg1MmMyMmEtZTAzMy00MTNjLThiMzMtZGQyMzY5ZTNjN2M1",
          "node": {
            "title": "Surf Camp in Costa Rica"
          }
        }
      ],
      "pageInfo": {
        "endCursor": "U3VyZiBDYW1wIGluIENvc3RhIFJpY2EKODg1MmMyMmEtZTAzMy00MTNjLThiMzMtZGQyMzY5ZTNjN2M1",
        "hasNextPage": true
      }
    }
  }
}

Aanchal Sikka