前言
在学习Elasticsearch的使用前,我们先来了解下es是如何实现全文搜索的。
倒排索引是 Elasticsearch 中非常 重要的索引结构,从 文档单词到文档 ID 的过程
为什么要使用倒排索引
先看下面的商品数据goods
id | 标题 | 描述 |
1 | 小米手机 | 小米手机性价比贼高,为发烧而生 |
2 | 苹果手机 | 高端手机,生态丰富 |
3 | 三只松鼠零食大礼包 | 便宜实惠,高端品牌质量有保证 |
4 | 小米电脑 | 小米电脑性价比贼高,便宜好用 |
如果我们要模糊查含有手机关键词的商品,以mysql查询为例,应该是下面的语句
select * from goods where 标题 like '%手机%' or 描述 like '%手机%'
了解mysql的都知道,用上面的语句查询,索引会失效导致全表查询,如果数据量大的话就会很慢很慢。
怎么解决呢?用倒排索引
倒排索引原理
倒排索引主要包含两个过程:创建倒排索引、倒排索引搜索
创建倒排索引
先对 文档的内容进行分词,形成一个个的 token,也就是 单词,然后保存这些 token 与文档的对应关系。
如上面的商品数据goods,保存后如下所示
token | 对应文档 |
小米 | 1,4 |
手机 | 1,2 |
苹果 | 2 |
电脑 | 4 |
三只松鼠 | 3 |
零食 | 3 |
大礼包 | 3 |
便宜 | 3,4 |
性价比 | 1,4 |
... | ... |
倒排索引搜索
对搜索词先分词,得到多个Token,然后去倒排索引中进行匹配
如搜索词:性价比手机
分词后:性价比、手机
性价比匹配到的文档是1、4;手机匹配到的文档是1、2
最终搜索到的文档就是1
1 | 小米手机 | 小米手机性价比贼高,为发烧而生 |
实践
简单实现下倒排索引的搜索引擎,中文分词比较麻烦,这里先使用英文,默认英文分词都是空格。
搜索引擎类
class SearchEngines { // 搜索数据 private $searchData; // 搜索 public function search($keyword) { // 转小写 $keyword = strtolower($keyword); // 对搜索词进行分词 $keywords = explode(' ', $keyword); $ids = []; foreach ($keywords as $keyword) { // 查找搜索词对应文档 $tmpIds = []; foreach ($this->searchData[$keyword] as $id) { $tmpIds[] = $id; } // 第一个不取交集 if (!$ids) { $ids = $tmpIds; continue; } // 取搜索词对应文档的交集 $ids = array_intersect($ids, $tmpIds); } return $ids; } // 插入数据 public function save($id, $keyword) { // 对关键词进行分词 $keywords = explode(' ', $keyword); foreach ($keywords as $keyword){ // 都改为小写 $keyword = strtolower($keyword); if (!isset($this->searchData[$keyword])) { $this->searchData[$keyword] = []; } if (!in_array($id, $this->searchData[$keyword])) { $this->searchData[$keyword][] = $id; } } } }
数据插入
$insertData = [ 1 => [ 'id' => 1, 'title' => 'Xiaomi phones', 'desc' => 'Xiaomi\'s mobile phone is more cost-effective than thieves, and is born of fever' ], 2 => [ 'id' => 2, 'title' => 'iPhone', 'desc' => 'High end mobile phones with rich ecology' ], 3 => [ 'id' => 3, 'title' => 'Three Squirrels Snack Pack', 'desc' => 'food' ], 4 => [ 'id' => 4, 'title' => 'Xiaomi Computer', 'desc' => 'Xiaomi computers are more cost-effective than thieves, and cheap and easy to use' ] ]; $searchEngines = new SearchEngines(); foreach ($insertData as $data) { $searchEngines->save($data['id'], $data['title']." ".$data['desc']); }
关键词搜索
$ids = $searchEngines->search('cost-effective phone'); foreach ($ids as $id) { echo $insertData[$id]['title']; }
执行结果