Herzlicher: Mysql: Ausschlaggebende Teilstrings bei LIKE-Vergleichen

Hallo,
man betrachte folgende vereinfachte Volltextsuche

SELECT * FROM artikel_tabelle WHERE artikel_text LIKE '%such_string%'

Man kann annehmen, dass such_string "gestemmt" wurde, d.h. auf seine Stammform gebracht wurde, z.B. aus "internationalen" wird "international".

Jetzt möchte ich bei Anzeige eines Treffer-Datensatzes die entscheidenden Wörter gelb markieren. Beispielsweise kommt im Artikel mit der id 25 das Wort "internationalem" vor, das ihn zu einem Treffer machte. Dieses Wort soll ungestemmt gelb markiert werden.

Um das zu bewerkstelligen, kann ich irgendwie die Teilstrings in "artikel_text" durch die mysql query zurückbekommen, die den Vergleich artikel_text LIKE '%such_string%' bei einem bestimmten Datensatz erfolgreich gemacht haben?

Oder wie würde man effizient vorgehen?

Bin für jeden konstruktiven Rat dankbar.

  1. Hallo liebe(r) SELFHTML-Benutzer,

    Wenn ich dich richtig verstanden habe, holst du einen Datensatz der $such_string enthält, und lässt die Ergebnisse in einer Schleife auswerten.

    Diese Schleife listet dir alle gefundenen Einträge untereinander oder wie auch immer. Wieso nicht in dieser Schleife eine Highlight-Funktion einsetzen, welche in der Datensatz-Zeile nach $such_string sucht und diesen Abschnitt mit einer CSS-Klasse hervorhebt?
    Z.B. str_replace(needle $such_string, replacement "<b>$such_string</b>", haystack $row);
    Ist etwas hard-coded aber das Prinzip sollte klar sein.

    Wie immer vielen Dank für's Lesen.
    Mit freundlichem Gruß,
    Rene Schindhelm

    1. Hallo Rene,

      Wieso nicht in dieser Schleife eine Highlight-Funktion einsetzen,

      richtig, genau DIE sucht er ja ...

      Es soll z.B. aus den 'haus'-Fundstuecken:
          'Haustüren sind breiter als normale Türen.'
          'Das hohe Baumhaus schwankt im Wind.'
          'Die Adresse ist ron@hausieren.org.'
          'Ich sitze im Hausboot, hier unten am River.'
          'Er blickte durch die Glashaustür direkt ins Bad.'
          'Hier ist Betteln und Hausieren verboten.'
          'Für die Jacht gab es ein eigenes Bootshaus.'
      dieses werden:
          '<b>Haustüren</b> sind breiter als normale Türen.'
          'Das hohe <b>Baumhaus</b> schwankt im Wind.'
          'Die Adresse ist <ron@<b>hausieren</b>.org>.'
          'Ich sitze im <b>Hausboot</b>, hier unten am River.'
          'Er blickte durch die <b>Glashaustür</b> direkt ins Bad.'
          'Hier ist Betteln und <b>Hausieren</b> verboten.'
          'Für die Jacht gab es ein eigenes <b>Bootshaus</b>.'

      Vermutlich ist dies nicht einfach mit str_replace zu realisieren.

      Gruss Norbert

      1. Ich wollte auch erstmal wissen, ob eine mysql query infos darüber liefern kann, welcher teilstring im artikel_text für den treffer gemäß LIKE-%%-Vergleich verantwortlich war.

        Beispieldatensatz und -suchstring:
        artikel_text: "Im internationalen Vergleich schneiden deutsche Unternehmen zunehmend schwächer ab."
        gestemmter such_string sei: "international"

        Ein LIKE-Vergleich mit ... WHERE artikel_text LIKE '%international%'
        würde einen Treffer bei diesem Datensatz hervorrufen.

        Kann mir der mysql query auch einfach "internationalen" zurückliefern, also den Teilstring, dem der Treffer zu verdanken ist?

        Oder führt kein Weg an der Übergabe des gestemmten Wortes "international" an das den Datensatz anzeigende PHP-Skript, welches dann selbständig Begriffe mit diesem Stamm heraussucht und bei der Ausgabe hervorhebt?

        1. Hello,

          Ich wollte auch erstmal wissen, ob eine mysql query infos darüber liefern kann, welcher teilstring im artikel_text für den treffer gemäß LIKE-%%-Vergleich verantwortlich war.

          Da müsstest Du dann wohl mal lesen: http://dev.mysql.com/doc/refman/5.0/en/regexp.html
          und uns bitte informieren, wenn Du es rausgefunden hast ;-))

          Ich will jetzt erstmal rausfinden, warum preg_match() nicht so arbeitet, wie ich mir das vorgestellt hatte...

          Harzliche Grüße vom Berg
          http://bergpost.annerschbarrich.de

          Tom

          --
          Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
          Nur selber lernen macht schlau
          Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

          1. Tom,
            ich weiß nicht, was mir der Link helfen soll. Da sind nur die Regexp für Mysql erklärt.
            Ich glaube nicht, dass mir ein SELECT query diese Informationen effizient bringen kann.

            Ich werde wohl schauen, wie ich das anzeigende Skript geschickt dazu bringen kann, die Wörter mit einem gegebenen Stamm hervorzuheben. Es wird wohl auch über reg. expr. laufen.

            1. Hello,

              ich weiß nicht, was mir der Link helfen soll. Da sind nur die Regexp für Mysql erklärt.

              Es war nur eine Idee, wo Du, wenn überhaupt, fündig werden könntest

              Ich habe bei Datenbanken bisher als maximale matching methods 'like' benutzt. Die RegWxp waren mir immer ein Graus und laut bisherigem Wissen auch Performancekiller. Like '%abc%' ist das vermutlich auch...

              Harzliche Grüße vom Berg
              http://bergpost.annerschbarrich.de

              Tom

              --
              Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
              Nur selber lernen macht schlau
              Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

            2. Hallo

              ich weiß nicht, was mir der Link helfen soll. Da sind nur die Regexp für Mysql erklärt.

              und die Funktion REGEXP liefert entweder eine 1 oder eine 0 zurück. Mehr nicht.
              Dafür ist sie auch nicht gedacht.

              Freundliche Grüße

              Vinzenz

            3. Dank dem kleinen Tool Regexbuddy und einer Online-Doku zu regulären Ausdrücken habe ich herausgefunden, das ein Aufruf von preg_replace mit dem regulären Ausdruck \b[a-zA-Z0-9]*international[a-zA-Z0-9]*\b Wörter ersetzen würde, die international enthalten.

              Zur Erläuterung
              \b markiert Wortanfang u. -ende innerhalb eines Strings
              Die Zeichen in den [] sind durch das * gar nicht oder mehrmals vorhanden.

              Falls einem Optimierungen einfallen, so poste er sie bitte hier.

              1. Hello,

                Dank dem kleinen Tool Regexbuddy und einer Online-Doku zu regulären Ausdrücken habe ich herausgefunden, das ein Aufruf von preg_replace mit dem regulären Ausdruck \b[a-zA-Z0-9]*international[a-zA-Z0-9]*\b Wörter ersetzen würde, die international enthalten.

                Soweit war ich im Prinzip schon in https://forum.selfhtml.org/?t=163670&m=1065915

                Hier nochmal das Testprogramm dazu;

                <?php   ### preg_replace_highlight.php  ###

                $_text = array();

                #$_text[] = "In internationalem Flair fand das letzte internationale Self-Treffen statt";

                $_text[] = 'Haustüren sind breiter als normale Türen.';
                $_text[] = 'Das hohe Baumhaus schwankt im Wind.';
                $_text[] = 'Die Adresse ist ron@hausieren.org.';
                $_text[] = 'Ich sitze im Hausboot, hier unten am River.';
                $_text[] = 'Er blickte durch die Glashaustür direkt ins Bad.';
                $_text[] = 'Hier ist Betteln und Hausieren verboten.';
                $_text[] = 'Für die Jacht gab es ein eigenes Bootshaus.';

                $such = "haus";

                foreach($_text as $text)
                {
                  #$pattern = '@\b([^ ]*' . $such . '[^ ]*)\b@ui';
                  #$pattern = '@\b([^ ]*' . $such . '.*)\b@ui';
                  #$pattern = '@.*?([^ ]*' . $such . '[^ ]*).*?@ui';
                  #$pattern = '@(\b.*?'.$such.'.*?\b)@ui';

                $pattern = '@\b([a-zA-Z0-9]*'.$such.'[a-zA-Z0-9]*)\b@ui';

                $replace = '<span class="highlight">${1}</span>';

                $text = preg_replace($pattern, $replace, $text);

                echo htmlspecialchars($pattern)."<br />\n";
                  echo $text  . "<br />\n";
                }

                ?>

                Leider funktionieren die alle nicht und ich finde auch den Denkfehler nicht... :-((

                Harzliche Grüße vom Berg
                http://bergpost.annerschbarrich.de

                Tom

                --
                Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                Nur selber lernen macht schlau
                Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

                1. Hallo Tom,

                  Leider funktionieren die alle nicht und ich finde auch den Denkfehler nicht... :-((

                  hmm,
                  soo kann man das auch wieder nicht sagen, siehe hier.

                  Uebrigens, mit dem 'i' schaltet man auf caseunsensitive, und das 'u'?
                  Und wenn man das 'u' entfernt wird die Trefferquote besser ...

                  Gruss und Dank
                  Norbert

                2. Es müssen noch Umlaute, ß und case-insensitivity mit /i am Ende eingebracht werden.

                  Bei mir sieht der fertige PHP Befehl so aus:

                  $maintext=preg_replace('/\b[a-zA-Z0-9äüöÄÜÖß]*international[a-zA-Z0-9äüöÄÜÖß]*\b/i','<b>$0</b>',$maintext);

                  Alle Wörter, die international als Stamm enthalten, werden fett ausgegeben.

                  Check it out! ;)

                  Ich habe das mal mit deinen Sätzen durchgespielt und es hat geklappt.

                  Wenn man noch möchte, dass die ganze E-Mail-Adresse erfasst wird, muss man @ und . in die eckigen Klammern einfügen.

                  Hoffe das hilft.

                  VG
                  Herzlichst

                  1. Hello,

                    Es müssen noch Umlaute, ß und case-insensitivity mit /i am Ende eingebracht werden.

                    $maintext=preg_replace('/\b[a-zA-Z0-9äüöÄÜÖß]*international[a-zA-Z0-9äüöÄÜÖß]*\b/i','<b>$0</b>',$maintext);

                    Das ist mal wieder so ein typischer Fall von "denkste"...
                    Hab ich vorhin so gedacht, man könnte ja auch die Zeichenklassen alle vollständig angeben, hab's dann aber gelassen und lieber den Punkt oder "nicht Leerzeichen" genommen.

                    Damit habe ich wirklich NICHT gerechnet, dass ein Punkt kein äöü usw. matcht und ein "nicht Leerzeichen" das auch nicht tut.

                    Wenn mir nun noch jemand verrät, ob und wie man eine Backreferenz als negierte Zeichenklasse nutzbar machen kann, dann wäre mein Bedarf an Regular Expressions für heute gedeckt ;-)

                    Harzliche Grüße vom Berg
                    http://bergpost.annerschbarrich.de

                    Tom

                    --
                    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                    Nur selber lernen macht schlau
                    Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

                    1. Hallo Tom,

                      habe jetzt Deine Loesung in die Demo-Seite uebernommen, klappt hervorragend!

                      Damit habe ich wirklich NICHT gerechnet, dass ein Punkt kein äöü usw. matcht und ein "nicht Leerzeichen" das auch nicht tut.

                      na-ja,
                      angeblich gibt es die Unterscheidung zwischen Text- und Binaerdatei NUR in Windoof, aber zwischen 'normalen' Text-Zeichen und dem Rest wird unter Unix sehr wohl unterschieden. Und europaeische Sonderzeichen sind danach keine Text-Zeichen. Desderwegen wurden zu Beginn des Binaeren Zeitalters Mails mit 'ß' oft im WROM abgelegt und ich habe mir angewoehnt alle Umlaute zu umschreiben.

                      Gruss und Dank
                      Norbert

                      1. Hallo allerseits,

                        die Loesung wurde wesentlich optimiert und plattformuebergreifend gestaltet.
                        Offensichtlich veringert ein passendes setlocale() den ganzen Aufwand enorm.
                        Die Demo-Seite zeigt auch wieder den neuen Code an.

                        Gruss und Dank
                        Norbert

                3. Hi,

                  $_text[] = 'Haustüren sind breiter als normale Türen.';

                  Ist das UTF-8-codiert?

                  #$pattern = '@\b([^ ]*' . $such . '[^ ]*)\b@ui';

                  Weil Du hier mit dem u-Modifier den UTF-8-Modus aktivierst ...

                  cu,
                  Andreas

                  --
                  Warum nennt sich Andreas hier MudGuard?
                  O o ostern ...
                  Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                  1. Hello,

                    $_text[] = 'Haustüren sind breiter als normale Türen.';

                    Ist das UTF-8-codiert?

                    #$pattern = '@\b([^ ]*' . $such . '[^ ]*)\b@ui';

                    Weil Du hier mit dem u-Modifier den UTF-8-Modus aktivierst ...

                    Das habe ich noch gemerkt und dann U für "switch greedyness" genommen, was ich eigentlich auch haben wollte. Hat aber am Matching des Punktes auch nichts geändert.

                    Harzliche Grüße vom Berg
                    http://bergpost.annerschbarrich.de

                    Tom

                    --
                    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                    Nur selber lernen macht schlau
                    Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

        2. Hallo,

          Kann mir der mysql query auch einfach "internationalen" zurückliefern,
          also den Teilstring, dem der Treffer zu verdanken ist?

          Rein IMHO nicht,
          weil MySQL den Teilstring gar nicht kennt.
          Es hat im Feld den Suchstring gefunden und bricht die Suche damit erfolgreich ab.
          Eine tiefergehende Analyse war ja auch nicht in Auftrag gegeben ... ;-)

          Gruss Norbert

  2. Hello,

    Jetzt möchte ich bei Anzeige eines Treffer-Datensatzes die entscheidenden Wörter gelb markieren. Beispielsweise kommt im Artikel mit der id 25 das Wort "internationalem" vor, das ihn zu einem Treffer machte. Dieses Wort soll ungestemmt gelb markiert werden.

    Das könnte eventuell mit preg_replace() klappen
    http://de2.php.net/manual/en/function.preg-replace.php

    <?php   ### preg_replace_highlight.php  ###

    $text = "In internationalem Flair fand das letzte internationale Self-Treffen statt";

    $such = "international";

    $pattern = '@\b([^ ]*?' . $such . '.*?)\b@ui';

    $replace = '<span class="highlight">${1}</span>';

    $text = preg_replace($pattern, $replace, $text);

    echo htmlspecialchars($pattern)."<br />\n";
    echo $text  . "<br />\n";

    ?>

    Nicht zufrieden bin ich mit dem [^ ] vor dem Suchbegriff, denn der könnte ja auch hinter einem Komma  oder am Satzanfang stehen. Aber wenn ich stattdessen '.*?' nehme, wird das 'In ' immer mit in den Begriff reingenommen...

    Also auch nochmal überarbeitungsbedürftig durch den Meister der Regular Expressions..

    Harzliche Grüße vom Berg
    http://bergpost.annerschbarrich.de

    Tom

    --
    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
    Nur selber lernen macht schlau
    Ein Jammer ist auch, dass die Dummen so selbstsicher und die Klugen voller Zweifel sind. Das sollte uns häufiger zweifeln lassen :-)

    1. Hallo Tom,

      habe mir erlaubt, Deinen Vorschlag auf meine Beispiele (siehe unten) anzuwenden:

      • im ersten String wird 'Haustüren' ignoriert
      • im zweiten, vierten und sexten String klappt es prima
      • im dritten String wird 'ron@hausieren' gehighlighted
      • im fuenften String wird 'Glashaustür' ignoriert
      • im siebenten String wird 'Bootshaus' ignoriert

      Leider sind RegExen nicht meine Domaine, aber ein guter Start ist es allemal.

      Gruss und Dank
      Norbert