/* * @todo : sphinx實時索引curd操做類,適應sphinx 1.10-beta版 * @created :Wed Nov 10 10:38:27 CST 2010 * @email:blvming@gmail.com * @website:http://www.sphinxsearch.org * @author blvming * @version 1.0 * @example * * $sphinx = new SphinxRt('articleRt','127.0.0.1:9306'); * //打開調試信息 * $sphinx->debug = true; * //查詢 * $prodList = $sphinx->where($condition)->order($orderCondition)->group('prod_uid')->search(); * //插入數據 * $sphinxData['title'] = $title; * $sphinxData['content'] = $content; * $sphinx->insert($sphinxData); * */ class SphinxRt { private $_link; //sphinx 鏈接池 protected $_field = array(); //當前索引的字段屬性 protected $_sql = array(); //sql表達式 protected $queryStr = ''; //查詢的sql public $rt = '' ; //當前索引 public $error = ''; //最後的錯誤信息 public $debug = false; //調試狀態 //構造函數 public function __construct($rt='',$host='192.168.1.10:9306') { try { $this->_link = mysql_connect($host); if(!$this->_link) { throw new Exception('sphinx 實時索引服務器鏈接失敗!'); } if($rt !='') { $this->rt = $this->_sql['rt'] = $rt; } } catch (Exception $e) { $this->error = $e->getMessage(); } } /** +---------------------------------------------------------- * @todo 設置索引表 * @access public * @param param * @return void +---------------------------------------------------------- */ public function rt($rt) { $this->_sql['rt'] = $this->rt = $rt; return $this; } /** +---------------------------------------------------------- * @todo where 匹配條件.注意:這裏必定要主動加上where 關鍵詞 不能出現這樣的狀況 where 1 * @access public * @param $where * @return void +---------------------------------------------------------- */ public function where($where) { $this->_sql['where'] = ' where '.$where; return $this; } /** +---------------------------------------------------------- * @todo limit * @access public * @param param * @return void +---------------------------------------------------------- */ public function limit($limit) { $this->_sql['limit'] = $limit; return $this; } /** +---------------------------------------------------------- * @todo option 評分權值設定等 * @access public * @param param * @return void +---------------------------------------------------------- */ public function option($option) { $this->_sql['option'] = $option; return $option; } /** +---------------------------------------------------------- * @todo field * @access public * @param param * @return void +---------------------------------------------------------- */ public function field($field) { $this->_sql['field'] = $field; return $this; } /** +---------------------------------------------------------- * @todo order * @access public * @param param * @return void +---------------------------------------------------------- */ public function order($order) { $this->_sql['order'] = $order; return $this; } /** +---------------------------------------------------------- * @todo group * @access public * @param param * @return void +---------------------------------------------------------- */ public function group($group,$withGroup) { $this->_sql['group'] = $group; if($group) { $this->_sql['withGroup'] = $withGroup; } return $this; } /** +---------------------------------------------------------- * @todo 檢索數據,並對數據進行排序,過濾,評分設定等 * @access public * @param param * @example select * from rt where match('keyword') group by gid WITHIN GROUP ORDER BY @weight DESC * order by gid desc limit 0,1 option ranker=bm25,max_matches=3,field_weights=(title=10,content=3); * @return array +---------------------------------------------------------- */ public function search() { //排序 if($this->_sql['order'] != '') { $orderSql = ' ORDER BY '.$this->_sql['order']; } //分組聚合 if($this->_sql['group'] !='') { $groupSql = ' GROUP BY '.$this->_sql['group']; //組內排序 if ($this->_sql['withGroup']!='') { $groupSql .= ' WITHIN GROUP ORDER BY '.$this->_sql['withGroup']; } } //附加選項 if($this->_sql['option'] !='') { $optionSql = ' OPTION '.$this->_sql['option']; } //數量限制 if($this->_sql['limit']!='') { $limitSql = 'limit '.$this->_sql['limit']; } //字段 if($this->_sql['field']=='') { $field = '*'; } else { $field= $this->_sql['field']; } if($this->_sql['where']!='') { $where = $this->_sql['where']; } else { $where =''; } $this->queryStr = sprintf("SELECT %s FROM %s %s %s %s %s %s",$field,$this->_sql['rt'],$where,$groupSql,$orderSql,$limitSql,$optionSql); $rs = $this->query(); if($rs) { $resArr = array(); while ($row = mysql_fetch_assoc($rs)) { $resArr[] = $row; } $resArr['meta'] = $this->getMeta(); return $resArr; } return false; } /** +---------------------------------------------------------- * @todo 添加索引,注意,這裏的添加並未考慮併發操做,可能在sphinx端會出現id衝突 * @access public * @param mixed $data 插入的數據 * @return bool +---------------------------------------------------------- */ public function insert($data,$lastId=0) { if(!empty($data)) { if($lastId===0) { $lastId = $this->getLastId(); } foreach ($data as $k=>$v) { $fields .= ','.$k; $values .= ",'".$v."'"; } $this->queryStr = "insert into ".$this->_sql['rt']."(id".$fields.") values ($lastId {$values})"; return $this->query(); } $this->error = '插入數據不能爲空'; return false; } /** +---------------------------------------------------------- * @todo 批量插入數據 * @access public * @param mixed $datas * @param boolean $asStr 是否使用逗號分隔的方式一次性插入 * @return void +---------------------------------------------------------- */ public function insertAll($datas,$asStr=true) { if(!empty($datas)) { $fields = 'id'; //字段 $values =''; //值 $lastId = $this->getLastId(); $i = 0; foreach ($datas as $k=>$v) { //一次性插入數據,格式化 if($asStr) { $values .=',('.($i+$lastId); foreach ($v as $kk=>$va) { //屬性字段 if($i==0) { $fields .= ','.$kk; } $values .= ",'".$va."'"; } $i++; $values .= ')'; } else { $this->insert($v,$lastId); } } //批量數據sql格式化 if($asStr) { $values = ltrim($values,','); $this->queryStr = sprintf("insert into {$this->_sql['rt']}(%s) values %s",$fields,$values); return $this->query(); } } else { $this->error = '無效數據!'; return false; } } /** +---------------------------------------------------------- * @todo 更新索引數據 * @access public * @param mixed $data 要更新的數據 * @param int $id 更新條件id * @return bool +---------------------------------------------------------- */ public function update($data,$id,$insert=true) { if(!empty($data) || $id>0) { //若是未找到記錄且不須要不須要插入的話 if($insert ===false && $this->getById($id) ===false) return true; foreach ($data as $k=>$v) { $fields .= ','.$k; $values .= ",'".$v."'"; } //若該條數據不存在,直接插入 $this->queryStr = "replace into ".$this->_sql['rt']."(id".$fields.") values ($id{$values})"; return $this->query(); } $this->error = '無效更新數據!'; return false; } /** +---------------------------------------------------------- * @todo 條件刪除索引,如,根據外部id刪除 * @access public * @param $condition * @return void +---------------------------------------------------------- */ public function delBy($condition) { $rs = $this->where($condition)->search(); if($rs) { foreach ($rs as $v) { if($v['id']) $idArr[] = $v['id']; } $this->delete($idArr); return true; } return false; } /** +---------------------------------------------------------- * @todo 刪除索引數據,sphinx暫未提供批量刪除的功能,如 in (123,34,565); * @access public * @param mixed $id * @return void +---------------------------------------------------------- */ public function delete($id) { if(is_array($id) && count($id)>=1) { $rs = true; foreach ($id as $v) { $this->queryStr = sprintf("delete from %s where id=%d",$this->_sql['rt'],$v); $rs &= $this->query(); } } else { $this->queryStr = sprintf("delete from %s where id=%d",$this->_sql['rt'],$id); $rs = $this->query(); } return $rs; } /** +---------------------------------------------------------- * @todo 清空表 * @access public * @return bool +---------------------------------------------------------- */ public function truncate() { $lastId = $this->getLastId(); for ($i=1;$i<=$lastId;$i++) { $this->delete($i); } return true; } /** +---------------------------------------------------------- * @todo 獲取總記錄 * @access public * @param param * @return void +---------------------------------------------------------- */ public function countAll() { $this->queryStr = "SELECT * FROM $this->_sql['rt'] "; $this->query(); $meta = $this->getMeta(); if($meta) { return $meta['total_found']; } return false; } /** +---------------------------------------------------------- * @todo 獲取當前最大值id,實現如mysql的auto_increment功能 * @access public * @param param * @return void +---------------------------------------------------------- */ public function getLastId() { $this->queryStr = "select * from {$this->_sql['rt']} order by id desc limit 1"; $rs = $this->query(); //若存在值,則取最大id的值,不然爲1 $row = mysql_fetch_assoc($rs); if($row) { $lastId = $row['id']+1; } return $lastId?$lastId:1; } /** +---------------------------------------------------------- * @todo 獲取查詢狀態值 * @access protected * @param param * @return array(); +---------------------------------------------------------- */ protected function getMeta() { $metaSql = "show meta"; $meta = mysql_query($metaSql); while ($row = mysql_fetch_assoc($meta)) { $metaArr[$row['Variable_name']] = $row['Value']; } return $metaArr; } /** +---------------------------------------------------------- * @todo 根據id獲取記錄 * @access public * @param int $id * @return array +---------------------------------------------------------- */ public function getById($id) { if($id>0) { $sql = "'select * from $this->rt where id=".$id; $rs = mysql_query($sql); $row = mysql_fetch_assoc($rs); return $row; } return false; } /** +---------------------------------------------------------- * @todo 獲取索引的字段值,前提條件是索引服務器中必須至少一個值,暫時沒有api顯示能夠直接像mysql 的語句 desc table 來獲取索引的字段; * @access public * @param param * @return void +---------------------------------------------------------- */ public function _getField($rt) { $rt = $rt?$rt:$this->rt; $this->queryStr = "select * from {$rt} limit 1"; $res = $this->query(); if($res) { $row = mysql_fetch_assoc($res); $field = array_keys($row); unset($field[1]); //去掉weight,這個字段是sphinx的權重值 return $field; } else { $this->error = '實時索引'.$rt.'沒有任何記錄,沒法獲取索引字段'; return false; } } /** +---------------------------------------------------------- * @todo mysql查詢 * @access public * @param param * @return void +---------------------------------------------------------- */ public function query($sql = '') { if($sql == '') { $sql = $this->queryStr; } if(!$this->_link) $this->triggerDebug($this->debug); $rs = mysql_query($sql,$this->_link); if(!$rs) $this->error = mysql_error(); $this->triggerDebug($this->debug); return $rs; } /** +---------------------------------------------------------- * @todo 獲取錯誤信息 * @access public * @return string +---------------------------------------------------------- */ public function getError() { return $this->error; } /** +---------------------------------------------------------- * @todo 獲取最後的sql語句 * @access public * @param param * @return string +---------------------------------------------------------- */ public function getLastSql() { return $this->queryStr; } /** +---------------------------------------------------------- * @todo 觸發錯誤信息 * @access public * @param param * @return void +---------------------------------------------------------- */ public function triggerDebug($debugMode=false) { if($debugMode) { $debugInfo = debug_backtrace(); $errorStr = 'file:'.$debugInfo[0]['file']; $errorStr .= ' line:'.$debugInfo[0]['line']; $errorStr .= ' sql:'.$debugInfo[0]['object']->queryStr; $errorStr .= ' error:'.$debugInfo[0]['object']->error.''; if($debugInfo[0]['object']->error!='')die($errorStr); echo ($errorStr); } return ; } } ?>