$c) { $words_flat[] = $word; } $word_ids = self::getWordIds($words_flat); if (in_array(null, $word_ids) or count($word_ids) == 0) { return array(); } return self::mySearchDocuments($word_ids, $project, $model); } /** * Search documents. * * Only the total of the ponderated occurences is used to sort the * results. * * @param array Ids. * @param IDF_Project Project to limit the search. * @param string Model class to limit the search. * @return array Sorted by score, returns model_class, model_id and score. */ public static function mySearchDocuments($wids, $project, $model) { $db =& Pluf::db(); $gocc = new IDF_Search_Occ(); $where = array(); foreach ($wids as $id) { $where[] = $db->qn('word').'='.(int)$id; } $prj = (is_null($project)) ? '' : ' AND project='.(int)$project->id; $md = (is_null($model)) ? '' : ' AND model_class='.$db->esc($model); $select = 'SELECT model_class, model_id, SUM(pondocc) AS score FROM '.$gocc->getSqlTable().' WHERE '.implode(' OR ', $where).$prj.$md.' GROUP BY model_class, model_id HAVING COUNT(*)='.count($wids).' ORDER BY score DESC'; return $db->select($select); } /** * Index a document. * * See Pluf_Search for the disclaimer and informations. * * @param Pluf_Model Document to index. * @param Stemmer used. ('Pluf_Text_Stemmer_Porter') * @return array Statistics. */ public static function index($doc, $stemmer='Pluf_Text_Stemmer_Porter') { $words = Pluf_Text::tokenize($doc->_toIndex()); if ($stemmer != null) { $words = self::stem($words, $stemmer); } // Get the total number of words. $total = 0.0; $words_flat = array(); foreach ($words as $word => $occ) { $total += (float) $occ; $words_flat[] = $word; } // Drop the last indexation. $gocc = new IDF_Search_Occ(); $sql = new Pluf_SQL('DELETE FROM '.$gocc->getSqlTable().' WHERE model_class=%s AND model_id=%s', array($doc->_model, $doc->id)); $db =& Pluf::db(); $db->execute($sql->gen()); // Get the ids for each word. $ids = self::getWordIds($words_flat); // Insert a new word for the missing words and add the occ. $n = count($ids); $new_words = 0; $done = array(); for ($i=0;$i<$n;$i++) { if ($ids[$i] === null) { $word = new Pluf_Search_Word(); $word->word = $words_flat[$i]; try { $word->create(); $new_words++; $ids[$i] = $word->id; } catch (Exception $e) { // 100% of the time, the word has been created // by another process in the background. $r_ids = self::getWordIds(array($word->word)); if ($r_ids[0]) { $ids[$i] = $r_ids[0]; } else { // give up for this word continue; } } } if (isset($done[$ids[$i]])) { continue; } $done[$ids[$i]] = true; $occ = new IDF_Search_Occ(); $occ->word = new Pluf_Search_Word($ids[$i]); $occ->model_class = $doc->_model; $occ->model_id = $doc->id; $occ->project = $doc->get_project(); $occ->occ = $words[$words_flat[$i]]; $occ->pondocc = $words[$words_flat[$i]]/$total; $occ->create(); } // update the stats $sql = new Pluf_SQL('model_class=%s AND model_id=%s', array($doc->_model, $doc->id)); $last_index = Pluf::factory('Pluf_Search_Stats')->getList(array('filter' => $sql->gen())); if ($last_index->count() == 0) { $stats = new Pluf_Search_Stats(); $stats->model_class = $doc->_model; $stats->model_id = $doc->id; $stats->indexations = 1; $stats->create(); } else { $last_index[0]->indexations += 1; $last_index[0]->update(); } return array('total' => $total, 'new' => $new_words, 'unique'=>$n); } /** * Remove an item from the index. * * You must call this function when you delete items wich are * indexed. Just add the call: * * IDF_Search::remove($this); * * in the preDelete() method of your object. * * @param mixed Item to be removed * @return bool Success */ public static function remove($item) { if ($item->id > 0) { $sql = new Pluf_SQL('model_id=%s AND model_class=%s', array($item->id, $item->_model)); $items = Pluf::factory('IDF_Search_Occ')->getList(array('filter'=>$sql->gen())); foreach ($items as $tl) { $tl->delete(); } } return true; } }