loadTbChampBase(); $this->maxSuggestions = $maxSuggest; $this->arrayResults = array(); if ($searchTable == 'notices') { $this->searchIndex = 'notices_mots_global_index'; $this->searchValueFields = 'notices_fields_global_index'; } $this->excludeNotes = $excludeNotes; $this->maxResultsByPermutation = $maxResultsByPermutation; if($tmp = trim($string)){ $this->inputString = $tmp; $this->findWords(); } } // --------------------------------------------------------------------------------------------------- // loadTbChampBase() : correspondance champs base / libellés OPAC // --------------------------------------------------------------------------------------------------- public function loadTbChampBase() { global $champ_base,$include_path,$msg; if(!count($champ_base)) { $file = $include_path."/indexation/notices/champs_base_subst.xml"; if(!file_exists($file)){ $file = $include_path."/indexation/notices/champs_base.xml"; } $fp=fopen($file,"r"); if ($fp) { $xml=fread($fp,filesize($file)); } fclose($fp); $champ_base=_parser_text_no_function_($xml,"INDEXATION",$file); } if(!count($this->tbChampBase)) { foreach($champ_base["FIELD"] as $k=>$v){ if(isset($v["TABLE"][0]["TABLEFIELD"])){ foreach($v["TABLE"][0]["TABLEFIELD"] as $k2=>$v2){ if(isset($v2["NAME"])){ if(isset($v2["ID"])){ $this->tbChampBase[(int)$v["ID"]."_".(int)$v2["ID"]]=$msg[$v2["NAME"]]; }else{ $this->tbChampBase[(int)$v["ID"]."_0"]=$msg[$v2["NAME"]]; } }else{ if(isset($v2["ID"])){ $this->tbChampBase[(int)$v["ID"]."_".(int)$v2["ID"]]=$msg[$v["NAME"]]; }else{ $this->tbChampBase[(int)$v["ID"]."_0"]=$msg[$v["NAME"]]; } } if(isset($v2["ID"])){ if(!trim($this->tbChampBase[(int)$v["ID"]."_".(int)$v2["ID"]])){ $this->tbChampBase[(int)$v["ID"]."_".(int)$v2["ID"]]="___champ sans libellé___"; } }else{ if(!trim($this->tbChampBase[(int)$v["ID"]."_0"])){ $this->tbChampBase[(int)$v["ID"]."_0"]="___champ sans libellé___"; } } } } if(isset($v["ISBD"])){ $tmp=$v["ISBD"][0]; $this->tbChampBase[(int)$v["ID"]."_".(int)$tmp["ID"]]=$msg[$tmp["NAME"]."_".$tmp["CLASS_NAME"]]; if(!trim($this->tbChampBase[(int)$v["ID"]."_".(int)$tmp["ID"]])){ $this->tbChampBase[(int)$v["ID"]."_".(int)$tmp["ID"]]="___champ sans libellé___"; } } } } } // --------------------------------------------------------------------------------------------------- // findWords() : nettoie et trouve tous les mots de la chaine saisie // --------------------------------------------------------------------------------------------------- public function findWords() { $this->cleanInputString = $this->cleanString($this->inputString); $this->arrayWords = str_word_count($this->cleanInputString,1,"0123456789"); //on nettoie les doublons éventuels $this->arrayWords = array_values(array_unique($this->arrayWords)); //on limite le nombre de mots cherchés $this->arrayWords = array_slice($this->arrayWords,0,5); if(count($this->arrayWords)){ $this->findAndPermuteSimilars(); } } // --------------------------------------------------------------------------------------------------- // findSimilars() : trouve les approchants pondérés depuis le tableau de mots // la pondération est inversée : plus "pond" est faible, plus le mot est pertinent // --------------------------------------------------------------------------------------------------- public function findAndPermuteSimilars() { global $dbh,$lang; if(count($this->arrayWords)){ foreach($this->arrayWords as $key=>$word){ $dmeta = new DoubleMetaPhone($word); $distMax=2; switch(count($this->arrayWords)){ case 1 : $maxSimilars=10; break; case 2 : $maxSimilars=5; break; case 3 : $maxSimilars=3; break; case 4 : $maxSimilars=2; break; default : $maxSimilars=1; break; } $temporary_table = 'suggest_'.hrtime(true); $qd = "drop table if exists $temporary_table"; pmb_mysql_query($qd); $qc = "create temporary table $temporary_table (id_word int, word varchar(255), dist int, index using btree(id_word), key i_word(word)) engine memory"; pmb_mysql_query($qc); $qi_1 = "insert ignore into $temporary_table (SELECT id_word, word, 0 as dist FROM words WHERE word LIKE '".addslashes($word)."%' AND lang IN ('','".$lang."') )"; pmb_mysql_query($qi_1); $qi_2 = "insert ignore into $temporary_table (SELECT id_word, word, levenshtein('".addslashes($word)."',word) as dist FROM words WHERE levenshtein('".$dmeta->primary." ".$dmeta->secondary."',double_metaphone) < ".$distMax." AND lang IN ('','".$lang."') )"; pmb_mysql_query($qi_2); $qr = "select distinct id_word, word, dist from $temporary_table join {$this->searchIndex} on num_word=id_word order by dist, word limit $maxSimilars"; $res=pmb_mysql_query($qr); $count=1; if($res && pmb_mysql_num_rows($res)){ $nbRows=pmb_mysql_num_rows($res); while($row=pmb_mysql_fetch_object($res)){ $this->arraySimilarsByWord[$key][] = $row->id_word; $this->arraySimilars[$row->id_word]["word"] = $row->word; $this->arraySimilars[$row->id_word]["pond"] = $count/$nbRows; $count++; } } } if(count($this->arraySimilarsByWord)){ $this->permutationCode=array(); $this->permutationPos=0; $this->findPermutations($this->arraySimilarsByWord); } if(count($this->arrayPermutations)){ $this->findAndOrderPermutationInDatabase(); } } } // --------------------------------------------------------------------------------------------------- // listUniqueSimilars() : renvoie un tableau des suggestions uniques // --------------------------------------------------------------------------------------------------- public function listUniqueSimilars(){ $arrayReturn = array(); if (count($this->arraySimilars)) { foreach ($this->arraySimilars as $value) { $arrayReturn[] = $value["word"]; } } $arrayReturn = array_unique($arrayReturn); return $arrayReturn; } // --------------------------------------------------------------------------------------------------- // findPermutations() : trouve les permutations du tableau en entrée // attention : fonction récursive (d'où le paramètre en entrée, et les deux propriétés de classe) // --------------------------------------------------------------------------------------------------- public function findPermutations($array) { if(count($array)) { for($i=0; $ipermutationCode[$this->permutationPos] = $array[0][$i]; $tarr = array_shift($tmpArray); $this->permutationPos++; $this->findPermutations($tmpArray); } } else { asort($this->permutationCode); $tmpValeur=implode(",",$this->permutationCode); if(!is_array($this->arrayPermutations) || !in_array($tmpValeur,$this->arrayPermutations)){ $this->arrayPermutations[]=$tmpValeur; } } $this->permutationPos--; } // --------------------------------------------------------------------------------------------------- // arrayResultsSort($sort) : trie la propriété arrayResultFinal selon la clé donnée // --------------------------------------------------------------------------------------------------- public function arrayResultsSort($sort){ $sort_values=array(); for ($i = 0; $i < sizeof($this->arrayResults); $i++) { $sort_values[$i] = $this->arrayResults[$i][$sort]; } asort ($sort_values); reset ($sort_values); foreach ($sort_values as $arr_key => $arr_val) { $sorted_arr[] = $this->arrayResults[$arr_key]; } $this->arrayResults = $sorted_arr; } // --------------------------------------------------------------------------------------------------- // findAndOrderPermutationInDatabase() : trouve les champs de notice où les permutations apparaissent // classés par distance max des deux termes les plus éloignés (ou position si un seul terme) pondérée // par nombre d'occurrences en regroupement // --------------------------------------------------------------------------------------------------- public function findAndOrderPermutationInDatabase() { global $dbh; if(count($this->arrayPermutations)){ $arrayResults=array(); foreach($this->arrayPermutations as $permutation){ $itemPermutation=explode(",",$permutation); //Cas particulier si un seul mot if(count($itemPermutation)==1){ $query="SELECT DISTINCT id_notice, code_champ, code_ss_champ, field_position FROM ".$this->searchIndex." WHERE num_word=".$itemPermutation[0]; if($this->excludeNotes){ $query.=" AND code_champ NOT IN (12,13,14)"; } $query.=" ORDER BY 4 LIMIT 0,".$this->maxResultsByPermutation; $res=pmb_mysql_query($query,$dbh) or die(); if(pmb_mysql_num_rows($res)){ while($row=pmb_mysql_fetch_object($res)){ $key=$row->id_notice."_".$row->code_champ."_".$row->code_ss_champ."_".$row->field_position."_".$itemPermutation[0]; $arrayResults[$key]=$row->field_position*$this->arraySimilars[$itemPermutation[0]]["pond"]; } } }else{ $ponderation=0; foreach($itemPermutation as $keyItem=>$idWord){ $ponderation+=$this->arraySimilars[$idWord]["pond"]; if(!$keyItem){ $select="DISTINCT n.id_notice, n.code_champ, n.code_ss_champ, n.field_position, ( SELECT MAX(field_position)-MIN(field_position) FROM ".$this->searchIndex." WHERE id_notice=n.id_notice AND code_champ=n.code_champ AND code_ss_champ=n.code_ss_champ AND num_word IN (".$permutation.")"; if($this->excludeNotes){ $select.=" AND code_champ NOT IN (12,13,14)"; } $select.=") as distance"; $from=$this->searchIndex." n"; $where="n.num_word=".$idWord; if($this->excludeNotes){ $where.=" AND n.code_champ NOT IN (12,13,14)"; } }else{ $from.=" JOIN ".$this->searchIndex." n".$keyItem." ON n.id_notice=n".$keyItem.".id_notice AND n.code_champ=n".$keyItem.".code_champ AND n.code_ss_champ=n".$keyItem.".code_ss_champ"; $where.=" AND n".$keyItem.".num_word=".$idWord; } } $query="SELECT ".$select." FROM ".$from." WHERE ".$where; $res=pmb_mysql_query($query,$dbh) or die(); if(pmb_mysql_num_rows($res)){ while($row=pmb_mysql_fetch_object($res)){ $key=$row->id_notice."_".$row->code_champ."_".$row->code_ss_champ."_".$row->field_position."_".implode("_",$itemPermutation); $arrayResults[$key]=$row->distance*$ponderation; } } } } asort($arrayResults); //Regroupement par valeur/champ foreach($arrayResults as $key=>$value){ $tmpArray=explode("_",$key); $query="SELECT value FROM ".$this->searchValueFields." WHERE id_notice=".$tmpArray[0]." AND code_champ=".$tmpArray[1]." AND code_ss_champ=".$tmpArray[2]; $res=pmb_mysql_query($query,$dbh) or die(); $row=pmb_mysql_fetch_object($res); $creeElement=true; if(count($this->arrayResults)){ foreach($this->arrayResults as $key2=>$value2){ if(($value2["field_content"]==$row->value) && ($value2["field_subfield"]==$tmpArray[1]."_".$tmpArray[2])){ $this->arrayResults[$key2]["occurrences"]++; $creeElement=false; break; } } } if($creeElement){ $tmpArrayTmp=array(); $tmpArrayTmp["field_content"]=$row->value; $tmpArrayTmp["field_clean_content"]=$row->value; $tmpArrayTmp["field_subfield"]=$tmpArray[1]."_".$tmpArray[2]; $tmpArrayTmp["ratio"]=$value; $tmpArrayTmp["occurrences"]=1; $this->arrayResults[]=$tmpArrayTmp; } } //Calcul des scores foreach($this->arrayResults as $key=>$value){ $this->arrayResults[$key]["score"]=$value["ratio"]/$value["occurrences"]; } //Classement des résultats $this->arrayResultsSort('score'); //On limite et on gère l'affichage $search=array(); foreach($this->arraySimilars as $similar){ $search[]=$similar["word"]; } arsort($search); if(is_array($this->arrayResults)){ //Les résultats qui commencent par la saisie sont placés en premiers $tmpArray=array(); foreach($this->arrayResults as $key=>$value){ if(preg_match('`^'.$this->cleanInputString.'`',$this->cleanString($value['field_content']))){ $tmpArray[]=$value; unset($this->arrayResults[$key]); } } $this->arrayResults=array_merge($tmpArray,$this->arrayResults); //limitation des résultats et gestion de l'affichage $arrayUniqueAffiche=array(); foreach($this->arrayResults as $key=>$value){ if (count($arrayUniqueAffiche)<$this->maxSuggestions && !in_array($value["field_content"],$arrayUniqueAffiche)) { //champ dans lequel la valeur a été trouvée : 27/08/2015 on n'affiche plus le champs //$this->arrayResults[$key]["field_name"]=$this->tbChampBase[$value["field_subfield"]]; //passage en gras des mots $this->arrayResults[$key]["field_content"]=$this->markBold($value["field_content"],implode("|",$search)); //pour les occurences trop longues, juste les mots en gras $this->arrayResults[$key]["field_content_search"]=$this->listFoundWords($this->arrayResults[$key]["field_content"]); $arrayUniqueAffiche[]=$value["field_content"]; } else { unset($this->arrayResults[$key]); } } } } } // --------------------------------------------------------------------------------------------------- // markBold($string,$wordToFind) : met un mot en gras dans une chaîne // --------------------------------------------------------------------------------------------------- public function markBold($string,$wordsToFind){ $specialChars = array("a","e","i","o","u","y","c","n" ); $specialCharsReplacement = array("[a|à|á|â|ã|ä|å]{1}","[e|è|é|ê|ë]{1}","[i|ì|í|î|ï]{1}","[o|ò|ó|ô|õ|ö|ø]{1}","[u|ù|ú|û|ü]{1}","[y|y]{1}","[c|ç]{1}","[n|ñ]{1}" ); $wordsToFind = str_replace($specialChars, $specialCharsReplacement, $wordsToFind); $tmpArray=preg_split("/([\s,\'\"\.\-\(\) ]+)/",trim($string),-1,PREG_SPLIT_DELIM_CAPTURE); foreach($tmpArray as $key=>$value){ $tmpArray[$key]=preg_replace("/^($wordsToFind)$/i", "\\1",$value); } return implode("",$tmpArray); } // --------------------------------------------------------------------------------------------------- // listFoundWords($string) : renvoie un tableau des mots uniques trouvés en gras // --------------------------------------------------------------------------------------------------- public function listFoundWords($string){ preg_match_all("`(.*?)<\/b>`",$string,$arrayReturn); $arrayReturn = array_unique($arrayReturn[1]); return $arrayReturn; } // --------------------------------------------------------------------------------------------------- // cleanString($string) : renvoie une chaine nettoyée // --------------------------------------------------------------------------------------------------- public function cleanString($string){ $string = str_replace("%","",$string); $string = convert_diacrit($string); $string = strip_empty_words($string); return $string; } public static function get_add_link() { global $msg; global $opac_show_suggest; global $opac_resa_popup; $add_link = ''; if ($opac_show_suggest) { $add_link .= "   ".$msg['empr_bt_make_sugg'].""; } return $add_link; } } # fin de définition de la classe