<?php


/**
 * 検索機能のクラス。
 * 
 * Wikiごとにシングルトン。
 */
class Search
{
	protected $wikiid;
	
	
	//あいまい検索用変数群
	static $fuzzypattern = array(
		'ヴァ' => 'バ',	'ヴィ' => 'ビ',	'ヴェ' => 'ベ',	'ヴォ' => 'ボ',
		'ヴ' => 'ブ',	'ヰ' => 'イ',	'ヱ' => 'エ',	'ヵ' => 'カ',
		'ァ' => 'ア',	'ィ' => 'イ',	'ゥ' => 'ウ',	'ェ' => 'エ',
		'ォ' => 'オ',	'ャ' => 'ヤ',	'ュ' => 'ユ',	'ョ' => 'ヨ');
	static $fuzzy_from;
	static $fuzzy_to;
	
	
	/**
	 * インスタンスを取得する。IDごとにシングルトン。
	 * 
	 * @param	$id	WikiFarmのID。指定しない場合は現在のWikiFarmのIDが使われる。
	 */
	static function getinstance($id = WIKIID)
	{
		static $ins = array();
		if($ins == array()){
			Search::$fuzzy_from = array_keys(Search::$fuzzypattern);
			Search::$fuzzy_to = array_values(Search::$fuzzypattern);
		}
		
		if(!isset($ins[$id])){
			$ins[$id] = new Search($id);
		}
		return $ins[$id];
	}
	
	
	/**
	 * コンストラクタ。
	 */
	protected function __construct($id)
	{
		$this->wikiid = $id;
	}
	
	
	/**
	 * ページ検索する。
	 * 
	 * @param	array(string)	$word	検索語句。
	 * @param	bool	$andsearch	trueの場合はAND検索、falseの場合はOR検索。
	 * @return	array(string)	ページ名。アルファベット順にソート済み。
	 */
	function normalsearch($word, $andsearch = true)
	{
		$db = DataBase::getinstance($this->wikiid);
		
		for($i = 0; $i < count($word); $i++){
			$_word[] = $db->escape($word[$i]);
		}
		
		$andor = $andsearch ? 'AND' : 'OR';
		$query  = "SELECT pagename FROM page";
		$query .= " WHERE";
		$query .= "  (pagename like '%" . join("%' $andor pagename like '%", $_word) . "%')";
		$query .= "  OR";
		$query .= "  (source like '%" . join("%' $andor source like '%", $_word) . "%')";
		$query .= " ORDER BY pagename ASC";
		return $this->_search($query);
	}
	
	
	/**
	 * あいまい検索する。
	 * 
	 * @param	array(string)	$word	検索語句。
	 * @param	bool	$andsearch	trueの場合はAND検索、falseの場合はOR検索。
	 * @return	array(string)	ページ名。アルファベット順にソート済み。
	 */
	function fuzzysearch($word, $andsearch = true)
	{
		$db = DataBase::getinstance($this->wikiid);
		
		for($i = 0; $i < count($word); $i++){
			$_word[] = $db->escape($word[$i]);
		}
		
		$db->create_function('fuzzymatch', array('Search', 'sqlite_udf_fuzzymatch'), 2);
		
		$andor = $andsearch ? 'AND' : 'OR';
		$query  = "SELECT pagename FROM page";
		$query .= " WHERE";
		$query .= "  (fuzzymatch('" . join("', pagename) $andor fuzzymatch('", $_word) . "', pagename))";
		$query .= "  OR";
		$query .= "  (fuzzymatch('" . join("', source) $andor fuzzymatch('", $_word) . "', source))";
		$query .= " ORDER BY pagename ASC";
		return $this->_search($query);
	}
	
	
	/**
	 * 正規表現検索する。
	 * 
	 * @param	array(string)	$word	検索語句。
	 * @param	bool	$andsearch	trueの場合はAND検索、falseの場合はOR検索。
	 * @return	array(string)	ページ名。アルファベット順にソート済み。
	 */
	function eregsearch($word, $andsearch = true)
	{
		$db = DataBase::getinstance($this->wikiid);
		
		for($i = 0; $i < count($word); $i++){
			$_word[] = $db->escape($word[$i]);
		}
		
		$andor = $andsearch ? 'AND' : 'OR';
		$query  = "SELECT pagename FROM page";
		$query .= " WHERE";
		$query .= "  (php('mb_ereg', '" . join("', pagename) $andor php('mb_ereg', '", $_word) . "', pagename))";
		$query .= "  OR";
		$query .= "  (php('mb_ereg', '" . join("', source) $andor php('mb_ereg', '", $_word) . "', source))";
		$query .= " ORDER BY pagename ASC";
		return $this->_search($query);
	}
	
	
	/**
	 * 更新日時で検索する。
	 * 
	 * @param	int	$from	開始日時のタイムスタンプ
	 * @param	int	$to	終了日時のタイムスタンプ
	 * @return	array(string)	ページ名。新しい順にソート済み。
	 */
	function timesearch($from, $to)
	{
		$query  = "SELECT pagename FROM page";
		$query .= " WHERE ($from <= timestamp AND timestamp <= $to)";
		$query .= " ORDER BY timestamp DESC";
		return $this->_search($query);
	}
	
	
	/**
	 * 検索クエリ実行。
	 * 
	 * @return	array(string)
	 */
	protected function _search($query)
	{
		$db = DataBase::getinstance($this->wikiid);
		$result = $db->query($query);
		$ret = array();
		while($row = $db->fetch($result)){
			$ret[] = $row['pagename'];
		}
		return $ret;
	}
	
	
	/**
	 * あいまい検索用SQLite登録関数。
	 */
	function sqlite_udf_fuzzymatch($pattern, $string)
	{
		//仮名は全角カタカナに、英数字・空白文字は半角に。
		$_pattern = mb_strtolower(mb_convert_kana($pattern, 'KVCas'));
		$_string = mb_strtolower(mb_convert_kana($string, 'KVCas'));
		//表記揺れの統一
		for($i = 0; $i < count(Search::$fuzzy_from); $i++){
			$_pattern = mb_ereg_replace(Search::$fuzzy_from[$i], Search::$fuzzy_to[$i], $_pattern);
			$_string = mb_ereg_replace(Search::$fuzzy_from[$i], Search::$fuzzy_to[$i], $_string);
		}
		//文字により削除
		$_pattern = mb_ereg_replace('[ッー・゛゜、。]', '', $_pattern);
		$_string = mb_ereg_replace('[ッー・゛゜、。]', '', $_string);
		
		return (bool)mb_ereg(mb_ereg_quote($_pattern), $_string);
	}
}

?>