• Elasticsearch深入8


    搜索推荐,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 eleaseselasticsearch-analysis-ik-5.2.0.zip

     4、解压缩ik压缩包

     将mysql驱动jar,放入ik的目录下

     5、修改jdbc相关配置

     6、重启es

     观察日志,日志中就会显示我们打印的那些东西,比如加载了什么配置,加载了什么词语,什么停用词

     7、在mysql中添加词库与停用词

     8、分词实验,验证热更新生效

    效果展示

    添加数据库扩展词

     成功加载至es中,打印相关日志

     

     再次运行相关查询

     

  • 相关阅读:
    NodeMCU快速上云集锦
    云数据库 MySQL 8.0 重磅发布,更适合企业使用场景的RDS数据库
    MySQL 8.0 技术详解
    为更强大而生的开源关系型数据库来了!阿里云RDS for MySQL 8.0 正式上线!
    阿里云CDN技术掌舵人文景:相爱相杀一路狂奔的这十年
    容器服务kubernetes federation v2实践五:多集群流量调度
    Helm V3 新版本发布
    Serverless助力AI计算:阿里云ACK Serverless/ECI发布GPU容器实例
    详解TableStore模糊查询——以订单场景为例
    洛谷P2727 01串 Stringsobits
  • 原文地址:https://www.cnblogs.com/jiahaoJAVA/p/11048382.html
Copyright © 2020-2023  润新知