Sphinx不能严格按照字段排序的小问题
如果不想使用权重,只希望严格按照时间、主键等排序,而匹配模式(Matching modes)又为非SPH_MATCH_BOOLEAN时(比较常用的是SPH_MATCH_ALL、SPH_MATCH_EXTENDED),Sphinx搜索结果在某一页中的排序会不太准确。例如:按照UNIX时间戳倒序排序,0,20为第一页,20,40为第二页,第一页的最小时间戳一定会大于第二页的最大时间戳,但是,第一页中的0,20条记录却不会严格按照时间戳排序,第二页亦是如此。因此,如果需要精确排序,用户翻到搜索结果的某一页,就需要对Sphinx在某一搜索结果页中的记录另行再排序。
具体实现的PHP代码如下:
首先,需要设置匹配模式为 $this->sphinx->SetMatchMode ( SPH_MATCH_BOOLEAN );
其次,设置排序规则:$this->sphinx->SetSortMode( SPH_SORT_EXTENDED, "pub_date DESC, @id DESC" );
最后,对分页之后返回的结果数组进行一个完全倒序,问题就可以解决了。
代码如下:
<?php
class CSearch{
public $sphinx; //sphinx对象指针
public $indexer; //在指定的索引中查询关键词
public $keyword; //查询关键词
public function CSearch(){
$this->sphinx = new CSphinx;
$this->sphinx->SetFieldWeights( array( "title" => 10, "content" => 5 ) );
$this->sphinx->SetMatchMode ( SPH_MATCH_BOOLEAN );
$this->sphinx->SetSortMode( SPH_SORT_EXTENDED, "pub_date DESC, @id DESC" );
}
/*
<summary>全文检索的查询入口</summary>
<params>
<keyword>将要检索的关键词</keyword>
<offset>返回的结果是从第$offset条开始输出的</offset>
<limit>返回的结果中,每页显示10条数据</limit>
<indexer>在指定的索引中检索关键词</indexer>
</params>
*/
public function query( $keyword, $offset = 0, $limit = 10, $indexer = "articles" ){
$returned = new stdClass;
$this->indexer = $indexer;
$this->keyword = $keyword;
$this->sphinx->SetLimits( $offset, $limit );
$results = $this->sphinx->query( $keyword, $indexer ); //在指定的索引中检索关键词,并将符合条件的结果集放在$results变量中
$returned->total = $results["total"]; //符合条件的结果条数
$returned->time = $results["time"]; //查询所耗费的时间,单位:秒
$article_ids = array_keys( $results["matches"] ); //符合条件的文章ID数组
unset( $results ); //释放sphinx查询所创建的结果集对象
if( empty( $article_ids ) ){
$returned->data = Array();
return $returned;
}
$data = $this->get_data( $article_ids ); //获取文章数据
$this->get_formated_data( $data, "content" ); //获取格式化之后的数据:产生文档片段(摘要)
$this->get_formated_data( $data, "title" ); //获取格式化之后的数据:产生文档片段(摘要)
$returned->data = array_reverse( $data );
return $returned;
}
/*
<summary>根据文章ID数组获取文章标题、作者、日期、guid和正文</summary>
<params>
<article_ids>文章ID数组</article_ids>
</params>
*/
public function get_data( $article_ids ){
return get_content_from_ids( $article_ids );
}
/*
*/
public function get_formated_data( &$data, $field = "content" ){
//$opts为包含其他可选的高亮参数的hash
$opts = Array();
$opts["single_passage"] = true; //是否仅抽取最佳的一个段落。布尔值。
$opts["before_match"] = '<span class="c_orange">'; //在匹配的关键字前面插入的字符
$opts["after_match"] = "</span>";
$opts["limit"] = 160; //摘要最多包含的符号(码点)数。整数
$contents = Array();
foreach( $data as $item ){
$item = trim( strip_tags( $item[$field] ) );
$contents[] = preg_replace( "/[\r\t\n]+/", "", $item );
}
$contents = $this->sphinx->buildExcerpts($contents, $this->indexer, $this->keyword, $opts );
foreach ($data as $k => &$v) {
$v[$field] = $contents[$k];
}
return $data;
}
}
?>
如果不想使用权重,只希望严格按照时间、主键等排序,而匹配模式(Matching modes)又为非SPH_MATCH_BOOLEAN时(比较常用的是SPH_MATCH_ALL、SPH_MATCH_EXTENDED),Sphinx搜索结果在某一页中的排序会不太准确。例如:按照UNIX时间戳倒序排序,0,20为第一页,20,40为第二页,第一页的最小时间戳一定会大于第二页的最大时间戳,但是,第一页中的0,20条记录却不会严格按照时间戳排序,第二页亦是如此。因此,如果需要精确排序,用户翻到搜索结果的某一页,就需要对Sphinx在某一搜索结果页中的记录另行再排序。
具体实现的PHP代码如下:
首先,需要设置匹配模式为 $this->sphinx->SetMatchMode ( SPH_MATCH_BOOLEAN );
其次,设置排序规则:$this->sphinx->SetSortMode( SPH_SORT_EXTENDED, "pub_date DESC, @id DESC" );
最后,对分页之后返回的结果数组进行一个完全倒序,问题就可以解决了。
代码如下:
<?php
class CSearch{
public $sphinx; //sphinx对象指针
public $indexer; //在指定的索引中查询关键词
public $keyword; //查询关键词
public function CSearch(){
$this->sphinx = new CSphinx;
$this->sphinx->SetFieldWeights( array( "title" => 10, "content" => 5 ) );
$this->sphinx->SetMatchMode ( SPH_MATCH_BOOLEAN );
$this->sphinx->SetSortMode( SPH_SORT_EXTENDED, "pub_date DESC, @id DESC" );
}
/*
<summary>全文检索的查询入口</summary>
<params>
<keyword>将要检索的关键词</keyword>
<offset>返回的结果是从第$offset条开始输出的</offset>
<limit>返回的结果中,每页显示10条数据</limit>
<indexer>在指定的索引中检索关键词</indexer>
</params>
*/
public function query( $keyword, $offset = 0, $limit = 10, $indexer = "articles" ){
$returned = new stdClass;
$this->indexer = $indexer;
$this->keyword = $keyword;
$this->sphinx->SetLimits( $offset, $limit );
$results = $this->sphinx->query( $keyword, $indexer ); //在指定的索引中检索关键词,并将符合条件的结果集放在$results变量中
$returned->total = $results["total"]; //符合条件的结果条数
$returned->time = $results["time"]; //查询所耗费的时间,单位:秒
$article_ids = array_keys( $results["matches"] ); //符合条件的文章ID数组
unset( $results ); //释放sphinx查询所创建的结果集对象
if( empty( $article_ids ) ){
$returned->data = Array();
return $returned;
}
$data = $this->get_data( $article_ids ); //获取文章数据
$this->get_formated_data( $data, "content" ); //获取格式化之后的数据:产生文档片段(摘要)
$this->get_formated_data( $data, "title" ); //获取格式化之后的数据:产生文档片段(摘要)
$returned->data = array_reverse( $data );
return $returned;
}
/*
<summary>根据文章ID数组获取文章标题、作者、日期、guid和正文</summary>
<params>
<article_ids>文章ID数组</article_ids>
</params>
*/
public function get_data( $article_ids ){
return get_content_from_ids( $article_ids );
}
/*
*/
public function get_formated_data( &$data, $field = "content" ){
//$opts为包含其他可选的高亮参数的hash
$opts = Array();
$opts["single_passage"] = true; //是否仅抽取最佳的一个段落。布尔值。
$opts["before_match"] = '<span class="c_orange">'; //在匹配的关键字前面插入的字符
$opts["after_match"] = "</span>";
$opts["limit"] = 160; //摘要最多包含的符号(码点)数。整数
$contents = Array();
foreach( $data as $item ){
$item = trim( strip_tags( $item[$field] ) );
$contents[] = preg_replace( "/[\r\t\n]+/", "", $item );
}
$contents = $this->sphinx->buildExcerpts($contents, $this->indexer, $this->keyword, $opts );
foreach ($data as $k => &$v) {
$v[$field] = $contents[$k];
}
return $data;
}
}
?>