前言
在使用mysql的时候,为了查询速度,我们都会使用索引这个东西(如果不知道什么叫索引,可以百度看看)
现在问题来了,索引对 like "%xx%" 是不生效的,这就意味着无法快速的模糊匹配查询数据,那么有什么办法解决这个问题吗?
倒排索引
例如有一组数据:
\[ { "name": "仙士可", "keyword": "关键字123" }, { "name": "php程序员仙士可", "keyword": "个人介绍xxx" } \]
如果需要查询 name 里面包含仙士可的,一般做法是直接遍历所有数据,一个个查,那么有什么快速的方法吗?
我们或许可以这样实现:
将name里面的所有文字拆分,每个文字都对应数组的索引
仙=>0 士=>0 可=>0 p=>1 h=>1 p=>1 程=>1 序=>1 员=>1 仙=>1 士=>1 可=>1
去重一下:
仙=>0 士=>0 可=>0 p=>1 h=>1 程=>1 序=>1 员=>1 仙=>1 士=>1 可=>1
合并一下:
仙=>\[0,1\] 士=>\[0,1\] 可=>\[0,1\] p=>\[1\] h=>\[1\] 程=>\[1\] 序=>\[1\] 员=>\[1\]
当我们需要查询仙士可时,首先查询 仙 对应的数组索引,然后查询 士,可,最后根据属性存储的数组索引做交集,交集出的数组索引则是查询出来的数据.
记录所有属性值对应的数据内容位置,根据属性值去找对应数据内容,通过属性值去确定一个数据的内容,这个索引方式就称为倒排索引
分词
这个时候获取大家就发现了一个新的问题:当一个属性值存在几千个字符时,是不是代表着会存储几千个索引内容呢?这样岂不是非常耗费空间?
答案是:确实会,为了一定能够搜索到这个值,确实会存储几千个,但是实际上,文章内容并没有这么多个不同的字,中国常用字也就几千个,不管多少文章,其实常用汉字就几千个,并不会过于增加空间
这样的话,又可能会产生新的问题: 本身中文常用汉字只有几千个,那如果有几千万篇文章,岂不是意味着每一个字都会对应到几百万甚至上千万的文章id吗?这样岂不是跟没有索引一样?
这就涉及到了新的一个东西, 分词.
分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。我们知道,在英文的行文中,单词之间是以空格作为自然分界符的,而中文只是字、句和段能通过明显的分界符来简单划界,唯独词没有一个形式上的分界符,虽然英文也同样存在短语的划分问题,不过在词这一层上,中文比之英文要复杂得多、困难得多。
分词分为几种类型:
词性编码
词性名称
注 解
Ag
形语素
形容词性语素。形容词代码为 a,语素代码g前面置以A。
a
形容词
取英语形容词 adjective的第1个字母。
ad
副形词
直接作状语的形容词。形容词代码 a和副词代码d并在一起。
an
名形词
具有名词功能的形容词。形容词代码 a和名词代码n并在一起。
b
区别词
取汉字“别”的声母。
c
连词
取英语连词 conjunction的第1个字母。
dg
副语素
副词性语素。副词代码为 d,语素代码g前面置以D。
d
副词
取 adverb的第2个字母,因其第1个字母已用于形容词。
e
叹词
取英语叹词 exclamation的第1个字母。
f
方位词
取汉字“方”
g
语素
绝大多数语素都能作为合成词的“词根”,取汉字“根”的声母。
h
前接成分
取英语 head的第1个字母。
i
成语
取英语成语 idiom的第1个字母。
j
简称略语
取汉字“简”的声母。
k
后接成分
l
习用语
习用语尚未成为成语,有点“临时性”,取“临”的声母。
m
数词
取英语 numeral的第3个字母,n,u已有他用。
Ng
名语素
名词性语素。名词代码为 n,语素代码g前面置以N。
n
名词
取英语名词 noun的第1个字母。
nr
人名
名词代码 n和“人(ren)”的声母并在一起。
ns
地名
名词代码 n和处所词代码s并在一起。
nt
机构团体
“团”的声母为 t,名词代码n和t并在一起。
nz
其他专名
“专”的声母的第 1个字母为z,名词代码n和z并在一起。
o
拟声词
取英语拟声词 onomatopoeia的第1个字母。
p
介词
取英语介词 prepositional的第1个字母。
q
量词
取英语 quantity的第1个字母。
r
代词
取英语代词 pronoun的第2个字母,因p已用于介词。
s
处所词
取英语 space的第1个字母。
tg
时语素
时间词性语素。时间词代码为 t,在语素的代码g前面置以T。
t
时间词
取英语 time的第1个字母。
u
助词
取英语助词 auxiliary
vg
动语素
动词性语素。动词代码为 v。在语素的代码g前面置以V。
v
动词
取英语动词 verb的第一个字母。
vd
副动词
直接作状语的动词。动词和副词的代码并在一起。
vn
名动词
指具有名词功能的动词。动词和名词的代码并在一起。
w
标点符号
x
非语素字
非语素字只是一个符号,字母 x通常用于代表未知数、符号。
y
语气词
取汉字“语”的声母。
z
状态词
取汉字“状”的声母的前一个字母。
un
未知词
不可识别词及用户自定义词组。取英文Unkonwn首两个字母。(非北大标准,CSW分词中定义)
识别文章中的所有字符,通过预设定/或者ai自动识别的方案,自动将一句话/一篇文章内的字符串拆分为一个个的词语:
php是世界上最好的语言.
拆分为:
php 是 世界上 最好的 语言
中文分词方案有很多,例如 scws,THULAC,结巴分词,等等 中文分词方案.
lucene
在上面,我们了解到了倒排索引,了解了中文分词,那么lucene就是现成的使用倒排索引实现的全文搜索引擎.
lucene还附带了中文分词等解决方案.
lucene 的大致存储结构为:
lucene field的类型:
为了使得lucene更好的标识存储,field的类型大致分为以下几种:
1:完全拆分字符实现索引
2:分词实现索引
3:只存储文档数据,不建立索引
4:只建立索引,数据不存储在文档
索引和查询流程为:
- 文档标明id存储
- 文档解析field
- 文档分析field,分词
- 建立索引,索引进入索引库
- 用户通过查询接口
- 创建查询语言
- lucene 执行查询,通过索引库获得数据
- 渲染数据
- 响应到用户端
elasticsearch
在原来的lucene 全文搜索引擎中,它仅仅是一个全文搜索架构,提供了完整的查询引擎架构,如果需要使用它,那就意味着要自己实现各种存储,查询的调用.
elasticsearch 进一步封装了lucene,封装 restful api操作存储对象.并且实现了分布式存储架构
中文文档:https://www.elastic.co/guide/cn/elasticsearch/guide/current/intro.html
安装与运行
docker 安装:
docker pull elasticsearch sudo docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \ -e "discovery.type=single-node" \ -e "cluster.name=elasticsearch" \ -v ~/docker/volumn/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ -v ~/docker/volumn/elasticsearch/data:/usr/share/elasticsearch/data \ -d elasticsearch
访问:http://127.0.0.1:9200/ 出现数据则表示运行成功
使用
elasticsearch 使用restful api 请求规范操作,意味着你可以使用postman,curl,等等http客户端进行操作.
例如使用curl:
新增一条数据:
tioncico@acbc32866c55 ~ % curl -X PUT --header 'Content-Type: application/json' http://127.0.0.1:9200/databasename/tablename/1 -d \ '{"name":"仙士可","age":"24"}' {"\_index":"databasename","\_type":"tablename","\_id":"1","\_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"created":true}%
获取一条数据:
tioncico@acbc32866c55 ~ % curl -X GET --header 'Content-Type: application/json' http://127.0.0.1:9200/databasename/tablename/1 {"\_index":"databasename","\_type":"tablename","\_id":"1","\_version":1,"found":true,"_source": {"name":"仙士可","age":"24"}}% tioncico@acbc32866c55 ~ %
其他高级搜索操作可查看官方文档