Skip to content

Cursor pagination doesn't work with search queries #1818

Description

@benknight

Cursor-based pagination combined with search queries generates SQL queries that do not correctly paginate search results. Investigating SQL Query Logs shows this is because cursor-based pagination translates to post date comparisons, which depends on queries being ordered by by post date. However, search queries are ordered by wp_posts.post_title LIKE '%query%' first, therefore causing the start/end cursors to reference posts that are not in chronological order.

During my investigation I found logic that was already put in place to prevent this, right here:
https://github.com/wp-graphql/wp-graphql/blob/develop/src/Data/Connection/PostObjectConnectionResolver.php#L262

/**
  * If the query contains search default the results to
  */
if ( isset( $query_args['search'] ) && ! empty( $query_args['search'] ) ) {
  /**
    * Don't order search results by title (causes funky issues with cursors)
    */

  $query_args['search_orderby_title'] = false;
  $query_args['orderby']              = 'date';
  $query_args['order']                = isset( $last ) ? 'ASC' : 'DESC';
}

However this block is never executing because it looks like the search query is at $query_args['s'] not $query_args['search'].

Example query:

query SearchPosts($after: String, $before: String, $query: String!) {
  posts(after: $after, before: $before, first: 10, last: null, where: {search: $query}) {
    pageInfo {
      startCursor
      endCursor
      hasNextPage
      hasPreviousPage
    }
    edges {
      cursor
    }
    nodes {
      databaseId
      title
      date
    }
  }
}

Will translate into the following SQL query:

SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND (((wp_posts.post_title LIKE '%example%') OR (wp_posts.post_excerpt LIKE '%example%') OR (wp_posts.post_content LIKE '%example%'))) AND wp_posts.post_type = 'post' AND ((wp_posts.post_status = 'publish')) AND CAST( wp_posts.post_date as DATETIME ) <= CAST( '2014-06-03 12:19:21' as DATETIME ) AND ( CAST( wp_posts.post_date as DATETIME ) < CAST( '2014-06-03 12:19:21' as DATETIME ) OR ( wp_posts.ID < 10768 ) ) ORDER BY wp_posts.post_title LIKE '%example%' DESC, wp_posts.post_date DESC, wp_posts.ID DESC LIMIT 0, 6

WPGraphQL version 1.3.3

Metadata

Metadata

Assignees

Labels

compat: possible breakThere is a possibility that this might lead to breaking changes, but not confirmed yetcomponent: paginationRelating to paginationeffort: highMore than a weekhas: workaroundA temporary workaround has been providedimpact: highUnblocks new use cases, substantial improvement to existing feature, fixes a major bugobject type: postRelating to the Post Object Typesstatus: in progressCurrently being worked ontype: bugIssue that causes incorrect or unexpected behavior

Type

No fields configured for Bug.

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions