FriendsOfREDAXO/search_it

Suchergebnis lückenhaft, trotz offenbar korrektem Index

Closed this issue · 30 comments

Beispiel: Suchbegriff 'Yoga'. Es gibt 6 Artikel mit dem Begriff im Titel, dazu einige bei denen er in weiteren Spalten steht. Gefunden werden aber nur 4. Es ist keine Regel zu erkennen, warum die einen auftauchen, die anderen nicht – im Index sind sie alle korrekt mit 'Yoga' erfasst. Es liegt auch nicht an meinem Suchergebnis-Templates, sondern die CMS-Testsuche verhält sich identisch.

Hmm...

search it indexiert eigentlich nur die Artikel-Inhalte. D.h. wenn deine Titel durch das Template ausgeben werden, werden sie nicht indexiert. Wenn das explizit gewünscht ist alle Artikel-Titel aufzunehmen, dann müsstest du die "art_title" Saplte von rex_article mit indexieren lassen.

Mit indiziert wird die Spalte 'name', die du vermutlich meintest, 'art_title' gibt es nicht. Das Obskure ist ja, dass es (mit nahezu identisch aufgebauten) Artikel mal funktioniert und mal nicht. Zweitens sieht der Index definitiv gut aus. So steht z. B. in rex_search_it_index/values das hier: 's:4:"name";s:21:"Yoga";}' der Artikel taucht aber nicht in den Treffern auf.

Nach längeren Untersuchungen traue ich mir inziwschen eine Hypothese zu: Der Index wird korrekt angelegt, aber nicht vollständig ausgewertet. Was gefunden werden soll, muss in den Spalten 'plaintext' oder 'unchangedtext' stehen, was ausschließlich in 'values' steht (also den zusätzlichen Feldern) wird nicht gefunden. Der Suchquery scheint 'values' entweder zu ignorieren oder nicht richtig zu erfassen. Ich habe das Spiel jetzt mit etwa 50 Suchbegriffen gespielt und meine Hypothese wurde in allen Fällen bestätigt oder zumindest nicht widerlegt. Nennen wir sie also ruhig belastbar.

Was gefunden werden soll, muss in den Spalten 'plaintext' oder 'unchangedtext' stehen,

Siehe:

search it indexiert eigentlich nur die Artikel-Inhalte. D.h. wenn deine Titel durch das Template ausgeben werden, werden sie nicht indexiert.

Dein Artikelname wird vermutlich nicht ausgegeben - und damit auch nicht indexiert.

Doch, er wird indexiert. Man braucht nur in die Spalte 'values' schauen, da steht er drin. Das ist ja der Punkt. Auch alles andere, das NUR in 'values' steht wird nicht gefunden. Wie gesagt: ca. 50-fach gecheckt, mit immer demselben Ergebnis.

Das ist der DB-Query:

SELECT SQL_CALC_FOUND_ROWS (SELECT SUM((( MATCH (`plaintext`,`unchangedtext`) AGAINST ('Yoga')) * 1) + 1) FROM `rex_search_it_index` summe WHERE summe.fid = r1.fid AND summe.ftable = r1.ftable) AS RELEVANCE_SEARCH_IT,
(SELECT COUNT(*) FROM `rex_search_it_index` summe WHERE summe.fid = r1.fid AND (summe.ftable IS NULL OR summe.ftable = r1.ftable) AND (summe.fcolumn IS NULL OR summe.fcolumn = r1.fcolumn) AND summe.texttype = r1.texttype) AS COUNT_SEARCH_IT,
`id`,
`fid`,
`catid`,
`ftable`,
`fcolumn`,
`texttype`,
`clang`,
`unchangedtext`,
`plaintext`,
`teaser`,
`values`,
`filename`,
`fileext`
            FROM `rex_search_it_index` r1
            WHERE (((((`plaintext` LIKE '%Yoga%') OR (`unchangedtext` LIKE '%Yoga%'))))) AND (
              (
                ((( MATCH (`plaintext`,`unchangedtext`) AGAINST ('Yoga')) * 1) + 1) = (SELECT MAX((( MATCH (`plaintext`,`unchangedtext`) AGAINST ('Yoga')) * 1) + 1) FROM `rex_search_it_index` r2 WHERE r1.ftable = r2.ftable AND r1.fid = r2.fid )
                AND fid IS NOT NULL
              ) OR
              ftable IS NULL
            )
            GROUP BY ftable,fid,clang
            ORDER BY RELEVANCE_SEARCH_IT DESC
            LIMIT 0,100`

In der WHERE-Zeile tauchen die 'values' nicht auf. Ändere ich die Zeile zu

`WHERE (((((`plaintext` LIKE '%Yoga%') OR (`unchangedtext` LIKE '%Yoga%') OR (`values` LIKE '%Yoga%'))))) AND (`

funktioniert es so, wie zumindest ich mir es vorstelle. Keine Ahnung, was jetzt wie genau geplant war, aber ich nehme mal an, die 'values' sollten eigentlich berücksichtigt werden, sonst wäre ihr Aufenthalt in der DB doch eigentlich sinnlos, oder?

@fitzliputz

Okay, ich korrigiere meine Aussage: Es ist nicht wichtig, ob der Name indexiert wird. Ausgewertet wird der Inhalt der Webseite - exakt so, wie du sie im Frontend siehst. d.h. wenn du den Artikelnamen dort nicht ausgibst, dann wird er auch nicht gefunden.

Es geht nicht darum, was du im Backend zum Artikel siehst.

Die value Spalte wird gefüllt duch die Indexierung der DB-Spalten.
Das sind später keine Ergebnisse vom Typ "article" sondern vom Typ "db_column". Du hast immer nur deinen Spezialfall vor Augen, wo die DB-Spalte aus einem Artikel stammt.
Der Such-Treffer ist trotzdem vom Typ db_column. Das musst du in deiner Ausgabe berücksichtigen.

@tyrant88 die Test-Suche im Backend gibt doch auch Datensätze aus, oder nicht? Daher glaube ich, wurde der Artikel-Name erst gar nicht indexiert.

@tyrant88 Kann gut sein, dass ich da was nicht kapiere. Meine Suchausgabe ist identisch mit jener der Test-Suche im Backend. Was habe ich denn davon, Metadaten in die Suche einzubeziehen, wenn sie nirgends berücksichtigt werden? Oder anders gefragt: Wie kann ich denn die Suchausgabe so beeinflussen, dass sowohl 'db_column' als auch 'article' ausgegeben werden? Otto-Normal-Sucher interessiert sich ja eher nicht für solche Feinheiten, sondern wundert sich, dass Artikel nicht gefunden werden, deren Überschrift er groß und breit lesen kann.

@alexplusde Okay, vielen Dank für den Link. Ich werde mich heute Abend mal damit befassen. Ich gebe dann Bescheid, was dabei herauskam. Übrigens ist deine Aussage

exakt so, wie du sie im Frontend siehst. d.h. wenn du den Artikelnamen dort nicht ausgibst, dann wird er auch nicht gefunden.

insofern nicht ganz nachzuvollziehen, als ich den Artikelnamen im Frontend sehr wohl ausgebe. Das hier ist z. B. die verwendete Standardkonstruktion:

<?php 
echo "<h1>".$this -> getName()."</h1>\n\n";
echo $this -> getArticle(1); 
?>

In den Spalten 'plaintext' und 'unchangedtext' befindet sich aber nur der Inhalt von $this->getArticle(1), die h1 fehlt.

In den Spalten 'plaintext' und 'unchangedtext' befindet sich aber nur der Inhalt von $this->getArticle(1), die h1 fehlt.

OK, dann hast du augenscheinlich alles richtig gemacht. Blöde Frage, aber der Suchindex ist auch aktuell? Also via Cronjob oder manuell aktualisiert?

Naja, wenn es im Template steht, kommt es nicht in den Index.
out-of-the-box nur die Artikelinhalte.
Genau für seinen Fall indexiert man dann zusätzlich die "name" Spalte.
Dann braucht man noch ein Ergebnismodul, welches auch die db-Treffer anzeigt.

Alles klar, ich war auf dem Stand einer früheren search_it-Version. Sorry, da habe ich mich getäuscht. Es werden nur die Inhalte der Blöcke (Module) ausgelesen, nicht der Template-Inhalt.

Dann gilt, was ich in der Anleitung weiter oben verlinkt habe. #207 (comment)

Ich habe jetzt versucht, die Ausgabe anzupassen, das führt aber leider nirgendwo hin. Zunächst habe ich festgestellt, dass meine Ausgabe weitgehend jener aus der verlinkten Anleitung entspricht. Die Letztere soll angeblich die Felder rex_article.name und rex_article.yrewrite_description ausgeben, ich kann im Beispiel-Code aber nicht erkennen, wo die ins Spiel kommen:

<section class="search_it-modul">
<p class="search_it-demotitle">[search_it] Suchergebnisse - Erweitertes Beispielmodul</p>
<?php
$server = rtrim(rex::getServer(),"/"); 
$request = rex_request('search', 'string', false); 

if($request) { 
    $search_it = new search_it(); 
    $result = $search_it->search($request); 
	# dump($result); // Zum Debuggen ausgeben.

    if($result['count']) { 
        echo '<h2 class="search_it-headline">{{ Suchergebnisse }}</h2>'; 

        echo '<ul class="search_it-results">';                           
        foreach($result['hits'] as $hit) { 

            # dump($hit);
            if($hit['type'] == 'db_column' AND $hit['table'] == rex::getTablePrefix().'article'){
                $text = $hit['article_teaser'];
            } else {
                $text = $hit['highlightedtext'];
            }

            if(($hit['type'] == 'db_column' AND $hit['table'] == rex::getTablePrefix().'article') || ($hit['type'] == 'article'))
            {
                $article = rex_article::get($hit['fid']); 
                echo '<li class="search_it-result search_it-article">
                          <p class="search_it-title">
                              <a href="'.$server.$article->getUrl().'" title="'.$article->getName().'">'.$article->getName().'</a>
                          </p>
                          <p class="search_it-url">'.$server.rex_getUrl($hit['fid'], $hit['clang'], array()).'</p>
                          <p class="search_it-teaser">'.$text.'</p>
                      </li>'; 
            } else {                                       
                echo '<p class="search_it-missing_type">Das Suchergebnis vom Typ <i class="search_it-type">'.$hit['type'].' </i> kann nicht dargestellt werden.</p>';
            }
        } 
        echo '</ul>';
    } else if(!$result['count']) { 
        echo '<p class="search_it-zero">Die Suche nach <i class="search_it-request">'. rex_escape($request).' </i> ergab keine Treffer.</p>';
    }
} 
    ?>
</section>

Oder anders gefragt: Meine (korrekt) indexierten Sonderspalten heißen name, art_p_refnam, art_p_unterz, art_p_vertex und art_teaser – Ende. Und das ist meine Suchausgabe:

<?php
$request = rex_request('search', 'string', false);
$res_out = "";

if ($request != "") {

    $search_it = new search_it();
    $result = $search_it -> search($request); // Suche ausführen
    $hits_count = $result['count'];
	
    // ++++ Ausgabe Treffer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    $res_out = "";
    $hits_num = 0;

    foreach($result['hits'] as $hit) :
        if($hit['type'] == 'db_column' AND $hit['table'] == rex::getTablePrefix().'article') $text = $hit['article_teaser'];
        else $text = $hit['highlightedtext'];
        // Text limitieren
        if (strlen($text) > 200) $text = substr($text, 0, 300)."&nbsp;…";

        if(($hit['type'] == 'db_column' AND $hit['table'] == rex::getTablePrefix().'article') || ($hit['type'] == 'article')) {
            
            // ---- Redaxo-Artikel-Objekt holen -------------------------------------
            $article = rex_article::get($hit['fid']);
            if (is_object($article)) {
            	if (!$article->isOnline()) continue;
            	$cur_name = $article -> getName();
            	// ---- Breadcrumb
				$tree = $article -> getParentTree();
				$out_breadcrumb = "";
				if (is_array($tree)) {		
					$numTree = count($tree);
					for($i = 0; $i < $numTree; $i++) {
						$art = $tree[$i];
						if ($art -> getValue("name") != $this -> getValue("name") && $art -> getValue("name") != $cur_name) {
							$out_breadcrumb .= $art -> getValue("name")."&nbsp;>&nbsp;";
						}
					}
				}
            	$res_out .= "<div class='c-suchtreffer'>\n";
            	$res_out .= "<a href='".$article->getUrl()."'>";
           		$res_out .= "<h3>".$out_breadcrumb.$cur_name."</h3>";
            	$res_out .= "<p>".$text."</p>";
            	$res_out .= "</a>\n</div>\n";
            }
        }
        $hits_num ++;
    endforeach;

    if ($hits_num > 0) {
        // Suchbegriff n mal enthalten
    } else {
       // Suchbegriff nicht enthalten
    }
}
?>

Wo bringe ich wie meinen Wunsch nach Ausgabe der besagten Sonderspalten zum Ausdruck? Sorry, aber Ich kapiere es wohl einfach nicht.

Hast du unter search_it in den Einstellungen bei zusätzlichen Quellen die Datenbankspalten rex_article.name mit ausgewählt?

Search it liefert dir aus dem Index in deinem Fall Articles, die die gesuchten Begriffe enthalten (im Text oder in deinen bei der Indexierung benutzten zusätzlichen Spalten).
Diese Artikel sind in dem result Array. Was du dann ausgibst ist deine Sache.
Wenn z.B. jedes Ergebnis ein Bild hätte, könntest du ein Bild mit Link zum Ergebnis ausgeben...
Die Beispiel-Module orientieren sich aber an klassischen SERPs.

Der gegenteilige Fall zu deinem ist eine YForm-Tabelle mit Datensätzen, aus denen eine Detailansicht für jeden Datensatz zusammengebaut wird. Da must du die Spalten auswählen, die die Suchwörter enthalten. Da gibt es dann keine Artikel als Ergebnis, sondern eben die Datensätze.

@alexplusde Ja klar, habe ich. Sonst würden die Angaben gar nicht erst in die Spalte 'values' gelangen.

@tyrant88 Das result Array enthält (mit meinem obigen Template) genau das, was ich schon die ganze Zeit betrauere. Nämlich nur die Artikel, die den Suchbegriff im Inhalt haben, bzw. in den DB-Spalten 'plaintext' und 'unchainedtext', aber nichts aus 'values', also den besagten Feldern. D. h. was hinten raus kommt, ist genau das, was im result Array steht. Ich schließe daraus, dass mein result nicht vollständig ist. Die Kernfrage lautet also nach wie vor, wie ich die doofen Meta-Daten ins result befördere.

Wenn du den result-array dumpst, müssen dort Ergbnisse vom typ db_column sein, wenn du eine Spalte irgendeiner Tabelle zusätzlich indexierst, die das Suchwort "yoga" enthält.
Was du in der Indextabelle in der Spalte "values" findest, erzeugt im result array Treffer vom Typ "db_result".

Dass sie das müssen, habe ich inzwischen ansatzweise kapiert. Nur tun sie es eben nicht. Das array result enthält auschließlich 'hits' des typs 'article'. Zwar gibt es in hits dieses typs auch values mit den besagten Feldern, aber eben nur dann, wenn der Begriff zusätzlich auch im normalen Artikel steht. Findet sich ein Suchbegriff ausschließlich in einem der Metadaten-Felder, bleibt result leer, d. h. es gibt keine hits, egal welchen typs. Es tut mir sehr leid, aber das ist nun mal das, was passiert. Ich kann das gerne ausführlicher dokumentieren, bevor am Ende noch der Eindruck ensteht, ich hätte sie nicht mehr alle.

Kann man sich das vlt. Mal online anschauen? Bisher bin ich davon ausgegangen, dass es eine Misskonfiguration ist.

Bist du im REDAXO Slack-Chat?

@alexplusde Ja, das ist kein Problem. Sehr banales Beispiel:

https://www.hospitalhof.de/?article_id=9&clang=0&search=pressieren&name=

Gesucht wird demzufolge nach dem Wort 'pressieren', das recht altbacken ist und insofern unter ca. 1300 Artikeln nur ein einziges Mal vorkommt, nämlich hier:

https://www.hospitalhof.de/programm/110919-warten-und-pressieren/

Wie leicht zu erkennen ist, steht 'pressieren' in der h1, also im Feld 'name', das auch korrekt indexiert wurde. Das result-Array enthält aber wie geagt keine hits.

Hier ist das zugehörige Feld 'values' aus dem Index:

a:5:{s:12:"art_p_refnam";s:28:"Prälat i. R. Paul Dieterich";s:12:"art_p_unterz";s:57:"Christoph Blumhardt, Seelsorger und Politiker (1842-1919)";s:12:"art_p_vertex";s:564:"Christoph Blumhardt (1842-1919), der in Bad Boll lange Zeit als Pfarrer und Seelsorger das Erbe seines Vaters pflegte, kam 1899 in Kontakt mit den damals bedrohten und in der Kirche verfemten Sozaildemokraten. Er stellte sich ganz auf die Seite derer, die um ihre Existenz kämpfen mussten, trat in die SPD ein und wurde ihr Abgeordneter im württembergischen Landtag. Wie er christliche Existenz in politischer Münze lebte, wie es ihm dabei ging und was er uns als Christ, Seelsorger und politisch lebender Mensch hinterlassen hat, ist das Thema dieses Vortrags.";s:10:"art_teaser";N;s:4:"name";s:21:"Warten und Pressieren";}

Du siehst, dass die besagten Felder 'art_p_unterz', 'art_p_vertex' etc. bis einschließlich 'name' erfasst wurden. Ich habe also eher kein Indexierungs- sondern ein Ausgabeproblem. Das ich möglicherweise selbst verschulde, aber ich weiß eben nicht womit.

Und ja, ich »bin« auch im Slack-Chat, das finde ich aber noch eine Ecke anstrengender als das hier. Wahrscheinlich bin ich eher so oldschool, so mit Ausdrucken am Biertisch und so. Wenn es was hilft, finde ich mich aber gerne dort ein.

Sorry, habe irrtümlich den Close-Button erwischt.

Sodele, und hier noch der schüttere Dump des pressieren-Results:

(
    [errormessages] => 
    [simwordsnewsearch] => 
    [simwords] => Array
        (
        )

    [count] => 0
    [hits] => Array
        (
        )

    [keywords] => Array
        (
            [0] => Array
                (
                    [search] => pressieren
                    [weight] => 1
                    [clang] => 
                )

        )

    [searchterm] => pressieren
    [sql] => 
            SELECT SQL_CALC_FOUND_ROWS (SELECT SUM((( MATCH (`plaintext`,`unchangedtext`) AGAINST ('pressieren')) * 1) + 1) FROM `rex_search_it_index` summe WHERE summe.fid = r1.fid AND summe.ftable = r1.ftable) AS RELEVANCE_SEARCH_IT,
(SELECT COUNT(*) FROM `rex_search_it_index` summe WHERE summe.fid = r1.fid AND (summe.ftable IS NULL OR summe.ftable = r1.ftable) AND (summe.fcolumn IS NULL OR summe.fcolumn = r1.fcolumn) AND summe.texttype = r1.texttype) AS COUNT_SEARCH_IT,
`id`,
`fid`,
`catid`,
`ftable`,
`fcolumn`,
`texttype`,
`clang`,
`unchangedtext`,
`plaintext`,
`teaser`,
`values`,
`filename`,
`fileext`
            FROM `rex_search_it_index` r1
            WHERE (((((`plaintext` LIKE '%pressieren%') OR (`unchangedtext` LIKE '%pressieren%'))))) AND (
              (
                ((( MATCH (`plaintext`,`unchangedtext`) AGAINST ('pressieren')) * 1) + 1) = (SELECT MAX((( MATCH (`plaintext`,`unchangedtext`) AGAINST ('pressieren')) * 1) + 1) FROM `rex_search_it_index` r2 WHERE r1.ftable = r2.ftable AND r1.fid = r2.fid )
                AND fid IS NOT NULL
              ) OR
              ftable IS NULL
            )
            GROUP BY ftable,fid,clang
            ORDER BY RELEVANCE_SEARCH_IT DESC
            LIMIT 0,100
    [blacklisted] => 
    [hash] => ca939c464bf614c3a45dd6e54327f493
    [time] => 0.000577926635742
)

Wie sieht denn die Zeile der Indextabelle aus? Du hast nur den value gepostet.

Die ganze Zeile sieht so aus:

INSERT INTO `rex_search_it_index` (`id`, `fid`, `catid`, `ftable`, `fcolumn`, `texttype`, `clang`, `filename`, `fileext`, `plaintext`, `unchangedtext`, `teaser`, `values`) 
VALUES
(
224, 
'5723', 
1, 
'rex_article', 
NULL, 
'article', 
1, 
NULL, 
NULL, 
'MIT Paul Dietrich\r\n Christoph Blumhardt (1842-1919), der in Bad Boll lange Zeit als Pfarrer und Seelsorger das Erbe seines Vaters pflegte, kam 1899 in Kontakt mit den damals bedrohten und in der Kirche verfemten Sozaildemokraten. Er stellte sich ganz auf die Seite derer, die um ihre Existenz kämpfen mussten, trat in die SPD ein und wurde ihr Abgeordneter im württembergischen Landtag. Wie er christliche Existenz in politischer Münze lebte, wie es ihm dabei ging und was er uns als Christ, Seelsorger und politisch lebender Mensch hinterlassen hat, ist das Thema dieses Vortrags.', 
' <div class=\'c-float\'>\n<figure>\n<a href=\'/media/dieterich_paul_hp.jpg\' class=\'fancy\' data-fancybox-group=\'otuwlbspx\' title=\'+\'><img src=\'index.php?rex_media_type=umfluss&amp;rex_media_file=dieterich_paul_hp.jpg\' alt=\'MIT Paul Dietrich\' width=\'250\' height=\'356\' /></a><figcaption>MIT Paul Dietrich</figcaption></figure>\n</div><div class=\'c-float-text\'>\n</div>\n\n<div class=\'clear\'></div>\n\n<p>Christoph Blumhardt (1842-1919), der in Bad Boll lange Zeit als Pfarrer und Seelsorger das Erbe seines Vaters pflegte, kam 1899 in Kontakt mit den damals bedrohten und in der Kirche verfemten Sozaildemokraten. Er stellte sich ganz auf die Seite derer, die um ihre Existenz kämpfen mussten, trat in die <span class=\"caps\">SPD</span> ein und wurde ihr Abgeordneter im württembergischen Landtag. Wie er christliche Existenz in politischer Münze lebte, wie es ihm dabei ging und was er uns als Christ, Seelsorger und politisch lebender Mensch hinterlassen hat, ist das Thema dieses Vortrags.</p>\n\n', 'MIT Paul Dietrich Christoph Blumhardt (1842-1919), der in Bad Boll lange Zeit als Pfarrer und Seelsorger das Erbe seines Vaters pflegte, kam 1899 in Kontakt mit den damals bedrohten und in der Kirche …', 
'a:5:{s:12:\"art_p_refnam\";s:28:\"Prälat i. R. Paul Dieterich\";s:12:\"art_p_unterz\";s:57:\"Christoph Blumhardt, Seelsorger und Politiker (1842-1919)\";s:12:\"art_p_vertex\";s:564:\"Christoph Blumhardt (1842-1919), der in Bad Boll lange Zeit als Pfarrer und Seelsorger das Erbe seines Vaters pflegte, kam 1899 in Kontakt mit den damals bedrohten und in der Kirche verfemten Sozaildemokraten. Er stellte sich ganz auf die Seite derer, die um ihre Existenz kämpfen mussten, trat in die SPD ein und wurde ihr Abgeordneter im württembergischen Landtag. Wie er christliche Existenz in politischer Münze lebte, wie es ihm dabei ging und was er uns als Christ, Seelsorger und politisch lebender Mensch hinterlassen hat, ist das Thema dieses Vortrags.\";s:10:\"art_teaser\";N;s:4:\"name\";s:21:\"Warten und Pressieren\";}')

@fitzliputz kannst du mir dann in Slack per Direktnachricht Zugangsdaten zum Backend schicken, damit ich mal rein schauen kann?

@alexplusde Okay, habe ich gerade abgeschickt.