Vor kurzem suchte ich nach einem Artikel im Blog von meinem sehr geschätzten Blogger-Kollegen Bernhard. Es ging um eine zusätzliche Sprache im Syntaxhighlighting-Plugin, was wir beide nutzen. Also suchte ich nach Syntaxhighlight in seinem Blog … aber ich fand viel zu viele Treffer, die das Suchwort gar nicht enthielten. Das erinnerte mich an etwas.
Da war doch was? Stimmt. Schon 2019 auf dem WordCamp Osnabrück hatte ich in meinem Talk zu diversen UX-Problemen im Block-Editor auf die „polluted search“ hingewiesen:
QuelleIch hatte dazu auch ein Issue für Gutenberg geöffnet (#10247), aber das Problem gab es natürlich schon: #3739
Leider schloss Gary Pendergast das Issue recht schnell mit folgendem Kommentar:
Unfortunately, this is a known issue in WordPress core – in a vanilla WordPress install, if you search for „table“, you’ll get results including
table
tags. MySQL’s string searching isn’t capable of dealing with this kind of contextual parsing.If you require 100% accurate search results, the best option is to use a dedicated search engine, like Elasticsearch. There are also Elasticsearch services available within the WordPress world, if setting up a dedicated search server is not an option.
Auch der Kommentar von Daniel Bachhuber hat daran nichts geändert:
Search Ignore HTML TagsDas Plugin nutzt vor allem zwei Techniken. Zum einen den sehr mächtigen posts_where
-Filter und zum anderen eine negierte regular expression via NOT REGEXP
.
Hier der Code als Plugin (UPDATE: Der Code funktioniert so nicht!):
<?php /** * Plugin Name: Ignore block name in search * Description: Updated the native search to ignore block editor comments * Version: 1.0 * Author: Torsten Landsiedel * License: GPL2 */ /* Based on "Search Ignore HTML Tags" by Pramod Sivadas wordpress.org/plugins/wp-search-ignore-html-tags/ */ /** * Modify search query to ignore comments * * @param [string] $where The WHERE clause of the query. * @return [string] Modified WHERE clause which ignores comments via regular expression */ function update_search_query( $where ) { if ( is_search() ) { global $wpdb; $query = get_search_query(); $query = $wpdb->esc_like( $query ); $where .= " AND {$wpdb->posts}.post_content NOT REGEXP '\<\!\-\-.*$query.*\-\-\>' "; } return $where; } add_filter( 'posts_where', 'update_search_query' );
Wir hängen eine Funktion namens update_search_query
an den posts_where
-Filter. Darin verändern wir die Abfrage nur, wenn es sich um eine Suche handelt (is_search
). Dann holen wir uns das globale Datenbankobjekt ($wpdb) und den Such-Query. Zur Sicherheit wird der Query escaped und um eine weitere Bedingung ergänzt.
Hier ist jetzt das Herzstück dieses kleinen Plugins. Die zusätzliche Bedingung ist, dass der Suchbegriff ($query
) nicht durch die regular expression gefunden wird. Wobei diese regular expression die Suche nach dem Begriff innerhalb von Kommentaren ist, also zwischen <!--
und -->
steht.
Im Original war die regular expression \<{1}.*$query.*\>{1}
, womit nur Texte innerhalb von <
und >
erfasst wurden.
Das Plugin läuft nun bei mir und zeigt für die Suche nach syntaxhighlight
tatsächlich nur noch den einen Beitrag (bzw. bald auch diesen hier), wo das Wort wirklich im Text steht.
Das Plugin von Pramod Sivadas hat damals leider kaum jemanden interessiert, wahrscheinlich weil das Problem mit HTML-Tags nicht so schlimm war. Es hat nur eine 1.0-Version, nur einen Thread im Forum und ist seit 8 Jahren nicht mehr angefasst worden. Daher fehlt eine breitere Testung der Technik. Könnte es Probleme geben?
Freue mich über Feedback zu möglichen Problemen, edge cases, und dergleichen in den Kommentaren!
Update 1: Das Plugin ist jetzt auch im offiziellen Plugin-Verzeichnis und auf GitHub!
Update 2: Leider habe ich die Funktionsweise von MySQL falsch verstanden. Die Abfrage sorgt dafür, dass eine zusätzliche Bedingung gelten muss. Diese lautet, dass der Suchbegriff nicht in einem Kommentar stehen darf. Das bedeutet, dass eine Suche nach „Paragraph“ ungünstigerweise auch dann nichts zurückliefert, wenn „Paragraph“ im Inhalt steht, sobald der String in einem HTML-Kommentar auftaucht. Also auch keine Lösung für das Problem.
Eine Lösung könnte REGEXP_REPLACE
sein, aber das ist erst mit MariaDB 10.0.5 bzw. MySQL 8.0.4 verfügbar und noch habe ich das leider nicht zum Laufen bekommen.
Danke an @ravishaheshan für den Hinweis!
Update 3: Für 8.0.4+ und MariaDB 10.0.5+ gibt es jetzt die funktionierende Lösung in meinem Plugin!
Der Code scheint wirklich gut zu funktionieren. Allerdings braucht, es glaube ich, die vielen Escape-Zeichen nicht. Es sollte also auch mit
'<!--.*$query.*-->'
funktionieren.Eine andere Kleinigkeit, die du ändern solltest: die zusätzliche Where-Clause wird so an alle Queries auf der Suchseite angehängt. Das ist natürlich zum einen unnötig und führt zum anderen dazu, dass manche Queries kaputt gehen, wenn
wp_posts
nicht gejoined ist. Besser also einen „Early Return“ machen, wenn es nicht die Main-Query ist.Bei mir würde die Funktion im Ergebnis dann wie folgt aussehen:
Vielen Dank! Ja, das war ein schneller Proof-of-concept. Danke fürs Ausbügeln 🙂
Gibt es eigentlich hierzu auch ein Core Ticket und könnte man vielleicht deine Lösung als Patch vorschlagen? Vielleicht auch inkl. der Bedingung zur Ignorierung von HTML Tag Namen?
Habe auf Trac noch nichts gefunden dazu. Die Gutenberg-Issues sind oben im Artikel verlinkt.
Hallo Torsten und Bernhard,
ich liebe so kleine Snippets mit großer Wirkung.
Vielen Dank dafür.
Jochen
Da die Lösung eine höhere Datenbank-Version erfordert als WordPress ist die Lösung auf „maybelater“ gestellt. Hier das Trac-Ticket dazu:
https://core.trac.wordpress.org/ticket/56294
Achtung an alle: Der Code oben ist falsch und funktioniert nicht wie gewünscht. Die funktionierende Lösung findet ihr in meinem Plugin: https://github.com/Zodiac1978/wp-search-ignore-block-names