搜索推荐,search as you type
百度 --> elas --> elasticsearch --> elasticsearch权威指南
GET /my_index/my_type/_search
{
"query": {
"match_phrase_prefix": {
"title": "hello d"
}
}
}
原理跟match_phrase类似,唯一的区别,就是把最后一个term作为前缀去搜索
hello就是去进行match,搜索对应的doc
w,会作为前缀,去扫描整个倒排索引,找到所有w开头的doc
然后找到所有doc中,即包含hello,又包含w开头的字符的doc
根据你的slop去计算,看在slop范围内,能不能让hello w,正好跟doc中的hello和w开头的单词的position相匹配
也可以指定slop,但是只有最后一个term会作为前缀
max_expansions:指定prefix最多匹配多少个term,超过这个数量就不继续匹配了,限定性能
默认情况下,前缀要扫描所有的倒排索引中的term,去查找w打头的单词,但是这样性能太差。可以用max_expansions限定,w前缀最多匹配多少个term,就不再继续搜索倒排索引了。
尽量不要用,因为,最后一个前缀始终要去扫描大量的索引,性能可能会很差
通过ngram分词机制实现index-time搜索推荐
什么是ngram
quick,5种长度下的ngram
在将新的doc放入es中的的时候已经将词做了处理了
hello world
h
he
hel
hell
hello 会将filed安装这种才分方式
hello we
w
wo
wor
worl
world
min ngram = 1
设置最大(就是分3层或者3级)max ngram = 3
h
he
hel
搜索的时候,不用再根据一个前缀,然后扫描整个倒排索引了; 简单的拿前缀去倒排索引中匹配即可,如果匹配上了,那么就好了; match,全文检索
PUT /my_index
{
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
}
}
测试分词规则
GET /my_index/_analyze
{
"analyzer": "autocomplete",
"text": "hello world"
}就可以看见效果了 没有贴图
PUT /my_index/_mapping/my_type
{
"properties": {
"title": {
"type":"string",
"analyzer": "autocomplete",
"search_analyzer": "standard"
}
}
}//添加分词规则
GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": "hello w"
}
}
}推荐效果就OK了
如果用match,只有hello的也会出来,全文检索,只是分数比较低
推荐使用match_phrase,要求每个term都有,而且position刚好靠着1位,符合我们的期望
容忍包含不相关的内容但是采取降低分数
搜索包含java,不包含spark的doc,但是这样子很死板
搜索包含java,尽量不包含spark的doc,如果包含了spark,不会说排除掉这个doc,而是说将这个doc的分数降低
包含了negative term的doc,分数乘以negative boost,分数降低
GET /forum/article/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"content": "java"
}
},
"negative": {
"match": {
"content": "spark"
}
},
"negative_boost": 0.2
}
}
}
如果你压根儿不需要相关度评分,直接走constant_score加filter,所有的doc分数都是1,没有评分的概念了
GET /forum/article/_search
{
"query": {
"bool": {
"should": [
{
"constant_score": {
"query": {
"match": {
"title": "java"
}
}
}
},
{
"constant_score": {
"query": {
"match": {
"title": "spark"
}
}
}
}
]
}
}
}
IK中文分词器的安装和使用
地址 : https://github.com/medcl/elasticsearch-analysis-ik/tags 选择合适的版本
1、ik配置文件
ik配置文件地址:es/plugins/ik/config目录
IKAnalyzer.cfg.xml:用来配置自定义词库
main.dic:ik原生内置的中文词库,总共有27万多条,只要是这些单词,都会被分在一起
quantifier.dic:放了一些单位相关的词
suffix.dic:放了一些后缀
surname.dic:中国的姓氏
stopword.dic:英文停用词
ik原生最重要的两个配置文件
main.dic:包含了原生的中文词语,会按照这个里面的词语去分词
stopword.dic:包含了英文的停用词
停用词,stopword
a the and at but
一般,像停用词,会在分词的时候,直接被干掉,不会建立在倒排索引中
2、自定义词库
(1)自己建立词库:每年都会涌现一些特殊的流行词,网红,蓝瘦香菇,喊麦,鬼畜,一般不会在ik的原生词典里
自己补充自己的最新的词语,到ik的词库里面去
IKAnalyzer.cfg.xml:ext_dict,custom/mydict.dic
补充自己的词语,然后需要重启es,才能生效
(2)自己建立停用词库:比如了,的,啥,么,我们可能并不想去建立索引,让人家搜索
custom/ext_stopword.dic,已经有了常用的中文停用词,可以补充自己的停用词,然后重启es
★修改IK分词器源码来基于mysql热更新词库
热更新
每次都是在es的扩展词典中,手动添加新词语,很坑
(1)每次添加完,都要重启es才能生效,非常麻烦
(2)es是分布式的,可能有数百个节点,你不能每次都一个一个节点上面去修改
es不停机,直接我们在外部某个地方添加新的词语,es中立即热加载到这些新词语
热更新的方案
(1)修改ik分词器源码,然后手动支持从mysql中每隔一定时间,自动加载新的词库
(2)基于ik分词器原生支持的热更新方案,部署一个web服务器,提供一个http接口,通过modified和tag两个http响应头,来提供词语的热更新
用第一种方案,第二种,ik git社区官方都不建议采用,觉得不太稳定
1、下载源码
https://github.com/medcl/elasticsearch-analysis-ik/tree/v5.2.0
ik分词器,是个标准的java maven工程,直接导入eclipse就可以看到源码
2、修改源码
Dictionary类,169行:Dictionary单例类的初始化方法,在这里需要创建一个我们自定义的线程,并且启动它
HotDictReloadThread类:就是死循环,不断调用Dictionary.getSingleton().reLoadMainDict(),去重新加载词典
Dictionary类,389行:this.loadMySQLExtDict();
Dictionary类,683行:this.loadMySQLStopwordDict();
3、mvn package打包代码
target\releases\elasticsearch-analysis-ik-5.2.0.zip
4、解压缩ik压缩包
将mysql驱动jar,放入ik的目录下
5、修改jdbc相关配置
6、重启es
观察日志,日志中就会显示我们打印的那些东西,比如加载了什么配置,加载了什么词语,什么停用词
7、在mysql中添加词库与停用词
8、分词实验,验证热更新生效
效果展示
添加数据库扩展词
成功加载至es中,打印相关日志
再次运行相关查询
原文地址:https://www.cnblogs.com/jiahaoJAVA/p/11048382.html