perf(2meet-data-optimizer): route sort-only searches from the flat table when every post has a row
- A sort-only search (HivePress _sort, no routed filter) LEFT JOINs the flat table to keep posts without a row; it now drives from the flat table (STRAIGHT_JOIN, INNER JOIN) when every post of the type has a flat row. The router gives a new post of a type with its own groups an empty row (wp_insert_post); coverage is one anti-join per group, cached 15 min and per post-mode change (forget_coverage()). - A flat-first search drops the GROUP BY posts.ID WordPress adds for any tax query when the flat row is the only join (WooCommerce's product_visibility NOT IN on searches is a subquery); a joining tax query keeps it. - Verify: live router suite (5 new cases, RED before); dev36 rating__desc page, ids identical: native ~3.3 s, 2.1 routing ~1.5–1.9 s, now ~0.51–0.85 s (main query 440 → 46 ms, COUNT 400 → 160 ms). p50 ≤ 300 ms is still not met: page rendering and HivePress's own get_posts() in pre_get_posts (suppress_filters, ~100 ms) remain. - PR: PR-079
This commit is contained in:
@@ -424,3 +424,135 @@ TMDO_Live::test(
|
||||
tmdo_live_assert_true( 0 === strpos( ltrim( $routed->request ), 'SELECT STRAIGHT_JOIN' ), 'flat-first: ' . $routed->request );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* HivePress-style sort-only search (rating__order), fields ids, first page.
|
||||
*
|
||||
* @param string $order ASC | DESC.
|
||||
* @return array<string, mixed> WP_Query args.
|
||||
*/
|
||||
function tmdo_live_router_rating_sort( string $order ): array {
|
||||
return array(
|
||||
'post_type' => 'hp_listing',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 10,
|
||||
'fields' => 'ids',
|
||||
'suppress_filters' => false,
|
||||
'meta_query' => array(
|
||||
array(
|
||||
'relation' => 'OR',
|
||||
'rating__order' => array( 'key' => 'hp_rating', 'type' => 'DECIMAL', 'compare' => 'NOT EXISTS' ),
|
||||
array( 'key' => 'hp_rating', 'type' => 'DECIMAL', 'compare' => 'EXISTS' ),
|
||||
),
|
||||
),
|
||||
'orderby' => 'rating__order',
|
||||
'order' => $order,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Native and routed runs of the same query.
|
||||
*
|
||||
* @param array<string, mixed> $args Query args.
|
||||
* @return array{0:WP_Query, 1:WP_Query}
|
||||
*/
|
||||
function tmdo_live_router_native_and_routed( array $args ): array {
|
||||
$native = new WP_Query( $args );
|
||||
$router = new TMDO_Post_Query_Router();
|
||||
$router->register_hooks();
|
||||
try {
|
||||
$routed = new WP_Query( $args );
|
||||
} finally {
|
||||
tmdo_live_router_unhook( $router );
|
||||
}
|
||||
return array( $native, $routed );
|
||||
}
|
||||
|
||||
TMDO_Live::test(
|
||||
'a sort-only search drives from the flat table when every post of the type has a flat row (2.2 PR-079)',
|
||||
static function () {
|
||||
TMDO_Live_Fixtures::field( 'post', 'hp_rating', 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'shadow_read' ) );
|
||||
TMDO_Post_Query_Router::forget_coverage();
|
||||
foreach ( array( 'DESC', 'ASC' ) as $order ) {
|
||||
list( $native, $routed ) = tmdo_live_router_native_and_routed( tmdo_live_router_rating_sort( $order ) );
|
||||
tmdo_live_assert_same( array_map( 'intval', $native->posts ), array_map( 'intval', $routed->posts ), "{$order} ids" );
|
||||
tmdo_live_assert_same( $native->found_posts, $routed->found_posts, "{$order} found_posts" );
|
||||
tmdo_live_assert_true( 0 === strpos( ltrim( $routed->request ), 'SELECT STRAIGHT_JOIN' ), "{$order} flat-first: " . $routed->request );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
'a sort-only search keeps the LEFT JOIN while a post of the type has no flat row (2.2 PR-079)',
|
||||
static function () {
|
||||
global $wpdb;
|
||||
TMDO_Live_Fixtures::field( 'post', 'hp_rating', 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'shadow_read' ) );
|
||||
$bare = TMDO_Live_Fixtures::post( 'hp_listing' );
|
||||
$wpdb->delete( TMDO_Schema_Manager::get_table_name( 'post', 'hp_listing_core' ), array( 'post_id' => $bare ) );
|
||||
TMDO_Post_Query_Router::forget_coverage();
|
||||
try {
|
||||
list( $native, $routed ) = tmdo_live_router_native_and_routed( tmdo_live_router_rating_sort( 'DESC' ) );
|
||||
} finally {
|
||||
TMDO_Post_Query_Router::forget_coverage();
|
||||
}
|
||||
tmdo_live_assert_same( array_map( 'intval', $native->posts ), array_map( 'intval', $routed->posts ), 'ids' );
|
||||
tmdo_live_assert_true( false === strpos( $routed->request, 'STRAIGHT_JOIN' ), 'not flat-first: ' . $routed->request );
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
'a post created while the router is active gets its empty flat row (2.2 PR-079)',
|
||||
static function () {
|
||||
global $wpdb;
|
||||
TMDO_Live_Fixtures::field( 'post', 'hp_rating', 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'shadow_read' ) );
|
||||
$router = new TMDO_Post_Query_Router();
|
||||
$router->register_hooks();
|
||||
try {
|
||||
$id = TMDO_Live_Fixtures::post( 'hp_listing' );
|
||||
} finally {
|
||||
tmdo_live_router_unhook( $router );
|
||||
}
|
||||
$table = TMDO_Schema_Manager::get_table_name( 'post', 'hp_listing_core' );
|
||||
tmdo_live_assert_same( '1', $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$table}` WHERE post_id = %d", $id ) ), 'flat row created' ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
'a flat-first search drops the GROUP BY WordPress adds for a subquery-only tax query (2.2 PR-079)',
|
||||
static function () {
|
||||
TMDO_Live_Fixtures::field( 'post', 'hp_rating', 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'shadow_read' ) );
|
||||
TMDO_Post_Query_Router::forget_coverage();
|
||||
// Like WooCommerce's product_visibility exclusion on front-end searches: NOT IN
|
||||
// is a subquery, yet WordPress groups by posts.ID for any tax query.
|
||||
$args = tmdo_live_router_rating_sort( 'DESC' ) + array(
|
||||
'tax_query' => array( array( 'taxonomy' => 'hp_listing_category', 'field' => 'term_id', 'terms' => array( 999999999 ), 'operator' => 'NOT IN' ) ),
|
||||
);
|
||||
list( $native, $routed ) = tmdo_live_router_native_and_routed( $args );
|
||||
tmdo_live_assert_same( array_map( 'intval', $native->posts ), array_map( 'intval', $routed->posts ), 'ids' );
|
||||
tmdo_live_assert_true( 0 === strpos( ltrim( $routed->request ), 'SELECT STRAIGHT_JOIN' ), 'flat-first: ' . $routed->request );
|
||||
tmdo_live_assert_true( false === stripos( $routed->request, 'GROUP BY' ), 'no GROUP BY: ' . $routed->request );
|
||||
}
|
||||
);
|
||||
|
||||
TMDO_Live::test(
|
||||
'a flat-first search keeps the GROUP BY when a tax query joins term relationships (2.2 PR-079)',
|
||||
static function () {
|
||||
TMDO_Live_Fixtures::field( 'post', 'hp_rating', 'hp_listing' );
|
||||
TMDO_Live_Fixtures::use_modes( array( 'post' => 'shadow_read' ) );
|
||||
TMDO_Post_Query_Router::forget_coverage();
|
||||
$terms = get_terms( array( 'taxonomy' => 'hp_listing_category', 'number' => 2, 'hide_empty' => true, 'fields' => 'ids' ) );
|
||||
if ( count( (array) $terms ) < 2 ) {
|
||||
throw new TMDO_Live_Skip( 'needs two used hp_listing_category terms' );
|
||||
}
|
||||
$args = tmdo_live_router_rating_sort( 'DESC' ) + array(
|
||||
'tax_query' => array( array( 'taxonomy' => 'hp_listing_category', 'field' => 'term_id', 'terms' => $terms, 'operator' => 'IN' ) ),
|
||||
);
|
||||
list( $native, $routed ) = tmdo_live_router_native_and_routed( $args );
|
||||
tmdo_live_assert_same( array_map( 'intval', $native->posts ), array_map( 'intval', $routed->posts ), 'ids' );
|
||||
tmdo_live_assert_true( false !== stripos( $routed->request, 'GROUP BY' ), 'GROUP BY kept: ' . $routed->request );
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user