diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 1f9516eec472e..8ca1e9c48458f 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2331,6 +2331,41 @@ public function get_posts() { } elseif ( 'none' === $q['orderby'] ) { $orderby = ''; } else { + // https://core.trac.wordpress.org/ticket/47642 + // Database engine fix to prevent strange (duplicate or missing) results in paginated resultsets + // Test for possible duplicate (non-unique, numeric or datetime) DB keys, add the unique ID + $force_ID = array( + 'post_author', + 'post_date', + 'post_date_gmt', + 'post_modified', + 'post_modified', + 'post_modified_gmt', + 'post_parent', + 'comment_count', + ); + if ( is_array( $q['orderby'] ) ) { + $needs_ID = false; + $has_ID = false; + foreach ( $q['orderby'] as $_orderby => $order ) { + if ( in_array( $_orderby, $force_ID, true ) ) { + $needs_ID = true; + } else { + if ( 'ID' === $_orderby ) { + $has_ID = true; + break; // All done + } + } + } + if ( $needs_ID && ! $has_ID ) { + $q['orderby']['ID'] = 'ASC'; + } + } else { + // Not an array, test as string + if ( in_array( $q['orderby'], $force_ID, true ) ) { + $q['orderby'] .= ' ID'; // Add ID here, note the space + } + } $orderby_array = array(); if ( is_array( $q['orderby'] ) ) { foreach ( $q['orderby'] as $_orderby => $order ) { @@ -2371,14 +2406,14 @@ public function get_posts() { // Order search results by relevance only when another "orderby" is not specified in the query. if ( ! empty( $q['s'] ) ) { $search_orderby = ''; - if ( ! empty( $q['search_orderby_title'] ) && ( empty( $q['orderby'] ) && ! $this->is_feed ) || ( isset( $q['orderby'] ) && 'relevance' === $q['orderby'] ) ) { + if ( ( ! empty( $q['search_orderby_title'] ) && ( empty( $q['orderby'] ) && ! $this->is_feed ) ) || ( isset( $q['orderby'] ) && 'relevance' === $q['orderby'] ) ) { $search_orderby = $this->parse_search_order( $q ); } if ( ! $q['suppress_filters'] ) { /** * Filters the ORDER BY used when ordering search results. - * + *https://core.trac.wordpress.org/attachment/ticket/47642 * @since 3.7.0 * * @param string $search_orderby The ORDER BY clause. @@ -2386,6 +2421,8 @@ public function get_posts() { */ $search_orderby = apply_filters( 'posts_search_orderby', $search_orderby, $this ); } + //Add additional sort by order to break ties consistently for pagination https://core.trac.wordpress.org/ticket/47642 + $orderby = $orderby ? $orderby . ', ID' : ' ID'; if ( $search_orderby ) { $orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby; diff --git a/tests/phpunit/tests/query/orderby.php b/tests/phpunit/tests/query/orderby.php new file mode 100644 index 0000000000000..9e45703544014 --- /dev/null +++ b/tests/phpunit/tests/query/orderby.php @@ -0,0 +1,59 @@ +post->create_many( 5 ); + + $wpdb->update( + $wpdb->posts, + array( + 'comment_count' => 50, + ), + array( + 'ID' => $posts[2], + ) + ); + $wpdb->update( + $wpdb->posts, + array( + 'comment_count' => 100, + ), + array( + 'ID' => $posts[4], + ) + ); + $args = array( + 'posts_per_page' => 1, + 'paged' => 1, + 'orderby' => 'comment_count', + 'order' => 'desc', + ); + + $query = new WP_Query( $args ); + $actual = array(); + $expected = array( + $posts[4], // 100 comments + $posts[2], // 50 comments + $posts[3], // 0 comments + $posts[1], // 0 comments + $posts[0], // 0 comments + ); + + while ( $query->have_posts() ) { + $actual = array_merge( $actual, wp_list_pluck( $query->posts, 'ID' ) ); + $args['paged']++; + $query = new WP_Query( $args ); + } + + $this->assertSame( $expected, $actual ); + } +}