Skip to content

Commit 85339ff

Browse files
committed
Enhance WP_Query ordering to ensure deterministic results by adding ID as a secondary sort field. This change addresses potential duplicate records across pages when multiple posts share the same value for a field. A list of fields requiring deterministic ordering has been introduced to improve query consistency
1 parent 7447c82 commit 85339ff

File tree

1 file changed

+60
-3
lines changed

1 file changed

+60
-3
lines changed

src/wp-includes/class-wp-query.php

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,7 @@ public function set( $query_var, $value ) {
18861886
* database query.
18871887
*
18881888
* @since 1.5.0
1889+
* @since x.x.x Adds deterministic ordering to prevent duplicate records across pages.
18891890
*
18901891
* @global wpdb $wpdb WordPress database abstraction object.
18911892
*
@@ -2501,12 +2502,45 @@ public function get_posts() {
25012502
if ( isset( $query_vars['orderby'] ) && ( is_array( $query_vars['orderby'] ) || false === $query_vars['orderby'] ) ) {
25022503
$orderby = '';
25032504
} else {
2504-
$orderby = "{$wpdb->posts}.post_date " . $query_vars['order'];
2505+
/*
2506+
* Ensure deterministic ordering to prevent duplicate records across pages.
2507+
* When multiple posts have the same value for a field, add ID as secondary sort to guarantee consistent ordering.
2508+
* Note: this is to circumvent a bug that is currently being tracked in https://core.trac.wordpress.org/ticket/44349.
2509+
*/
2510+
$orderby = "{$wpdb->posts}.post_date " . $query_vars['order'] . ', ' . "{$wpdb->posts}.ID " . $query_vars['order'];
25052511
}
25062512
} elseif ( 'none' === $query_vars['orderby'] ) {
25072513
$orderby = '';
25082514
} else {
2509-
$orderby_array = array();
2515+
/*
2516+
* Ensure deterministic ordering to prevent duplicate records across pages.
2517+
* When multiple posts have the same value for a field, add ID as secondary sort to guarantee consistent ordering.
2518+
* Note: this is to circumvent a bug that is currently being tracked in https://core.trac.wordpress.org/ticket/44349.
2519+
*/
2520+
$fields_requiring_deterministic_orderby = array(
2521+
'post_name',
2522+
'post_author',
2523+
'post_date',
2524+
'post_title',
2525+
'post_modified',
2526+
'post_mime_type',
2527+
'post_parent',
2528+
'post_type',
2529+
'name',
2530+
'author',
2531+
'date',
2532+
'title',
2533+
'modified',
2534+
'parent',
2535+
'type',
2536+
'menu_order',
2537+
'comment_count',
2538+
);
2539+
2540+
$orderby_array = array();
2541+
$needs_deterministic_orderby = false;
2542+
$has_id_orderby = false;
2543+
25102544
if ( is_array( $query_vars['orderby'] ) ) {
25112545
foreach ( $query_vars['orderby'] as $_orderby => $order ) {
25122546
$orderby = addslashes_gpc( urldecode( $_orderby ) );
@@ -2517,7 +2551,20 @@ public function get_posts() {
25172551
}
25182552

25192553
$orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
2554+
2555+
// Check if this field needs deterministic ordering
2556+
if ( in_array( $_orderby, $fields_requiring_deterministic_orderby, true ) ) {
2557+
$needs_deterministic_orderby = true;
2558+
} elseif ( 'ID' === $_orderby ) {
2559+
$has_id_orderby = true;
2560+
}
2561+
}
2562+
2563+
// Add ID as tie-breaker if needed and not already present
2564+
if ( $needs_deterministic_orderby && ! $has_id_orderby ) {
2565+
$orderby_array[] = "{$wpdb->posts}.ID " . $query_vars['order'];
25202566
}
2567+
25212568
$orderby = implode( ', ', $orderby_array );
25222569

25232570
} else {
@@ -2532,11 +2579,21 @@ public function get_posts() {
25322579
}
25332580

25342581
$orderby_array[] = $parsed;
2582+
2583+
// Check if this field needs deterministic ordering
2584+
if ( in_array( $orderby, $fields_requiring_deterministic_orderby, true ) ) {
2585+
$needs_deterministic_orderby = true;
2586+
} elseif ( 'ID' === $orderby ) {
2587+
$has_id_orderby = true;
2588+
}
25352589
}
25362590
$orderby = implode( ' ' . $query_vars['order'] . ', ', $orderby_array );
25372591

25382592
if ( empty( $orderby ) ) {
2539-
$orderby = "{$wpdb->posts}.post_date " . $query_vars['order'];
2593+
$orderby = "{$wpdb->posts}.post_date " . $query_vars['order'] . ', ' . "{$wpdb->posts}.ID " . $query_vars['order'];
2594+
} elseif ( $needs_deterministic_orderby && ! $has_id_orderby ) {
2595+
// Add ID as tie-breaker for deterministic ordering
2596+
$orderby .= ", {$wpdb->posts}.ID " . $query_vars['order'];
25402597
} elseif ( ! empty( $query_vars['order'] ) ) {
25412598
$orderby .= " {$query_vars['order']}";
25422599
}

0 commit comments

Comments
 (0)