da die "bordeigene" Suche, zumindest im Moment, nicht so funktioniert wie sie soll (http://forum.contenido.org/viewtopic.php?f=92&t=34513), habe ich mir selbst eine geschneidert. Dabei wird auch Rücksicht genommen auf ggf. nicht für den Suchenden freigegebene Kategorien, ob Artikel on- oder offline sind und ob sie als "suchbar" in den Artikeleigenschaften deklariert wurden. Darüber hinaus besteht die Möglichkeit,
# die Mindestlänge für Suchworte sowie
# maximale Anzahl aufzulistender Suchtreffer und
# die Länge des Textteasers festzulegen (Kommt hier das Suchwort drin vor, wird es gehighlighted)
# eine Gewichtung der Suchtreffer vorzunehmen - je nachdem, ob der Suchstring in einer Headline, in einem Textelement und/oder in den Metaangaben zu keywords und description vorkommt -
# entsprechende Listung und Prozentanzeige im Frontend inklusive.
Ggf. schiebe ich noch eine Pagination und ein Logging der Suchbegriffe mit diversen Filtermodi (alphabetisch, nach Zeitraum, Anzahl der Eingaben, ...) nach.
Wichtig:
- Alle Korrekturen, Anmerkungen usw. der Folgepostings sind in den Quelltexten dieses Eröffnungsposts berücksichtigt.
- Wenn Suchbegriffe mit Umlauten nicht gefunden werden sollten, dann bitte in der Zeile
$conversion=utf8_encode(html_entity_decode(stripslashes(mb_strtolower($array_crawler[value]))));
utf8_encode rausnehmen:
$conversion=html_entity_decode(stripslashes(mb_strtolower($array_crawler[value])));
neues Modul "search_sd" anlegen mit diesen Bestandteilen:
Input:
Code: Alles auswählen
/***********************************************
* CONTENIDO MODUL - INPUT
*
* Modulname : search_sd
* Author(s) : Seamless-Design Markus Hübner
* Copyright : Seamless-Design Markus Hübner
* Created : 05.02.2015
************************************************/
echo mi18n("input - discard hit if category is");
echo '<table>';
echo '<tr><td>'.mi18n("input - offline").'</td>';
echo '<td>';
if ("CMS_VALUE[100]" !="true") echo '<input type="radio" name="CMS_VAR[100]" value="true"> '.mi18n("input - yes");
else echo '<input type="radio" name="CMS_VAR[100]" value="true" checked> '.mi18n("input - yes");
if ("CMS_VALUE[100]" != "false") echo '<input type="radio" name="CMS_VAR[100]" value="false"> '.mi18n("input - no");
else echo '<input type="radio" name="CMS_VAR[100]" value="false" checked> '.mi18n("input - no");
echo '</td></tr>';
echo '<tr><td>'.mi18n("input - secured").'</td>';
echo '<td>';
if ("CMS_VALUE[110]" !="true") echo '<input type="radio" name="CMS_VAR[110]" value="true"> '.mi18n("input - yes");
else echo '<input type="radio" name="CMS_VAR[110]" value="true" checked> '.mi18n("input - yes");
if ("CMS_VALUE[110]" != "false") echo '<input type="radio" name="CMS_VAR[110]" value="false"> '.mi18n("input - no");
else echo '<input type="radio" name="CMS_VAR[110]" value="false" checked> '.mi18n("input - no");
echo '</td></tr>';
echo '<tr><td>'.mi18n("input - secured but unlocked for fe-user group").'</td>';
echo '<td>';
if ("CMS_VALUE[120]" !="true") echo '<input type="radio" name="CMS_VAR[120]" value="true"> '.mi18n("input - yes");
else echo '<input type="radio" name="CMS_VAR[120]" value="true" checked> '.mi18n("input - yes");
if ("CMS_VALUE[120]" != "false") echo '<input type="radio" name="CMS_VAR[120]" value="false"> '.mi18n("input - no");
else echo '<input type="radio" name="CMS_VAR[120]" value="false" checked> '.mi18n("input - no");
echo '</td></tr>';
echo '</table>';
echo '<table>';
echo '<tr><td>'.mi18n("input - maximum length of description").'</td><td><input type="text" name="CMS_VAR[200]" value="CMS_VALUE[200]"/></td></tr>';
echo '<tr><td>'.mi18n("input - maximum amount of hits").'</td><td><input type="text" name="CMS_VAR[300]" value="CMS_VALUE[300]"/></td></tr>';
echo '<tr><td>'.mi18n("input - minimum length of searchstring").'</td><td><input type="text" name="CMS_VAR[400]" value="CMS_VALUE[400]"/></td></tr>';
echo '<tr><td>'.mi18n("input - hints for multiplyers").'</td></tr>';
echo '<tr><td>'.mi18n("input - multiplyer hit in headline").'</td><td><input type="text" name="CMS_VAR[500]" value="CMS_VALUE[500]"/></td></tr>';
echo '<tr><td>'.mi18n("input - multiplyer hit in text").'</td><td><input type="text" name="CMS_VAR[510]" value="CMS_VALUE[510]"/></td></tr>';
echo '<tr><td>'.mi18n("input - multiplyer hit in meta keywords").'</td><td><input type="text" name="CMS_VAR[520]" value="CMS_VALUE[520]"/></td></tr>';
echo '<tr><td>'.mi18n("input - multiplyer hit in meta description").'</td><td><input type="text" name="CMS_VAR[530]" value="CMS_VALUE[530]"/></td></tr>';
echo '</table>';
Code: Alles auswählen
<?php
/***********************************************
* CONTENIDO MODUL - OUTPUT
*
* Modulname : search_sd
* Author(s) : Seamless-Design Markus Hübner
* Copyright : Seamless-Design Markus Hübner
* Created : 05.02.2015
************************************************/
/*
TODOs:
# check, if idtype is really used! -> searchstring -> PIFA-Form is found within database - although PIFA-Form is no longer used/online
*/
// assert framework initialization
defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
/*########## variables begin ##########*/
$do_not_add_if_cat_offline="CMS_VALUE[100]";
$do_not_add_if_cat_secured="CMS_VALUE[110]";
$do_not_add_if_cat_secured_but_unlocked_for_fe_group="CMS_VALUE[120]";
$length_text_max="CMS_VALUE[200]"; // how many letters should be displayed before result is cut
$max_amount_hits_displayed="CMS_VALUE[300]"; // becomes a role after complete data is searched, because of ranking! not the first found articles have to be displayed but the most important ones!
if($max_amount_hits_displayed=="") $max_amount_hits_displayed=10;
if($max_amount_hits_displayed>100) $max_amount_hits_displayed=100;
$searchstring_minimum_length="CMS_VALUE[400]";
/*
for each idtype you can define worth of hit - e.g. a hit within a headline (idtype 1) is worthier than within normal text, so a hit within a headline could be counted twice. Also metadata is being checked, because explicitly mentioned here, the article is normally more important than articles without mentioning
searchstring in metadata. After this, hits are used as a score for ordering search results.
have a look at con_type for idtype-definition!
*/
$hit_multiplier_headline="CMS_VALUE[500]";
if($hit_multiplier_headline=="") $hit_multiplier_headline=2;
$hit_multiplier_text="CMS_VALUE[510]";
if($hit_multiplier_text=="") $hit_multiplier_text=1;
$hit_multiplier_meta_keywords="CMS_VALUE[520]"; // search string is found within keywords or description
if($hit_multiplier_meta_keywords=="") $hit_multiplier_meta_keywords=1.5;
$hit_multiplier_meta_description="CMS_VALUE[530]"; // search string is found within keywords or description
if($hit_multiplier_meta_description=="") $hit_multiplier_meta_description=1.5;
$string_search_input="";
$string_search_input=strip_tags($_POST['searchterm']);
$array_search_input=array('"','\'',''','"','x');
$array_replace_input=array('','','apos','quot','u');
$string_search_input=str_replace($array_search_input,$array_replace_input,$string_search_input);
$array_search=array('"','\'',''','"','Ü','Ä','Ö','ü','ä','ö','ß');
$array_replace=array('','','apos','quot','Ãœ','Ä','Ã-','ü','ä','ö','ß');
$string_search=addslashes(strip_tags(mb_strtolower($_POST['searchterm'])));
$string_search=str_replace($array_search,$array_replace,$string_search);
/****** checking which idcats are allowed for fe-user and if fe-user has global rights begin ******/
if($do_not_add_if_cat_secured=="false" OR $do_not_add_if_cat_secured_but_unlocked_for_fe_group=="false")
{
$id_feu=$auth->auth["uid"];
$query = "SELECT idfrontendgroup FROM con_frontendgroupmembers WHERE idfrontenduser='$id_feu'";
$result = cRegistry::getDb();
$result->query($query);
while($result->nextRecord())
{
$arr_fe_groups=$result->toArray();
}
$visitor_fe_group=$arr_fe_groups['idfrontendgroup'];
$arr_visitor_fe_group_allowed_cat_ids=array();
$query = "SELECT item FROM con_frontendpermissions WHERE idfrontendgroup='$visitor_fe_group'";
$result = cRegistry::getDb();
$result->query($query);
while($result->nextRecord())
{
$arr_result=$result->toArray();
$arr_visitor_fe_group_allowed_cat_ids[]=$arr_result[item];
}
//print_r($arr_visitor_fe_group_allowed_cat_ids);
$fe_user_global_rights="false";
if(in_array('__GLOBAL__',$arr_visitor_fe_group_allowed_cat_ids)) $fe_user_global_rights="true";
}
/****** checking which idcats are allowed for fe-user and if fe-user has global rights end ******/
/*########## variables end ##########*/
/*########## functions begin ##########*/
function show_result($show_content_hit_idart,$show_content_lang,$string_search_input,$hits,$hits_maximum,$length_text_max,$hit_number)
{
$hit_number++; //delivered number is array-pointer, therefor first hit has number 0. so hit_number has to be increased by 1
$array_search=array('Ü','Ä','Ö','ü','ä','ö','ß');
$array_replace=array('Ü','Ä','Ö','ü','ä','ö','ß');
$article = new cApiArticleLanguage();
$article->loadByArticleAndLanguageId($show_content_hit_idart,$show_content_lang);
echo '<div class="s_sd_result">';
$width_relevance=round(($hits/$hits_maximum)*100,0);
echo '<div class="s_sd_result_number">'.$hit_number.')</div>';
echo '<div class="s_sd_relevance">';
echo '<div class="s_sd_relevance_score" style="width:'.$width_relevance.'%;">';
echo $width_relevance.'%';
echo '</div>';
echo '</div>';
if($article->getContent('CMS_HTMLHEAD', 1)!="")
{
echo '<div class="s_sd_result_headline">'.strip_tags($article->getContent('CMS_HTMLHEAD', 1)).'</div>';
}
if($article->getContent('CMS_HTML', 1)!="")
{
/*highlighting exact string regarding upper-/lowercase*/
$string_search_input=str_replace($array_search,$array_replace,$string_search_input);
$show=str_replace($string_search_input, '<span class="s_sd_highlight_item">'.$string_search_input.'</span>',substr(strip_tags($article->getContent('CMS_HTML', 1)),0,$length_text_max));
/*highlighting string even if search-string within input-field is written uppercase*/
$string_search_input_strtolower=strtolower($string_search_input);
$show=str_replace($string_search_input_strtolower, '<span class="s_sd_highlight_item">'.$string_search_input_strtolower.'</span>',$show);
/*highlighting string even if first letter of search-string within input-field is written lowercase and string within text begins with first letter uppercase*/
$string_search_input_ucfirst=ucfirst($string_search_input);
$show=str_replace($string_search_input_ucfirst, '<span class="s_sd_highlight_item">'.$string_search_input_ucfirst.'</span>',$show);
echo '<div class="s_sd_result_text">'.$show.'</div>';
}
echo '<div class="s_sd_more"><a class="link_text" href="front_content.php?idart='.$show_content_hit_idart.'" title="'.mi18n("more").'">'.mi18n("more").'</a></div>';
echo '</div>';
}
/*########## functions end ##########*/
/*########## checking if searchstring is at least x letters long begin ##########*/
if(strlen($string_search)<$searchstring_minimum_length)
{
$start_search=false;
echo '<p>'.mi18n("minimum length of word is 3 letters").'</p>';
}
else $start_search=true;
/*########## checking if searchstring is at least x letters long end ##########*/
/*########## displaying form begin ##########*/
echo '<form method="post" action="">';
echo '<input class="s_sd_input_text" type="text" name="searchterm" value="'.$string_search_input.'"/>';
echo '<input class="s_sd_input_submit" type="submit" value=" "/>';
echo '</form>';
/*########## displaying form end ##########*/
/*########## search begin ##########*/
if($start_search==true)
{
$hits_maximum=0; // amount represents 100% for calculating relevance of results
/********** collecting idartlang, idtype and amount of hits begin **********/
// array contains all idtypes with hits, so idartlang might be contained several times
//$query = "SELECT * FROM con_content ORDER BY idartlang ASC";
$query = "SELECT idartlang,idtype,value FROM con_content ORDER BY idartlang ASC";
$result = cRegistry::getDb();
$result->query($query);
$array_detections=array();
while($result->nextRecord())
{
$array_crawler=$result->toArray();
$conversion=utf8_encode(html_entity_decode(stripslashes(mb_strtolower($array_crawler['value']))));
//if(strpos(" ".utf8_encode(html_entity_decode(stripslashes(mb_strtolower($array_crawler['value'])))),$string_search))
if(strpos(" ".$conversion,$string_search))
{
//$amount_of_hits=substr_count(utf8_encode(html_entity_decode(stripslashes(mb_strtolower($array_crawler['value'])))),$string_search);
$amount_of_hits=substr_count($conversion,$string_search);
$array_detections[]=array('idartlang'=>$array_crawler['idartlang'],'idtype'=>$array_crawler['idtype'],'hits'=>$amount_of_hits);
}
}
/********** collecting idartlang, idtype and amount of hits end **********/
if(count($array_detections)>0)
{
/********** summarizing hits of all idtypes to one idartlang begin **********/
$array_detections_to_display=array();
foreach($array_detections AS $element)
{
$current_idartlang=$element['idartlang'];
if($current_idartlang==$previous_idartlang)
{
$hits_to_increase=$array_detections_to_display[$current_idartlang];
$add_hits=$element['hits'];
if($element['idtype']==1) $add_hits=$add_hits*$hit_multiplier_headline; // if string is found within a headline, it is x-times worthier
if($element['idtype']==2) $add_hits=$add_hits*$hit_multiplier_text; // if string is found within a text, it is x-times worthier
$hits=$hits_to_increase+$add_hits;
}
else
{
if($element['idtype']==1) $hits=$element['hits']*$hit_multiplier_headline;
if($element['idtype']==2) $hits=$element['hits']*$hit_multiplier_text;
else $hits=$element['hits'];
/* searchstring contained in metadata begin */
/*
idmetatypes:
3: description
5: keywords
*/
$query_meta_1 = "SELECT idmetatype,metavalue FROM con_meta_tag WHERE idartlang='$current_idartlang'";
$result_meta_1 = cRegistry::getDb();
$result_meta_1->query($query_meta_1);
while($result_meta_1->nextRecord())
{
$array_meta=$result_meta_1->toArray();
if($array_meta['idmetatype']=="3")
{
if(strpos(" ".utf8_encode(html_entity_decode(stripslashes(mb_strtolower($array_meta['metavalue'])))),$string_search))
{
$hits=$hits*$hit_multiplier_meta_description;
}
}
if($array_meta['idmetatype']=="5")
{
if(strpos(" ".utf8_encode(html_entity_decode(stripslashes(mb_strtolower($array_meta['metavalue'])))),$string_search))
{
echo '.';
$hits=$hits*$hit_multiplier_meta_keywords;
}
}
}
/* searchstring contained in metadata end */
}
$array_detections_to_display[$element['idartlang']]=$hits;
if($hits>$hits_maximum) $hits_maximum=$hits;
$previous_idartlang=$element['idartlang'];
}
/********** summarizing hits of all idtypes to one idartlang end **********/
/********** building array for collecting search result information begin **********/
arsort($array_detections_to_display); //sorting array by value, descending
$array_data_collection=array();
foreach ($array_detections_to_display as $key => $value)
{
if($counter_max_amount_hits<$max_amount_hits_displayed)
{
$query = "SELECT idart,idlang FROM con_art_lang WHERE idartlang='$key' AND idlang='$lang' AND online='1' AND searchable='1'";
$result = cRegistry::getDb();
$amount_of_rows = $result->num_rows;
$result->query($query);
$array_idart_hit=array();
while($result->nextRecord())
{
$array_idart_hit=$result->toArray();
/* category online or secured begin */
/*
con_art_lang -> idartlang & idart
idart in con_cat_art -> idcatart & idcat
idcat in con_cat_lang -> visible & public
$query_cat_1: article is placed in which category
$query_cat_2: category's online- and secure-status
*/
$query_cat_1 = "SELECT idcat FROM con_cat_art WHERE idart='$array_idart_hit[idart]'";
$result_query_cat_1 = cRegistry::getDb();
$result_query_cat_1->query($query_cat_1);
$array_query_cat_1=array();
while($result_query_cat_1->nextRecord())
{
$array_query_cat_1=$result_query_cat_1->toArray();
$query_cat_2 = "SELECT visible,public FROM con_cat_lang WHERE idcat='$array_query_cat_1[idcat]'";
$result_query_cat_2 = cRegistry::getDb();
$result_query_cat_2->query($query_cat_2);
$array_query_cat_2=array();
while($result_query_cat_2->nextRecord())
{
$array_query_cat_2=$result_query_cat_2->toArray();
}
}
/* category online or secured end */
$do_not_add="false";
if($do_not_add_if_cat_offline=="true" AND $array_query_cat_2['visible']=="0") $do_not_add="true";
/*if category is secured */
if($do_not_add_if_cat_secured=="true" AND $array_query_cat_2['public']=="0") $do_not_add="true";
/*if category is secured but visitor is logged in as fe-user */
if($do_not_add_if_cat_secured_but_unlocked_for_fe_group=="true" AND $array_query_cat_2['public']=="0") $do_not_add="true";
//elseif($do_not_add!="true")
elseif($do_not_add!="true" AND $array_query_cat_2['public']=="0" AND $fe_user_global_rights!="true")
{
if(!in_array($array_query_cat_1[idcat],$arr_visitor_fe_group_allowed_cat_ids)) $do_not_add="true";
}
if($do_not_add=="false")
{
$counter_max_amount_hits++;
$array_data_collection[]=array("idart"=>$array_idart_hit['idart'],"idartlang"=>$key,"idlang"=>$array_idart_hit['idlang'],"hits"=>$value);
}
} //while($result->nextRecord())
} //if($counter_max_amount_hits<$max_amount_hits_displayed)
}
/********** building array for collecting search result information end **********/
/********** displaying search results begin **********/
$amount_of_hits_finally=count($array_data_collection);
if ($amount_of_hits_finally>0)
{
if($max_amount_hits_displayed>$amount_of_hits_finally) $max_amount_hits_displayed=$amount_of_hits_finally;
echo '<p>'.mi18n("found hits").': ';
if($max_amount_hits_displayed<$amount_of_hits_finally) echo $max_amount_hits_displayed;
else echo $amount_of_hits_finally;
echo '</p>';
$i=0;
while($i<$max_amount_hits_displayed)
{
//echo $i.'<br/>';
show_result($array_data_collection[$i]['idart'],$lang,$string_search_input,$array_data_collection[$i]['hits'],$hits_maximum,$length_text_max,$i); //$lang: only show results with matching current chosen language
$i++;
}
}
else echo '<p>'.mi18n("nothing found").'</p>';
} //if(count($array_detections)>0)
elseif($string_search!="") echo '<p>'.mi18n("nothing found").'</p>';
/********** displaying search results end **********/
}
?>
Code: Alles auswählen
a.link_text_link {
border: 1px solid #444444;
text-decoration: none;
padding: 0.2em;
}
a.link_text_link:hover {
color: #ff0000;
}
div.s_sd_result {
border: 1px solid #999999;
padding: 0.5em;
margin-bottom: 2em;
}
div.s_sd_relevance {
width: 30%;
border: 1px solid #999999;
padding: 1px;
float: left;
}
div.s_sd_relevance_score {
font-size: 0.6em;
background-color: #999999;
color: #ffffff;
padding: 1px;
}
div.s_sd_result_headline {
clear: both;
font-weight: 900;
font-size: 1.2em;
margin-top: 0.3em;
}
div.s_sd_result_number {
font-weight: 900;
float: left;
margin-right: 0.3em;
}
div.s_sd_result_text {
font-size: 0.8em;
}
span.s_sd_highlight_item {
font-weight: 900;
text-decoration: underline;
}
div.s_sd_more {
text-align: right;
}
div.s_sd_more a {
color: #000000;
}
div.s_sd_more a:hover {
color: #ff0000;
}
input.s_sd_input_text {
width: 80%;
height: 1.8em;
margin-right: 1em;
border: 1px solid #999999;
background-color: #ffffff;
}
input.s_sd_input_submit {
border: 1px solid #999999;
border-radius: 0.3em;
width: 2em;
height: 2em;
background-image: url('MEINPFAD/icon-start-search.png');
background-repeat: no-repeat;
background-size: cover;
background-color: #ffffff;
}
input.s_sd_input_submit:hover {
border: 1px solid #000000;
cursor: pointer;
}
lang_de_DE.txt
Code: Alles auswählen
found hits=Suchtreffer
input - discard hit if category is=Suchtreffer nicht berücksichtigen wenn Artikel in Kategorie liegt, die
input - hints for multiplyers=Multiplikatoren, wenn Suchbegriff enthalten ist in
input - maximum amount of hits=Maximale Anzahl aufgelisteter Suchtreffer
input - maximum length of description=Länge des Suchtreffer-Teasers
input - minimum length of searchstring=Minimale Länge des Suchwortes
input - multiplyer hit in headline=Überschrift
input - multiplyer hit in meta description=Meta-Description
input - multiplyer hit in meta keywords=Meta-Keywords
input - multiplyer hit in text=Text
input - no=nein
input - offline=offline ist
input - secured=geschützt ist
input - secured but unlocked for fe-user group=geschützt ist, aber für Frontend-User-Group des Besuchers freigegeben
input - yes=ja
minimum length of word is 3 letters=Bitte geben Sie mindestens drei Zeichen ein.
more=mehr
nothing found=Kein Suchtreffer.
Output:
Code: Alles auswählen
<?php
/***********************************************
* CONTENIDO MODUL - OUTPUT
*
* Modulname : search_sd-additional-form
* Author(s) : Seamless-Design Markus Hübner
* Copyright : Seamless-Design Markus Hübner
* Created : 05.02.2015
************************************************/
// assert framework initialization
defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
/*########## variables begin ##########*/
$idart_resultpage=MEINE-IDART;
/*########## variables end ##########*/
/*########## displaying form begin ##########*/
echo '<div class="s_sd_af">';
echo '<form method="post" action="front_content.php?idart='.$idart_resultpage.'">';
echo '<input class="s_sd_af_input_text" type="text" name="searchterm" value="'.$string_search_input.'"/>';
echo '<input class="s_sd_af_input_submit" type="submit" value=" "/>';
echo '</form>';
echo '</div>';
/*########## displaying form end ##########*/
?>
CSS:
Code: Alles auswählen
div.s_sd_af {
margin-top: 1em;
background-color: #ffffff;
border: 1px solid #ffffff;
border-radius: 0.3em;
padding: 0.5em;
}
input.s_sd_af_input_text {
width: 60%;
height: 1.8em;
margin-right: 1em;
border: 1px solid #999999;
background-color: #ffffff;
}
input.s_sd_af_input_submit {
border: 1px solid #999999;
border-radius: 0.3em;
width: 2em;
height: 2em;
background-image: url('MEINPFAD/icon-start-search.png');
background-repeat: no-repeat;
background-size: cover;
background-color: #ffffff;
}
input.s_sd_af_input_submit:hover {
border: 1px solid #000000;
cursor: pointer;
}
Wer den Aufbau der Suchtreffer anpassen möchte: Das HTML-Markup läuft nicht über smarty, sondern ist in der function "show_result" im Output des Moduls "search_sd" festgelegt. Das Ranking selbst läuft simpel über die Anzahl der Suchtreffer im Artikel. Ein Artikel, bei dem das Suchwort 5 mal vorkommt wird höher gerankt als ein Artikel, bei dem es nur 3 mal vorkommt. Da das Suchwort in einer Headline aber mehr Bedeutung bekommen und zum Beispiel doppelt gewichtet werden sollte, kann hier in der Konfiguration ein entsprechender Multiplikator eingetragen werden. Gleiches gilt für die Metadaten. Ein Treffer in den Keywords bekommt mit dem Multiplikator 3 den - logisch - dreifachen Wert. Ein Treffer in den Keywords hat dann die gleiche Gewichtung wie drei Treffer im Textelement.
Viel Spaß damit!