PageRenderTime 25ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/core/resources/classes/class.Search.php

https://github.com/otto-torino/gino
PHP | 203 lines | 100 code | 24 blank | 79 comment | 26 complexity | 448cb8538ab7092d78052d390d54ecd5 MD5 | raw file
  1. <?php
  2. /**
  3. * @file class.Search.php
  4. * @brief Contiene la definizione ed implementazione della classe Gino.Search
  5. *
  6. * @copyright 2005-2014 Otto srl (http://www.opensource.org/licenses/mit-license.php) The MIT License
  7. * @author marco guidotti guidottim@gmail.com
  8. * @author abidibo abidibo@gmail.com
  9. */
  10. namespace Gino;
  11. /**
  12. * @brief Libreria per ricerche full text pesate sulle tabelle
  13. *
  14. * Codice SQL da eseguire sul database MySQL
  15. * @code
  16. * DELIMITER $$
  17. *
  18. * DROP FUNCTION IF EXISTS `replace_ci`$$
  19. * CREATE FUNCTION `replace_ci` ( str TEXT,needle CHAR(255),str_rep CHAR(255))
  20. * RETURNS TEXT
  21. * DETERMINISTIC
  22. * BEGIN
  23. * DECLARE return_str TEXT;
  24. * SELECT replace(lower(str),lower(needle),str_rep) INTO return_str;
  25. * RETURN return_str;
  26. * END$$
  27. *
  28. * DELIMITER ;
  29. * @endcode
  30. *
  31. * @copyright 2005-2014 Otto srl (http://www.opensource.org/licenses/mit-license.php) The MIT License
  32. * @author marco guidotti guidottim@gmail.com
  33. * @author abidibo abidibo@gmail.com
  34. */
  35. class Search {
  36. private $_table;
  37. /**
  38. * @brief Costruttore
  39. *
  40. * @param string $table testo del FROM in una query SQL (ad esempio: page AS p, page_block AS pb)
  41. * @param array $opts
  42. * array associativo di opzioni
  43. * - @b highlight_range (integer)
  44. * @return void, istanza di Gino.Search
  45. */
  46. function __construct($table, $opts=array()) {
  47. $this->_table = $table;
  48. $this->_highlight_range = isset($opts['highlight_range']) ? $opts['highlight_range'] : 120;
  49. }
  50. /**
  51. * @brief Ripulisce la stringa di ricerca
  52. * @description Elimina parole con poco significato
  53. * @param string $search_string
  54. * @return string
  55. */
  56. private function clearSearchString($search_string) {
  57. $unconsidered = array("lo", "l", "il", "la", "i", "gli", "le", "uno", "un", "una", "un", "su", "sul", "sulla", "sullo", "sull", "in", "nel", "nello", "nella", "nell", "con", "di", "da", "dei", "d", "della", "dello", "del", "dell", "che", "a", "dal", "è", "e", "per", "non", "si", "al", "ai", "allo", "all", "al", "o");
  58. $clean_string = strtolower($search_string);
  59. $clean_string = preg_replace("#\b(".implode("|", $unconsidered).")\b#", "", $clean_string);
  60. $clean_string = preg_replace("#\W|(\s+)#", " ", $clean_string);
  61. $clean_string = preg_quote($clean_string);
  62. return $clean_string;
  63. }
  64. /**
  65. * @brief Ricava le parole chiave da una stringa di ricerca
  66. * @param string $search_string
  67. * @return array di parole chiave
  68. */
  69. private function getKeywords($search_string) {
  70. $clean_string = $this->clearSearchString($search_string);
  71. $empty_array = array(""," ");
  72. return array_diff(array_unique(explode(" ", $clean_string)), $empty_array);
  73. }
  74. /**
  75. * @brief Costruisce la query di una ricerca full text
  76. *
  77. * @param array $selected_fields campi da selezionare nella ricerca (costruzione del SELECT), ad esempio
  78. * @code
  79. * array("p.item_id", array("highlight"=>true, "field"=>"p.title"), array("highlight"=>true, "field"=>"p.subtitle"), array("highlight"=>true, "field"=>"pb.text"))
  80. * @endcode
  81. * @param array $required_clauses tipo di ricerca sul testo (costruzione del WHERE), ad esempio
  82. * @code
  83. * array("p.item_id"=>array("field"=>true, "value"=>"pb.item"))
  84. * @endcode
  85. * @param array $weight_clauses (costruzione del WHERE e rilevanza dei risultati), ad esempio
  86. * @code
  87. * array("p.title"=>array("weight"=>3), "p.subtitle"=>array("weight"=>2), "pb.text"=>array("weight"=>1))
  88. * @endcode
  89. * @return string, query
  90. */
  91. public function makeQuery($selected_fields, $required_clauses, $weight_clauses){
  92. $final_keywords = 0;
  93. $selected = array();
  94. foreach($selected_fields as $f) {
  95. $selected[] = is_array($f) ? $f['field'] : $f;
  96. }
  97. $relevance = "(";
  98. $occurrences = "(";
  99. $sqlwhere_r = "";
  100. $sqlwhere_w = "";
  101. $sql_where = '';
  102. foreach($required_clauses as $f=>$fp) {
  103. if(is_array($fp)) {
  104. if(isset($fp['inside']) && $fp['inside']) $sqlwhere_r .= "$f LIKE '%".$fp['value']."%' AND ";
  105. elseif(isset($fp['begin']) && $fp['begin']) $sqlwhere_r .= "$f LIKE '".$fp['value']."%' AND ";
  106. elseif(isset($fp['end']) && $fp['end']) $sqlwhere_r .= "$f LIKE '%".$fp['value']."' AND ";
  107. elseif(isset($fp['field']) && $fp['field']) $sqlwhere_r .= "$f=".$fp['value']." AND ";
  108. else $sqlwhere_r .= "$f='".$fp['value']."' AND ";
  109. }
  110. else {
  111. $sqlwhere_r .= "$f='$fp' AND ";
  112. }
  113. }
  114. foreach($weight_clauses as $f=>$fp) {
  115. $search_keywords = $this->getKeywords($fp['value']);
  116. $final_keywords += count($search_keywords);
  117. foreach($search_keywords as $keyw) {
  118. $occurrences .= "(LENGTH($f)-LENGTH(replace_ci($f,'$keyw','')))/LENGTH('$keyw') + ";
  119. if(isset($fp['inside']) && $fp['inside']) {
  120. $relevance .= "(INSTR($f, '".$keyw."')>0)*".$fp['weight']." + ";
  121. $sqlwhere_w .= "$f LIKE '%".$keyw."%' OR ";
  122. }
  123. else {
  124. $relevance .= "(($f REGEXP '[[:<:]]".$keyw."[[:>:]]')>0)*".$fp['weight']." + ";
  125. $sqlwhere_w .= "$f REGEXP '[[:<:]]".$keyw."[[:>:]]' OR ";
  126. }
  127. }
  128. }
  129. if($final_keywords) $sqlwhere_w = substr($sqlwhere_w, 0, strlen($sqlwhere_w)-4);
  130. $relevance .= "0)";
  131. $occurrences .= "0)";
  132. if($sqlwhere_r || $sqlwhere_w) {
  133. $sqlwhere = "WHERE ";
  134. if($sqlwhere_r) $sqlwhere .= $sqlwhere_r;
  135. if($sqlwhere_w) $sqlwhere .= "(".$sqlwhere_w.")";
  136. else $sqlwhere = substr($sqlwhere, 0, strlen($sqlwhere)-5);
  137. }
  138. $query = "SELECT ".implode(",", $selected).", $relevance AS relevance, $occurrences AS occurrences FROM $this->_table $sqlwhere ORDER BY relevance DESC, occurrences DESC";
  139. return $final_keywords ? $query : false;
  140. }
  141. /**
  142. * @brief Risultati di una ricerca full text
  143. *
  144. * @see self::makeQuery()
  145. * @param object $dbObj istanza del database (db::instance())
  146. * @param array $selected_fields campi da selezionare nella ricerca
  147. * @param array $required_clauses tipo di ricerca sul testo
  148. * @param array $weight_clauses
  149. * @return array di risultati, ciascun risultato è un array con chiavi relevance, occurrences, <field-name>
  150. */
  151. public function getSearchResults($dbObj, $selected_fields, $required_clauses, $weight_clauses) {
  152. $res = array();
  153. $query = $this->makeQuery($selected_fields, $required_clauses, $weight_clauses);
  154. if($query === false) return array();
  155. $rows = $dbObj->select(null, null, null, array('custom_query'=>$query));
  156. if($rows and sizeof($rows)>0) {
  157. $i = 0;
  158. foreach($rows as $row) {
  159. $res[$i] = array();
  160. foreach($selected_fields as $f) {
  161. $res[$i]['relevance'] = $row['relevance'];
  162. $res[$i]['occurrences'] = $row['occurrences'];
  163. if(is_array($f) && isset($f['highlight']) && $f['highlight']) {
  164. $fp = $weight_clauses[$f['field']];
  165. $search_keywords = $this->getKeywords($fp['value']);
  166. $rexp = (isset($fp['inside']) && $fp['inside']) ? implode("|", $search_keywords) : "\b".implode("\b|\b", $search_keywords)."\b";
  167. if(preg_match("#(.|\n){0,$this->_highlight_range}($rexp)(.|\n){0,$this->_highlight_range}#ui", cutHtmlText($row[preg_replace("#.*?\.#", "", $f['field'])], 50000000, '', true, false, true), $matches)) {
  168. $res[$i][$f['field']] = preg_replace("#".$rexp."#i", "<span class=\"evidence\">$0</span>", $matches[0]);
  169. }
  170. else $res[$i][$f['field']] = '';
  171. }
  172. else $res[$i][$f] = $row[preg_replace("#.*?\.#", "", $f)];
  173. }
  174. $i++;
  175. }
  176. }
  177. return $res;
  178. }
  179. }