我之前使用Yii2框架做了一个电影网站dy360.net,网站的搜索是通过MySQL的like实现,搜索的条件很多,担心以后数据越来越多,会拖垮网站的速度,于是想通过搜索引擎来解决! 开始准备使用sphinx,研究发现不太好使。后发现迅搜(xunsearch),文档比较清楚,上手比较简单,并且有Yii2的composer包,于是决定使用迅搜。
使用的过程中遇到这样一个问题:一部电影有多个演员,出品国家或地区,其实就是文档的多值属性的新建、搜索的问题。我研究了一下,最终结局方案如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
project.name = yddy project.default_charset = utf-8 server.index = 8383 server.search = 8384
[id] type = id phrase = yes
[summary] type = body cutlen=900
[year] type = numeric phrase = yes index = self [actors_text] type = string index = self tokenizer = split(/)
[category_id] type = string index = self tokenizer = split(,)
[country_id] type = string index = self tokenizer = split(,) phrase = yes
|
其实就是使用迅搜的tokenizer分词器,文档上具体的语法如下:
1 2 3 4 5 6 7 8 9 10 11
| tokenizer 分词器
默认为 default 采用内置的功能强大的 scws 分词,适合绝大多数字符串字段。也可以指定自定义分词器, 格式为 name 或 name(arg) 两种形式,其中 name 是分词器名称,arg 则是传递给分词器构造函数的参数。 自定义分词器需要在 lib/ 目录下编写名为 XSTokenizerName 的分词类并实现接口 XSTokenizer, 内置支持的分词器有以下几种:
full 表示本字段的值整体作为一个检索词,像各种 ID 都适合这种情况 none 表示本字段没有任何词汇用于索引 split([ ]) 表示根据参数分割内容,默认参数为空格,若参数以 / 开头并以 / 结尾则 内部调用 preg_split(arg, ..) 来分割取词,以支持正则或其它特殊字符分割 xlen([2]) 表示根据指定参数长度分段取词,如 ABCDEF => AB + CD + EF xstep([2]) 表示根据指定参数步长逐段取词,如 ABCDEF => AB + ABCD + ABCDEF scws([3]) 表示采用指定参数为复合等级的 scws 分词,(若无特殊复合需求,无需指定) tokenizer = default
|
然后,根据不同情况使用不同的分割参数,譬如,有的使用’/‘,有的使用’,’。
最后,搜索代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public static function search($params){ $db = Yii::$app->xunsearch->getDatabase('yddy'); $xs = $db->xs;
$search = $xs->getSearch();
$keywords = $params['word']; if(empty($keywords)){ $keywords =''; } $search = $search->setQuery($keywords);
if($category=$params['category']){ $search->addQueryTerm('category_id',$category); } if($country=$params['country']){ $search->addQueryTerm('country_id',$country); }
if( (empty($params['other']) ) && ($year = intval($params['year'])) && is_numeric($params['year'])){ $search->addQueryTerm('year',$year);
}elseif(($params['other'] == 'ago') && ($year = intval($params['year'])) ){ //$search->addRange('year',1900,1978); $search->addRange('year',1900,$year); }
if( $rating=intval($params['rating'])){ $search->addQueryTerm('rate', $rating); }
$search->addQueryTerm('subtype',$params['subtype']); //$search->addQueryTerm('status',1); //echo $search->getQuery(); return $search;
}
|