fix(2meet-data-optimizer): keep meta_key native when the query still orders by it

- meta_key is routed as EXISTS only when no orderby entry names meta_value,
  meta_value_num or the key (several orderings or an array included);
  clearing it made WordPress drop the price ordering
- A sort group is dropped as unused only when orderby names neither it,
  meta_value(_num) nor its key (it is then the primary clause)
- meta_compare_key other than '=' keeps meta_key native (LIKE matches other keys)
- Found in the 2.4 correctness review (regressions from PR-122)
- Verify: PostQueryRouterTest (3 new), router suites 67 OK
- PR: PR-130
This commit is contained in:
2026-09-28 00:27:56 +08:00
parent 7ad65eab95
commit bed3f906da
2 changed files with 72 additions and 8 deletions
@@ -172,8 +172,11 @@ class TMDO_Post_Query_Router {
// meta_key + orderby meta_value(_num): native INNER JOINs the key
// (EXISTS) and orders by meta_value+0 (NUMERIC) or meta_value.
$meta_key = $query->get( 'meta_key' );
$take_key = is_string( $meta_key ) && in_array( $sort_name, array( 'meta_value', 'meta_value_num' ), true )
// A key comparison other than equality (meta_compare_key LIKE …) matches
// other keys too: not a test on one flat column.
$meta_key = $query->get( 'meta_key' );
$key_exact = in_array( (string) $query->get( 'meta_compare_key' ), array( '', '=' ), true );
$take_key = $key_exact && is_string( $meta_key ) && in_array( $sort_name, array( 'meta_value', 'meta_value_num' ), true )
&& null !== self::field_for( array( 'key' => $meta_key ), $pt );
if ( $take_key ) {
$field = self::field_for( array( 'key' => $meta_key ), $pt );
@@ -192,11 +195,13 @@ class TMDO_Post_Query_Router {
$routed[ $pt ][] = $route;
$sort = self::sort_for( $pt, $route, $direction );
}
// meta_key alone (no meta_value, not a meta_value sort): WP_Query adds a
// clause that tests the key exists — HivePress's featured-listing lookup.
$exists_key = ! $take_key && is_string( $meta_key ) && '' !== $meta_key
// meta_key alone (no meta_value, not ordered by): WP_Query adds a clause
// that tests the key exists — HivePress's featured-listing lookup. Any
// orderby naming meta_value, meta_value_num or the key sorts by that
// clause, which clearing meta_key would drop.
$exists_key = ! $take_key && $key_exact && is_string( $meta_key ) && '' !== $meta_key
&& '' === (string) $query->get( 'meta_value' ) && '' === (string) $query->get( 'meta_compare' )
&& ! in_array( $sort_name, array( 'meta_value', 'meta_value_num' ), true )
&& ! array_intersect( $named, array( 'meta_value', 'meta_value_num', $meta_key ) )
&& null !== self::field_for( array( 'key' => $meta_key ), $pt );
if ( $exists_key ) {
$route = self::route_clause(
@@ -220,8 +225,12 @@ class TMDO_Post_Query_Router {
if ( ! isset( $clause['key'] ) ) {
// Sort shape: matches every post, only orders by the key.
$name = 'AND' === $relation ? self::sort_shape( $clause ) : '';
if ( '' !== $name && ! in_array( $name, $named, true ) && self::field_for( $clause[ $name ], $pt ) ) {
continue; // Matches every post and orders nothing here (a copy of a search's meta query).
// Matches every post and orders nothing here (a copy of a search's meta
// query) — unless orderby names it, or meta_value(_num) / its key, which
// sort by it when it is WordPress's primary (first) clause.
if ( '' !== $name && ! array_intersect( $named, array( $name, 'meta_value', 'meta_value_num', (string) ( $clause[ $name ]['key'] ?? '' ) ) )
&& self::field_for( $clause[ $name ], $pt ) ) {
continue;
}
$field = '' !== $name && $name === $sort_name ? self::field_for( $clause[ $name ], $pt ) : null;
if ( $field ) {
+55
View File
@@ -471,6 +471,61 @@ class PostQueryRouterTest extends TestCase {
$this->assertStringContainsString( 'BETWEEN', $where );
}
/**
* WordPress orders by the primary meta clause (meta_key) for meta_value,
* meta_value_num or the key's name anywhere in orderby; routing the key and
* clearing meta_key would drop that ordering (2.4 correctness review).
*/
public function test_meta_key_ordered_by_among_several_orderings_stays_native(): void {
foreach ( array(
array( 'meta_value_num' => 'ASC', 'title' => 'ASC' ),
'meta_value date',
'_stock',
) as $orderby ) {
$this->set_post_mode( 'aeav_only' );
$query = $this->make_query( array( 'post_type' => 'product', 'meta_key' => '_stock', 'orderby' => $orderby ) );
( new WPDO_Post_Query_Router() )->pre_get_posts( $query );
$label = is_array( $orderby ) ? implode( ',', array_keys( $orderby ) ) : $orderby;
$this->assertSame( '_stock', $query->get( 'meta_key' ), $label );
$this->assertSame( '', $query->get( 'wpdo_post_clauses' ), $label );
}
}
/** meta_compare_key LIKE matches other keys too: not a test on one flat column. */
public function test_meta_key_with_a_key_comparison_stays_native(): void {
foreach ( array( 'rand', 'meta_value_num' ) as $orderby ) {
$this->set_post_mode( 'aeav_only' );
$query = $this->make_query( array( 'post_type' => 'product', 'meta_key' => '_stock', 'meta_compare_key' => 'LIKE', 'orderby' => $orderby ) );
( new WPDO_Post_Query_Router() )->pre_get_posts( $query );
$this->assertSame( '_stock', $query->get( 'meta_key' ), $orderby );
$this->assertSame( '', $query->get( 'wpdo_post_clauses' ), $orderby );
}
}
/**
* Without meta_key the first clause is WordPress's primary one: orderby
* meta_value_num sorts by the sort group's key, so the group stays.
*/
public function test_sort_group_ordered_as_the_primary_clause_is_not_dropped(): void {
$this->assert_not_routed(
array(
'post_type' => 'product',
'orderby' => 'meta_value_num',
'meta_query' => array(
array(
'relation' => 'OR',
'price__order' => array( 'key' => '_price', 'compare' => 'NOT EXISTS', 'type' => 'DECIMAL' ),
array( 'key' => '_price', 'compare' => 'EXISTS', 'type' => 'DECIMAL' ),
),
array( 'key' => '_stock', 'value' => 1, 'compare' => '>=', 'type' => 'NUMERIC' ),
),
),
'primary sort group'
);
}
public function test_meta_key_with_a_value_is_left_alone(): void {
$this->set_post_mode( 'aeav_only' );
$router = new WPDO_Post_Query_Router();