From 0e97f802207ecea0c9a1ffe79753cf0146bc66c7 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Fri, 5 Jun 2026 01:37:14 +0000 Subject: [PATCH] Plugin Directory: Search: Only boost whole-slug matches, to prevent slug squatting. The slug boost is meant to reward searches looking for a specific plugin by its slug (e.g. `wordpress-seo`), not topic searches that happen to share one word with a slug. Because `slug_text` is word-tokenized and was part of the high-weight title `most_fields` clause, a single-word query like `map` matched a fragment of every `*-map(s)` slug and collected the full slug boost, letting plugins squat on common terms via their slug. Match the query against the whole-slug `slug` keyword field instead, normalised with `sanitize_title()`, via a `constant_score` clause. Slug searches still hit the expected plugin, while a single common word only matches a plugin whose entire slug is that word. See #8225. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../plugin-directory/class-plugin-search.php | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-search.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-search.php index a8085ed34e..95713ff44e 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-search.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-search.php @@ -274,16 +274,43 @@ public function jetpack_search_es_query_args( $es_query_args, $query ) { ], ]; - // A direct slug match + // Boost on a title match. $should_match[] = [ 'multi_match' => [ 'query' => $search_phrase, - 'fields' => $this->localise_es_fields( [ 'title', 'slug_text' ] ), + 'fields' => $this->localise_es_fields( [ 'title' ] ), 'type' => 'most_fields', 'boost' => 5, ], ]; + // A direct slug match. + // + // The slug boost is meant to reward searches that are looking for a + // specific plugin by its slug (e.g. `wordpress-seo`, `wp-google-maps`), + // not searches for a topic that happens to appear as one word of a slug. + // + // Previously `slug_text` was included in the title `most_fields` match + // above. Because `slug_text` is word-tokenized (`wp-google-maps` becomes + // `wp`, `google`, `maps`), a generic single-word query like `map` matched + // a fragment of every `*-map(s)` slug and collected the full slug boost. + // That let plugins squat on common terms by stuffing them into the slug. + // + // Instead we match the query against the whole-slug `slug` keyword field, + // normalised the same way the slug itself was generated. `sanitize_title()` + // turns `WP Google Maps` into `wp-google-maps`, so an actual slug search + // still scores a decisive, constant boost, while a single common word + // only matches a plugin whose entire slug is that word. + $slug_candidate = sanitize_title( $search_phrase ); + if ( $slug_candidate ) { + $should_match[] = [ + 'constant_score' => [ + 'filter' => [ 'term' => [ 'slug' => $slug_candidate ] ], + 'boost' => 8, + ], + ]; + } + $should_match[] = [ 'multi_match' => [ 'query' => $search_phrase,