用Drupal很多年一直都是用的自带搜索,但存在数据量大时速度很慢、搜索结果不准确等问题,中途尝试过Apache Solr,但感觉太复杂、麻烦了。也知道Views可以用作内部搜索,但一直没有实施。
最近在设法降低MySQL服务器负载时,查看RDS后台的慢查询日志,发现有相当多的慢查询都是Drupal自带的搜索造成的,特别是数据量大的中文站、搜索汉字词特别长的时候。还有些数据量大的站,生成搜索索引都需要非常长的时间(生成的搜索表可能比内容类型的字段表大得多),自己去试着搜索也是非常慢(有些需要几十秒以上,几乎无法正常使用)。于是就尝试用Views来替代,现在记录如下:
Views是可以带参数来列表的,一种是使用Advanced设置里面的CONTEXTUAL FILTERS,但这一般是用于关键词是完整参数的情况,另一种使用FILTER CRITERIA来设置,可以选择Global: Combine fields filter,在需要的字段组合中查找,一般采用Contains all words查询,这就保持了足够的灵活性,我们现在就是使用的这种办法。截图示范如下:

上面这个Views的访问URL就是这样的形式:https://www.example.com/search-views?keyword=关+键+词+Keyword
替换系统自带的搜索框需要自己做一个自定义搜索表单的Block,可以单个站设置,系列网站也可以在自定义模块中设置,示例代码如下:
function my_module_block_info() {
$blocks=array();
$blocks['search_form_for_web']=array(
'info'=> t('Search').' '.t('Form').' '.t('For').' '.t('WEB'),
'cache'=>DRUPAL_NO_CACHE,
'weight'=>0,
'status'=>1,
'region'=>'header',
'visibility'=>BLOCK_VISIBILITY_NOTLISTED,
'pages'=>'',
);
function my_module_block_view($delta = '') {
$block=array();
switch($delta){
case 'search_form_for_web':
$block['subject'] = '';
if(user_access('access content')){
$button = t('Search');
$placeholder = t('Keywords');
global $language_url;
global $base_url;
$prefix_url = $language_url->prefix;
$path = ($prefix_url == '') ? "" : "/$prefix_url";
$output = "<form action=\"$base_url$path/search_redirect_web\" method=\"get\">
<input type=\"text\" name=\"keyword\" placeholder=\"$placeholder\" class=\"search_form\">
<input type=\"submit\" value=\"$button\" class=\"search_form\">
</form>";
$block['content']=$output;
}
break;
default:
}
return $block;
}
可以看到这是获取用户填写的搜索关键词keyword后用get的方式提交到search_redirect_web这个重定向页面,之所以要有一个重定向页面是为了对keyword进行适当的转换处理后再提交到Views里去获取结果。
这个重定向页面在自定义模块中的定义示例如下:
function my_module_menu() {
$items= array();
$items['search_redirect_web'] = array (
'title'=>'search redirect web',
'page callback'=>'search_redirect_web',
'access arguments'=>array('access content'),
);
return $items;
}
function search_redirect_web() {
$request_uri = $_SERVER['REQUEST_URI'];
$query_string = $_SERVER['QUERY_STRING'];
$keyword = substr($query_string,strpos($query_string,'keyword=')+strlen('keyword='));
if ($keyword != NULL) {
$input = urldecode($keyword);
$output_encode = keyword_convert($input);
print "input = $input, output_encode = $output_encode, ";
global $language_url;
global $base_url;
$prefix_url = $language_url->prefix;
$path = ($prefix_url == '') ? "" : "/$prefix_url";
$http_referer = $_SERVER['HTTP_REFERER'];
if (strpos($http_referer,'?amp') != FALSE || strpos($http_referer,'&') != FALSE) {
$amp_mip = '&';
} elseif (strpos($http_referer,'?mip') != FALSE || strpos($http_referer,'&mip') != FALSE) {
$amp_mip = '&mip';
} else {
$amp_mip = '';
}
$url = "$base_url$path/search-views?keyword=$output_encode$amp_mip";
print "url = $url";
header("location: $url");
} else {
print "not redirect, request_uri = $request_uri, query_string = $query_string\n";
}
}
function keyword_convert($input) {
$input_length = mb_strlen($input);
$output = '';
$remain = $input;
for ($i=0; $i<$input_length; $i++) {
$sub = substr($remain,0,1);
$mb_sub = mb_substr($remain,0,1);
if ($sub == $mb_sub) {
$output .= $sub;
} else {
$output .= " $mb_sub ";
}
$remain = mb_substr($remain, 1);
print "i = $i, sub = $sub, mb_sub = $mb_sub, output = $output, remain = $remain<br />\n";
}
$output = str_replace(' ',' ',$output);
$output = trim($output);
$output_encode = urlencode($output);
$output_encode = str_replace('+++','+',$output_encode);
$output_encode = str_replace('++','+',$output_encode);
$output_encode = str_replace('++','+',$output_encode);
return $output_encode;
}
这里的keyword_convert转换函数就是对输入的关键词进行处理,然后编码成网址形式输出。因为需要考虑多语言、多站点路径、amp/mip版本的兼容,上面写得复杂了一点,如果不需要考虑这么多,还可以简化。
总结一下,就是需要三个东西:
- 一个Block用于接收用户输入关键词(简单的时候就用输入一个字段关键词,复杂也可以输入多个字段关键词);
- 一个重定向页面,用于处理用户输入的关键词(中文分词、判断直接跳转、限制长度、去掉无意义符号等);
- 一个Views,用于查询及返回结果(简单的时候采用字段过滤,复杂也可以采用多个字段过滤或者选择)。
Drupal自带的搜索只适合中小网站,像是一个黑盒,不需要很了解其原理,能够使用就行,简单易行。但对于数据量大、用户要求精准的情况,还是采用Views的方式合适,可以定制搜索的结果范围,合理限制搜索负载,快速给出结果,只是实现起来复杂一些。
评论