From ddefdc16d4f73ab010b231c945cce5966f1917d5 Mon Sep 17 00:00:00 2001 From: wpdev Date: Sun, 27 Sep 2026 00:22:47 +0800 Subject: [PATCH] fix(2meet-data-optimizer): keep searches native while a group is known to drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The post query router routes no clause of a group whose last drift-check recorded miss / diff / extra; all or nothing, so such a query stays fully native. A group never checked keeps routing (decision 2026-09-27: no slowdown after upgrading; PR-073 samples daily). - The check sits in route_clause(), which every routed clause, HivePress sort shape and meta_key sort goes through. - Verify: PostQueryRouterDriftTest (drifting group and drifting sort key RED before); dev36 hp_listing price search in shadow_read: clean → flat, drift → native, unchecked → flat, found 90347 each. - PR: PR-072 --- .../query/class-tmdo-post-query-router.php | 18 ++- tests/bootstrap.php | 1 + .../integration/PostQueryRouterDriftTest.php | 115 ++++++++++++++++++ tests/integration/bootstrap.php | 1 + 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 tests/integration/PostQueryRouterDriftTest.php diff --git a/includes/query/class-tmdo-post-query-router.php b/includes/query/class-tmdo-post-query-router.php index bf1f1ac..0f0210b 100644 --- a/includes/query/class-tmdo-post-query-router.php +++ b/includes/query/class-tmdo-post-query-router.php @@ -523,9 +523,23 @@ class TMDO_Post_Query_Router { return false; } + /** + * The last `wp tmdo drift-check` found the group out of step with native + * meta. A group never checked keeps routing. + * + * @param string $group Entity group name. + * @return bool + */ + private static function drifting( string $group ): bool { + $status = get_option( TMDO_Drift_Check::OPTION, array() ); + $last = is_array( $status ) ? ( $status['post'][ $group ] ?? null ) : null; + return is_array( $last ) && (int) ( $last['miss'] ?? 0 ) + (int) ( $last['diff'] ?? 0 ) + (int) ( $last['extra'] ?? 0 ) > 0; + } + /** * The routed form of a clause, or null when the flat column would not - * compare the way WordPress compares the meta_value string. + * compare the way WordPress compares the meta_value string, or when the + * group is known to drift from native meta. * * Native casts meta_value by the clause type (CHAR when absent); flat * columns are typed. Routed: EXISTS / NOT EXISTS; string columns with @@ -560,7 +574,7 @@ class TMDO_Post_Query_Router { && in_array( $compare, array( '=', '!=', '<>', 'IN', 'NOT IN' ), true ) && array() === array_filter( (array) $value, static fn( $v ) => ! is_scalar( $v ) || (string) (int) $v !== (string) $v ); } - if ( ! $ok ) { + if ( ! $ok || self::drifting( (string) $field['group'] ) ) { return null; } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 91cf5f2..8ebaeea 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -622,6 +622,7 @@ require_once TMDO_PATH . 'includes/engine/class-tmdo-query-compiler.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-schema-manager.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-entity-registry.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-entity-migration-engine.php'; +require_once TMDO_PATH . 'includes/diagnostic/class-tmdo-drift-check.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-entity-health.php'; require_once TMDO_PATH . 'includes/adapters/interface-entity-adapter.php'; require_once TMDO_PATH . 'includes/adapters/class-tmdo-adapter-post.php'; diff --git a/tests/integration/PostQueryRouterDriftTest.php b/tests/integration/PostQueryRouterDriftTest.php new file mode 100644 index 0000000..806ddca --- /dev/null +++ b/tests/integration/PostQueryRouterDriftTest.php @@ -0,0 +1,115 @@ + 'shadow_read', + 'user' => 'disabled', + 'term' => 'disabled', + 'comment' => 'disabled', + ) + ); + } + + private static function set_modes( ?array $modes ): void { + $cache = ( new ReflectionClass( WPDO_Mode_Manager::class ) )->getProperty( 'cache' ); + $cache->setAccessible( true ); + $cache->setValue( null, $modes ); + } + + private function drift( string $group, int $diff ): void { + $GLOBALS['_wp_options'][ TMDO_Drift_Check::OPTION ]['post'][ $group ] = array( + 'miss' => 0, + 'diff' => $diff, + 'extra' => 0, + 'sample' => 0, + 'checked_at' => '2026-09-27 00:00:00', + ); + } + + private function price_query( array $extra = array() ): WP_Query { + $query = new WP_Query(); + foreach ( array( + 'post_type' => 'product', + 'meta_query' => array( + array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), + ), + ) + $extra as $k => $v ) { + $query->set( $k, $v ); + } + return $query; + } + + public function test_a_drifting_group_keeps_the_query_native(): void { + $this->drift( 'wc_product', 3 ); + $query = $this->price_query(); + $original = $query->get( 'meta_query' ); + + ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); + + $this->assertSame( $original, $query->get( 'meta_query' ) ); + $this->assertSame( '', $query->get( 'wpdo_post_clauses' ) ); + } + + public function test_a_clean_group_is_routed(): void { + $this->drift( 'wc_product', 0 ); + $query = $this->price_query(); + + ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); + + $this->assertNotEmpty( $query->get( 'wpdo_post_clauses' ) ); + } + + public function test_a_group_never_checked_is_routed(): void { + $query = $this->price_query(); + + ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); + + $this->assertNotEmpty( $query->get( 'wpdo_post_clauses' ) ); + } + + public function test_drift_in_another_group_does_not_matter(): void { + $this->drift( 'hp_listing_core', 5 ); + $query = $this->price_query(); + + ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); + + $this->assertNotEmpty( $query->get( 'wpdo_post_clauses' ) ); + } + + public function test_sorting_by_a_drifting_key_stays_native(): void { + $this->drift( 'wc_product', 1 ); + $query = new WP_Query(); + $query->set( 'post_type', 'product' ); + $query->set( 'meta_key', '_price' ); + $query->set( 'orderby', 'meta_value_num' ); + + ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); + + $this->assertSame( '_price', $query->get( 'meta_key' ) ); + $this->assertSame( '', $query->get( 'wpdo_post_sort' ) ); + } +} diff --git a/tests/integration/bootstrap.php b/tests/integration/bootstrap.php index 1953369..318a7a5 100644 --- a/tests/integration/bootstrap.php +++ b/tests/integration/bootstrap.php @@ -586,6 +586,7 @@ require_once TMDO_PATH . 'includes/engine/class-tmdo-query-compiler.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-schema-manager.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-entity-registry.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-entity-migration-engine.php'; +require_once TMDO_PATH . 'includes/diagnostic/class-tmdo-drift-check.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-entity-health.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-auto-promoter.php'; require_once TMDO_PATH . 'includes/engine/class-tmdo-hook-bus.php';