284 lines
6.9 KiB
PHP
284 lines
6.9 KiB
PHP
<?php
|
||
class Search
|
||
{
|
||
var $terms;
|
||
var $text;
|
||
var $xs;
|
||
var $search;
|
||
var $error;
|
||
var $pager;
|
||
var $count;
|
||
var $related;
|
||
var $corrected;
|
||
var $total;
|
||
var $search_cost;
|
||
var $docs;
|
||
var $hot;
|
||
var $base_url;
|
||
var $expanded;
|
||
|
||
//require_once '/home/wlx/xunsearch/sdk/php/lib/XS.php';
|
||
|
||
private function safe_query($search)
|
||
{
|
||
return preg_replace('/%|_|\'|\\\\/', '\\\\$0', stripslashes($search));
|
||
}
|
||
|
||
function parse_search($search="", $safe = true)
|
||
{
|
||
if (empty($search)) $search=$this->text;
|
||
$temp = array();
|
||
preg_match_all('/"([^"]+)"|([^\\s]+)/', (( $safe ) ? $this->safe_query($search) : $search), $temp);
|
||
|
||
for ($i = 1; $i < count($temp); $i++)
|
||
{
|
||
foreach ( $temp[$i] as $value )
|
||
{
|
||
if ( strlen($value) >= 3 )
|
||
{
|
||
$this->terms[] = $value;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
function sql_expr($field)
|
||
{
|
||
$sql=" 1=1 ";
|
||
if (!is_array($field))
|
||
{
|
||
$field=array($field);
|
||
}
|
||
if (!count($this->terms))
|
||
$this->parse_search();
|
||
foreach($this->terms as $t)
|
||
{
|
||
$sql.=" and (1<>1 ";
|
||
foreach($field as $f)
|
||
{
|
||
$sql.=" or ".$f." ilike '%".$t."%' ";
|
||
}
|
||
$sql.=") ";
|
||
}
|
||
return $sql;
|
||
}
|
||
function __construct($text='')
|
||
{
|
||
require_once '/home/wlx/xunsearch/sdk/php/lib/XS.php';
|
||
$this->xs=new XS('westdc');
|
||
$this->search=$this->xs->search;
|
||
$this->terms = array();
|
||
$this->text=$text;
|
||
|
||
// other variable maybe used
|
||
$this->count = $this->total = $this->search_cost = 0;
|
||
$this->docs = $this->related = $this->corrected =$this->expanded = $this->hot = array();
|
||
$this->error = $this->pager = $this->base_url='';
|
||
}
|
||
|
||
function dosearch()
|
||
{
|
||
//
|
||
// 支持的 GET 参数列表
|
||
// q: 查询语句
|
||
// m: 开启模糊搜索,其值为 yes/no
|
||
// f: 只搜索某个字段,其值为字段名称,要求该字段的索引方式为 self/both
|
||
// s: 排序字段名称及方式,其值形式为:xxx_ASC 或 xxx_DESC
|
||
// p: 显示第几页,每页数量为 XSSearch::PAGE_SIZE 即 10 条
|
||
// ie: 查询语句编码,默认为 UTF-8
|
||
// oe: 输出编码,默认为 UTF-8
|
||
// xml: 是否将搜索结果以 XML 格式输出,其值为 yes/no
|
||
//
|
||
// variables
|
||
$eu = '';
|
||
$__ = array('q', 'm', 'f', 's', 'p', 'ie', 'oe', 'syn', 'xml','east','west','south','north','begin','end');
|
||
foreach ($__ as $_)
|
||
$$_ = isset($_GET[$_]) ? $_GET[$_] : '';
|
||
|
||
// recheck request parameters
|
||
$q = get_magic_quotes_gpc() ? stripslashes($q) : $q;
|
||
$f = empty($f) ? '_all' : $f;
|
||
${'m_check'} = ($m == 'yes' ? ' checked' : '');
|
||
${'syn_check'} = ($syn == 'yes' ? ' checked' : '');
|
||
${'f_' . $f} = ' checked';
|
||
${'s_' . $s} = ' selected';
|
||
if (!isset($q)) $q='';
|
||
// base url
|
||
$this->base_url = '/search?q=' . urlencode($q) . '&m=' . $m . '&f=' . $f . '&s=' . $s .'&begin='.$begin.'&end='.$end.'&east='.$east.'&north='.$north.'&west='.$west.'&south='.$south. $eu;
|
||
|
||
$total_begin = microtime(true);
|
||
// perform the search
|
||
try
|
||
{
|
||
$this->search->setCharset('UTF-8');
|
||
|
||
if (empty($q))
|
||
{
|
||
// just show hot query
|
||
$this->hot = $this->search->getHotQuery();
|
||
}
|
||
else
|
||
{
|
||
// fuzzy search
|
||
$this->search->setFuzzy($m === 'yes');
|
||
|
||
// synonym search
|
||
$this->search->setAutoSynonyms($syn === 'yes');
|
||
|
||
// set query
|
||
if (!empty($f) && $f != '_all')
|
||
{
|
||
$this->search->setQuery($f . ':(' . $q . ')');
|
||
}
|
||
else
|
||
{
|
||
$this->search->setQuery($q);
|
||
}
|
||
|
||
//spatial search
|
||
if (!empty($east) && !empty($west) && !empty($south) && !empty($north))
|
||
{
|
||
$this->search->addRange('east',null,$east);
|
||
$this->search->addRange('west',$west,null);
|
||
$this->search->addRange('south',$south,null);
|
||
$this->search->addRange('north',null,$north);
|
||
}
|
||
|
||
//date search
|
||
if (!empty($begin))
|
||
{
|
||
$from=strtotime($begin);
|
||
$this->search->addRange('timebegin',$from,null);
|
||
}
|
||
if (!empty($end))
|
||
{
|
||
$to=strtotime($end);
|
||
$this->search->addRange('timeend',null,$to);
|
||
}
|
||
|
||
// set sort
|
||
if (($pos = strrpos($s, '_')) !== false)
|
||
{
|
||
$sf = substr($s, 0, $pos);
|
||
$st = substr($s, $pos + 1);
|
||
$this->search->setSort($sf, $st === 'ASC');
|
||
}
|
||
|
||
// set offset, limit
|
||
$p = max(1, intval($p));
|
||
$n = XSSearch::PAGE_SIZE;
|
||
$this->search->setLimit($n, ($p - 1) * $n);
|
||
|
||
// get the result
|
||
$search_begin = microtime(true);
|
||
$this->docs = $this->search->search();
|
||
$this->search_cost = microtime(true) - $search_begin;
|
||
|
||
// get other result
|
||
$this->count = $this->search->getLastCount();
|
||
$this->total = $this->search->getDbTotal();
|
||
|
||
if ($xml !== 'yes')
|
||
{
|
||
if ($this->count<1)
|
||
$this->expanded=$this->search->getExpandedQuery($q);
|
||
// try to corrected, if resul too few
|
||
if ($this->count < 1 || $this->count < ceil(0.001 * $this->total))
|
||
$this->corrected = $this->search->getCorrectedQuery();
|
||
// get related query
|
||
$this->related = $this->search->getRelatedQuery();
|
||
}
|
||
|
||
// gen pager
|
||
if ($this->count > $n)
|
||
{
|
||
$pb = max($p - 5, 1);
|
||
$pe = min($pb + 10, ceil($this->count / $n) + 1);
|
||
$this->pager = '';
|
||
do
|
||
{
|
||
$this->pager .= ($pb == $p) ? '<strong>' . $p . '</strong>' : '<a href="' . $this->base_url . '&p=' . $pb . '">[' . $pb . ']</a>';
|
||
}
|
||
while (++$pb < $pe);
|
||
}
|
||
}
|
||
}
|
||
catch (XSException $e)
|
||
{
|
||
$this->error = strval($e);
|
||
}
|
||
// calculate total time cost
|
||
$this->total_cost = microtime(true) - $total_begin;
|
||
|
||
// XML OUPUT
|
||
if ($xml === 'yes' && !empty($q))
|
||
{
|
||
header("Content-Type: text/xml; charset=$oe");
|
||
echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
|
||
echo "<xs:result count=\"$this->count\" total=\"$this->total\" cost=\"$this->total_cost\" xmlns:xs=\"http://www.xunsearch.com\">\n";
|
||
if ($this->error !== '')
|
||
echo " <error><![CDATA[" . $this->error . "]]></error>\n";
|
||
|
||
foreach ($this->docs as $doc)
|
||
{
|
||
echo " <doc index=\"" . $doc->rank() . "\" percent=\"" . $doc->percent() . "%\">\n";
|
||
foreach ($doc as $k => $v)
|
||
{
|
||
echo " <$k>";
|
||
if (is_numeric($v))
|
||
echo $v;
|
||
else
|
||
echo "\n <![CDATA[" . $v . "]]>\n ";
|
||
echo "</$k>\n";
|
||
}
|
||
echo " </doc>\n";
|
||
}
|
||
echo "</xs:result>\n";
|
||
exit(0);
|
||
}
|
||
}
|
||
//搜索建议
|
||
function suggest($q)
|
||
{
|
||
$terms = array();
|
||
if (!empty($q) && strpos($q, ':') === false)
|
||
{
|
||
try {
|
||
$terms = $this->search->setCharset('UTF-8')->getExpandedQuery($q);
|
||
} catch (XSException $e) { }
|
||
}
|
||
return json_encode($terms);
|
||
}
|
||
|
||
//添加新文档
|
||
//$data: 包含field和value的数组
|
||
function add($data)
|
||
{
|
||
$doc=new XSDocument;
|
||
$index=$this->xs->index;
|
||
$doc->setFields($data);
|
||
$index->add($doc);
|
||
}
|
||
|
||
//更新已有文档
|
||
//$data: 包含field和value的数组
|
||
function update($data)
|
||
{
|
||
$doc=new XSDocument;
|
||
$index=$this->xs->index;
|
||
$doc->setFields($data);
|
||
$index->update($doc);
|
||
}
|
||
//根据主键删除对应的索引
|
||
function del($data,$field='')
|
||
{
|
||
$index=$this->xs->index;
|
||
if (empty($field))
|
||
$index->del($data);
|
||
else {
|
||
$index->del($data,$field);
|
||
}
|
||
}
|
||
}
|