需求
- 能够在键盘输入的瞬间响应搜索结果;
- 不需要太复杂的查询,单个字段作为搜索条件;
- 分词、模糊匹配;
- 实时更新;
一、安装Redis
wget http://dl.fedoraproject.org/pub/epel/7/x86_64/r/redis-3.2.3-1.el7.x86_64.rpm
rpm -ivh redis-3.2.3-1.el7.x86_64.rpm
systemctl start redis
出现 libjemalloc.so.1()(64bit) 被 redis-3.2.3-1.el7.x86_64 需要, 默认用jemalloc管理内存
wget http://dl.fedoraproject.org/pub/epel/7/x86_64/j/jemalloc-3.6.0-1.el7.x86_64.rpm
rpm -ivh jemalloc-3.6.0-1.el7.x86_64.rpm
外网访问
vim /etc/redis.conf # 取消 bind 127.0.0.1 修改 protected-mode no
二、PHP程序实现(基于laravel5.1)
1. 基础表数据
CREATE TABLE `article` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
`score` int(10) unsigned DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
2. PHP 分词程序 SCWS
wget http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2
tar xvjf scws-1.2.3.tar.bz2 # 如果缺少bzip2 请 yum install bzip2
cd scws-1.2.3
./configure --prefix=/usr/local/scws
make
make install
# 下载字典
cd /usr/local/scws/etc
wget http://www.xunsearch.com/scws/down/scws-dict-chs-gbk.tar.bz2
wget http://www.xunsearch.com/scws/down/scws-dict-chs-utf8.tar.bz2
tar xvjf scws-dict-chs-gbk.tar.bz2
tar xvjf scws-dict-chs-utf8.tar.bz2
# 安装PHP扩展
cd scws-1.2.3 # 下载目录打开
phpize
./configure --with-scws=/usr/local/scws # 若 PHP 安装在特殊目录, 则请在 configure 后加上 --with-php-config=$php_prefix/bin/php-config
make
make install
# php.ini 配置
[scws]
extension = scws.so
scws.default.charset = utf8
scws.default.fpath = /usr/local/scws/etc
# 注意请检查 php.ini 中的 extension_dir 的设定值是否正确, 否则请将 extension_dir 设为空,
# 再把 extension = scws.so 指定绝对路径。
3. laravel 安装 predis 包
composer require "predis/predis:1.0.*"
4. 基础 models
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $table = 'article';
public $timestamps = false;
}
5. 数据添加时建立索引
use App\Models\Article;
$server = [
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
];
$redisClient = new \Predis\Client($server);
Article::created(function ($article) use ($redisClient) {
$so = scws_new();
$so->send_text($article->title);
while ($tmp = $so->get_result()) {
foreach ($tmp as $value) {
$redisClient->sadd('search:key:'.$value['word'], $article->id);
$redisClient->set('search:score:'.$article->id, $article->score);
$redisClient->hmset('search:data', [$article->id => $article->title]);
}
}
$so->close();
});
6. 数据删除时删除索引
Article::deleted(function ($article) use ($redisClient) {
$so = scws_new();
$so->send_text($article->title);
while ($tmp = $so->get_result()) {
foreach ($tmp as $value) {
$redisClient->srem('search:key:'.$value['word'], $article->id);
$redisClient->del('search:score:'.$article->id);
$redisClient->hdel('search:data', $article->id);
}
}
$so->close();
});
7. 数据修改时自己改吧!!!
8. 数据搜索实现
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function index(Request $request)
{
$server = [
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
];
$redisClient = new \Predis\Client($server);
$key = 'search:key:';
$title = $request->input('title');
$so = scws_new();
$so->send_text($title);
while ($tmp = $so->get_result()) {
foreach ($tmp as $value) {
$aTitle[] = $key . $value['word'];
$aTitleVal[] = $value['word'];
}
}
$so->close();
if (isset($aTitle)) {
$mergeKey = 'search:key:' . implode('+', $aTitleVal);
$redisClient->sinterstore($mergeKey, $aTitle);
$interstore = $redisClient->sort($mergeKey, ['BY' => 'search:score:*', 'sort' => 'desc']);
$result = $redisClient->hmget('search:data', $interstore);
print_r($result);
}
}
}