![]() |
Xataface 2.0
Xataface Application Framework
|
00001 <?php 00046 class Dataface_Index { 00047 00051 function createIndexTable(){ 00052 $sql = "create table dataface__index ( 00053 index_id int(11) not null auto_increment, 00054 `table` varchar(64) not null, 00055 record_id varchar(255) not null, 00056 record_url varchar(255) not null, 00057 record_title varchar(255) not null, 00058 record_description text, 00059 `lang` varchar(2) not null, 00060 `searchable_text` text, 00061 fulltext index `searchable_text_index` (`searchable_text`), 00062 unique key `record_key` (`record_id`,`lang`), 00063 primary key (`index_id`))"; 00064 $res = mysql_query($sql, df_db()); 00065 00066 if ( !$res ){ 00067 trigger_error(mysql_error(df_db()), E_USER_ERROR); 00068 } 00069 } 00070 00071 00079 function indexRecord(&$record, $lang='*'){ 00080 $app =& Dataface_Application::getInstance(); 00081 if ( $lang == '*' ){ 00082 // If the language specified is '*', that means we will 00083 // be indexing all languages. 00084 $this->indexRecord($record, $app->_conf['lang']); 00085 00086 if ( is_array($app->_conf['languages']) ){ 00087 import('Dataface/IO.php'); 00088 $io = new Dataface_IO($record->_table->tablename); 00089 foreach ( array_keys($app->_conf['languages']) as $lang){ 00090 if ( $lang == $app->_conf['lang'] ){ 00091 continue; 00092 } 00093 $io->lang = $lang; 00094 $io->read($record->getId(), $record); 00095 $this->indexRecord($record, $lang); 00096 } 00097 } 00098 return true; 00099 } 00100 00101 if ( !isset($lang) ) $lang = $app->_conf['lang']; 00102 00103 $del =& $record->_table->getDelegate(); 00104 if ( isset($del) and method_exists($del, 'getSearchableText') ){ 00105 $searchable_text = $del->getSearchableText($record); 00106 if ( !is_string($searchable_text) ){ 00107 // If this method returns anything other than a string, 00108 // then we do not index the record... we just return false. 00109 return false; 00110 } 00111 } else { 00112 // The getSearchableText() method is not defined, so we will 00113 // just produce a concatenation of all text fields in the 00114 // record and index those. 00115 $fields = $record->_table->getCharFields(true); 00116 $searchable_text = implode(', ', $record->strvals($fields)); 00117 } 00118 00119 $sql = " 00120 replace into dataface__index 00121 (`record_id`,`table`,`record_url`,`record_title`,`record_description`,`lang`,`searchable_text`) 00122 values 00123 ( 00124 '".addslashes($record->getId())."', 00125 '".addslashes($record->_table->tablename)."', 00126 '".addslashes($record->getPublicLink())."', 00127 '".addslashes($record->getTitle())."', 00128 '".addslashes(strip_tags($record->getDescription()))."', 00129 '".addslashes($lang)."', 00130 '".addslashes($searchable_text)."' 00131 )"; 00132 if ( !@mysql_query($sql, df_db()) ){ 00133 $this->createIndexTable(); 00134 if ( !mysql_query($sql, df_db()) ){ 00135 trigger_error(mysql_error(df_db()), E_USER_ERROR); 00136 } 00137 } 00138 00139 00140 return true; 00141 } 00142 00143 function indexFoundRecords($query, $lang='*'){ 00144 for ( $start = 0; $start >= 0; $start +=100 ){ 00145 // We do it in chunks of 100 00146 00147 $records = df_get_records_array($query['-table'], $query, $start, 100, false); 00148 if ( !$records or (count($records) == 0) or PEAR::isError($records) ) return true; 00149 00150 foreach ($records as $record){ 00151 00152 $this->indexRecord($record, $lang); 00153 } 00154 unset($records); 00155 } 00156 } 00157 00158 function buildIndex($tables=null, $lang='*', $clear=true){ 00159 if ( $clear ){ 00160 $sql = "delete from dataface__index"; 00161 if ( !mysql_query($sql, df_db()) ){ 00162 $this->createIndexTable(); 00163 } 00164 } 00165 foreach ( $tables as $tablename ){ 00166 if ( $this->isTableIndexable($tablename) ){ 00167 $this->indexFoundRecords(array('-table'=>$tablename), $lang); 00168 } 00169 } 00170 $sql = "optimize table dataface__index"; 00171 if ( !mysql_query($sql, df_db()) ){ 00172 $this->createIndexTable(); 00173 $sql = "optimize table dataface__index"; 00174 mysql_query($sql, df_db()); 00175 } 00176 00177 00178 } 00179 00180 function isTableIndexable($tablename){ 00181 $app =& Dataface_Application::getInstance(); 00182 $indexableTables = @$app->_conf['_index']; 00183 if ( !is_array($indexableTables) ){ 00184 $indexableTables = array(); 00185 } 00186 00187 if ( @$indexableTables['__default__'] ){ 00188 $default = 1; 00189 } else { 00190 $default = 0; 00191 } 00192 00193 $table =& Dataface_Table::loadTable($tablename); 00194 if ( $default ){ 00195 if ( isset($table->_atts['__index__']) and $table->_atts['__index__'] == 0){ 00196 return false; 00197 } else { 00198 return true; 00199 } 00200 } else { 00201 if ( @$indexableTables[$tablename] or @$table->_atts['__index__'] ){ 00202 return true; 00203 } else { 00204 return false; 00205 } 00206 } 00207 } 00208 00209 function _cmp_words_by_length($a,$b){ 00210 if ( strlen($a) < strlen($b) ) return 1; 00211 else if ( strlen($b) < strlen($a) ) return -1; 00212 else return 0; 00213 } 00214 00220 function find($query, $returnMetadata=false, $lang=null){ 00221 if ( !$lang ) $lang = @Dataface_Application::getInstance()->_conf['lang']; 00222 if ( !$lang ) $lang = 'en'; 00223 00224 $select = "select record_id,`table`,record_url,record_title,record_description, `searchable_text`, `lang`,match(searchable_text) against ('".addslashes($query['-search'])."') as `relevance`"; 00225 $sql = " 00226 00227 from dataface__index 00228 where `lang`='".addslashes($lang)."' and 00229 match(searchable_text) 00230 against ('".addslashes($query['-search'])."')"; 00231 00232 00233 $countsql = "select count(record_id), `table` as num ".$sql." group by `table`"; 00234 00235 if ( isset($query['-table']) ){ 00236 $sql .= " and `table` = '".addslashes($query['-table'])."'"; 00237 } 00238 00239 00240 00241 if ( !isset($query['-limit']) ){ 00242 $query['-limit'] = 30; 00243 } 00244 00245 00246 00247 if ( !isset($query['-skip']) ){ 00248 $query['-skip'] = 0; 00249 } 00250 $skip = intval($query['-skip']); 00251 $limit = intval($query['-limit']); 00252 $sql .= " limit $skip, $limit"; 00253 $sql = $select.$sql; 00254 00255 $res = @mysql_query($sql, df_db()); 00256 if ( !$res ){ 00257 $this->createIndexTable(); 00258 $res = mysql_query($sql, df_db()); 00259 if ( !$res ){ 00260 trigger_error(mysql_error(df_db()), E_USER_ERROR); 00261 } 00262 } 00263 00264 $out = array(); 00265 $phrases = array(); 00266 $words = explode(' ', str_replace('"', '', $query['-search'])); 00267 if ( preg_match_all('/"([^"]+)"/', $query['-search'], $matches, PREG_PATTERN_ORDER) ){ 00268 foreach ($matches[1] as $m){ 00269 $phrases[] = $m; 00270 } 00271 } 00272 $numWords = count($words); 00273 if ( $numWords > 1 ){ 00274 $words2 = array(implode(' ', $words)); 00275 for ( $i=0; $i<$numWords; $i++){ 00276 for ( $j=$i; $j<$numWords; $j++){ 00277 00278 $temp = $words; 00279 for ( $k=$i; $k<=$j; $k++ ){ 00280 unset($temp[$k]); 00281 } 00282 $words2[] = implode(' ', $temp); 00283 } 00284 } 00285 $words = $words2; 00286 } 00287 00288 usort($words, array($this, '_cmp_words_by_length')); 00289 00290 while ( $row = mysql_fetch_assoc($res) ){ 00291 $st = strip_tags($row['searchable_text']); 00292 $st = html_entity_decode($st, ENT_COMPAT, Dataface_Application::getInstance()->_conf['oe']); 00293 00294 unset($row['searchable_text']); 00295 00296 $summary = array(); 00297 foreach ($phrases as $p){ 00298 if ( preg_match_all('/.{0,50}'.preg_quote($p, '/').'.{0,50}/', $st, $matches, PREG_PATTERN_ORDER) ){ 00299 //print_r($matches); 00300 foreach ($matches[0] as $m){ 00301 $summary[] = $m; 00302 if ( count($summary) > 5 ) break; 00303 } 00304 //print_r($summary); 00305 } 00306 } 00307 00308 if ( !$summary ){ 00309 foreach ($words as $p){ 00310 if ( !trim($p) ) continue; 00311 if ( preg_match_all('/.{0,50}'.preg_quote($p, '/').'.{0,50}/', $st, $matches, PREG_PATTERN_ORDER) ){ 00312 foreach ($matches[0] as $m){ 00313 $summary[] = $m; 00314 if ( count($summary) > 5 ) break; 00315 } 00316 } 00317 } 00318 } 00319 if ( $summary ){ 00320 $row['record_description'] = '...' .implode(' ... ', $summary).' ...'; 00321 } 00322 00323 $out[] = $row; 00324 00325 } 00326 @mysql_free_result($res); 00327 if ( $returnMetadata ){ 00328 $app =& Dataface_Application::getInstance(); 00329 $res = @mysql_query($countsql, df_db()); 00330 if ( !$res ) trigger_error(mysql_error(df_db()), E_USER_ERROR); 00331 $found = 0; 00332 $total_found = 0; 00333 $tables_matches = array(); 00334 00335 while ($row = mysql_fetch_row($res) ){ 00336 00337 $label = @$app->_conf['table_labels'][$row[1]]; 00338 if ( !$label ) $label = @$app->tables[$row[1]]; 00339 if ( !$label ) $label = $row[1]; 00340 $tables_matches[ $row[1] ] = array('found'=>$row[0], 'label'=>$label); 00341 $total_found += intval($row[0]); 00342 if ( !@$query['-table'] or $query['-table'] == $row[1] )$found += intval($row[0]); 00343 } 00344 @mysql_free_result($res); 00345 00346 $meta = array(); 00347 $meta['found'] = $found; 00348 $meta['skip'] = $query['-skip']; 00349 $meta['limit'] = $query['-limit']; 00350 $meta['start'] = $query['-skip']; 00351 $meta['end'] = min($meta['start']+$meta['limit'], $meta['found']); 00352 $meta['tables'] = $tables_matches; 00353 $meta['table'] = $query['-table']; 00354 $meta['table_objects'] =& $table_objects; 00355 $meta['total_found'] = $total_found; 00356 return array('results'=>$out, 'metadata'=>@$meta); 00357 } else { 00358 00359 return $out; 00360 } 00361 00362 } 00363 }