希望长大对我而言,是可以做更多想做的事,而不是被迫做更多不想做的事...... 首页 Elasticsearch搜索特性 丁D 学无止境 2019-09-29 09:15 51392已阅读 前缀匹配 搜索推荐 近似/短语匹配 摘要本次将学习正则匹配,前缀匹配、通配符匹配、正则匹配、近似/短语匹配、搜索推荐、模糊搜索。 [TOC] ### es实现mysql的like 方案一、可以是用wildcard通配符,但是要设置不分词,这种方案性能不好 方案二、可以使用ngram分词器 "min_gram": 2,"max_gram": 3 单词假设是 abcde0001 ab bc cd abc cde 001.。。。等等 被分词2个字母一组和3个字符一组。。、。 注意空格 如a bcde0001 https://www.cnblogs.com/rainersha/p/11982846.html ###前缀匹配 ```js 假设es中有3条这样的数据,前缀匹配(不分词) C3D0-KD345 C3K5-DFG65 C4I8-UI365 PUT my_index { "mappings": { "my_type": { "properties": { "title": { "type": "keyword" } } } } } 需求:我们要通过"C3"来查找 1.使用match是搜索不到的 2.使用"prefix" GET my_index/my_type/_search { "query": { "prefix": { "title": { "value": "C3" } } } } 前缀匹配性能很差。 因为前缀匹配,要扫描所有的倒排索引,假设“C3D0-KD345” 这条数据,并不能停止,因为不知道后面还有没有"C3"打头的。 match不一样(分词) C3-D0-KD345 doc1 C3-K5-DFG65 doc2 C4-I8-UI365 doc3 C3 doc1 doc2 D0 KD345 K5 ..... match扫描到了"C3"就可以停止了,所以match的性能是很高的。。 ``` ###通配符 ```js 通配符 是不分词的所以要设置成keyword GET my_index/my_type/_search { "query": { "wildcard": { "title": { "value": "C?K*5" } } } } ?:任意字符 *:0个或任意多个字符 性能一样差,必须扫描整个倒排索引,才ok ``` ###正则 ```js 一样不分词 GET /my_index/my_type/_search { "query": { "regexp": { "title": "C[0-9].+" } } } C[0-9].+ [0-9]:指定范围内的数字 [a-z]:指定范围内的字母 .:一个字符 +:前面的正则表达式可以出现一次或多次 wildcard和regexp,与prefix原理一致,都会扫描整个索引,性能很差 ``` ###近似/短语匹配 ```js java is my favourite programming language, and I also think spark is a very good big data system. java spark are very related, because scala is spark's programming language and scala is also based on jvm like java. 假设es有上面两句话(会分词) 1.我们需要查询“java spark”这个短语,两个单词会连在一起 2.我们需要查询“java spark”,这个短语,不一定子要连在一起,但是靠近越近,分数越高 需求1 match 1.分词“java”和“spark” 2.使用“java”去扫描倒排索引找出对应得doc返回 3.使用“spark”去扫描倒排索引找出对应得doc返回 所以match只能返回含有“java”或“spark”或两者都有的doc term 1.不分词 “java spark”去扫描倒排索引找到对应得doc返回 当然至于doc字段分词和不分词看有没有设置成keyword。 所以上面match和term都没办法实现我们的需求 这个个时候我们要使用match_phrase GET /forum/article/_search { "query": { "match_phrase": { "content": "java spark" } } } match_phrase:原理 1.将"java spark"分词成"java"和"spark" 2.使用"java"扫描倒排索引得到doc, 3.使用"spark"扫描倒排索引得到doc, hello world, java spark doc1 hi, spark java doc2 hello doc1(0) wolrd doc1(1) java doc1(2) doc2(2) spark doc1(3) doc2(1) 得到 java doc1(2) doc2(2) spark doc1(3) doc2(1) 4.比较位置spark的位置要比大1 所以只有doc1 需求2 GET /forum/article/_search { "query": { "match_phrase": { "title": { "query": "java spark", "slop": 1 } } } } “slop”的含义 query string,搜索文本,中的几个term,要经过几次移动才能与一个document匹配,这个移动的次数,就是slop 例子: hello world, java is very good, spark is also very good. “java spark”,match phrase,搜不到 java is very good spark is java spark java --> spark java --> spark java --> spark 所以我们将“slop”设置成3就行,意思是移动3步 假设我们现在要搜索 spark java呢 java is very good spark is spark java spark/java(java向左移动,重叠1步) java spark(交换位置2步) java --> spark java --> spark java --> spark 所以我们将“slop”设置成5就行 ``` ###搜索推荐 ####match_phrase_prefix ```js 假设es中有 hello world hello we hello win hello wind hello dog hello cat 将我们使用"hello w"去搜索希望能查出hello world hello we hello win hello wind 我们可以使用“match_phrase_prefix” GET /my_index/my_type/_search { "query": { "match_phrase_prefix": { "title": "hello w" } } } 原理 原理跟match_phrase类似,只是最后一个词语用前缀匹配 1.将“hello w”分词成“hello”和“w” 2.“hello”使用match去搜索对应得doc 3.“w”使用前缀匹配去扫描倒排索引中所有的数据(性能很差) 4.计算slop,“w”要刚好比“hello”的位置大1,当然也可以自己设置“slop” 这个方式性能很差,可以使用ngram来实现搜索推荐 ``` ####ngram ```js 什么是ngram quick,5种长度下的ngram ngram length=1,q u i c k ngram length=2,qu ui ic ck ngram length=3,qui uic ick ngram length=4,quic uick ngram length=5,quick 什么是edge ngram quick,anchor首字母后进行ngram q qu qui quic quick 使用edge ngram将每个单词都进行进一步的分词切分,用切分后的ngram来实现前缀搜索推荐功能 helloworld min ngram = 1 max ngram = 3 h he hel 搜索的时候,不用再根据一个前缀,然后扫描整个倒排索引了; 简单的拿前缀去倒排索引中匹配即可,如果匹配上了,那么就好了; match,全文检索 1.给index创建一个分词器 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": "quick brown" } 2.给index创建mapping PUT /my_index/_mapping/my_type { "properties": { "title": { "type": "string", "analyzer": "autocomplete", "search_analyzer": "standard" } } } 3. GET /my_index/my_type/_search { "query": { "match_phrase": { "title": "hello w" } } } 如果用match,只有hello的也会出来,全文检索,只是分数比较低 推荐使用match_phrase,要求每个词语都有,而且position刚好靠着1位,符合我们的期望的 ``` ####completion ```js es实现completion,不会构建倒排索引页不会构建正排索引,就是纯的用于进行前缀 搜索的一种特殊的数据结构,而且会全部放在内存中,所以auto completion进行的 前缀搜索提示,性能是非常高的 PUT /news_website { "mappings": { "news" : { "properties" : { "title" : { "type": "text", "analyzer": "ik_max_word", "fields": { "suggest" : { "type" : "completion", "analyzer": "ik_max_word" } } }, "content": { "type": "text", "analyzer": "ik_max_word" } } } } } PUT /news_website/news/1 { "title": "大话西游电影", "content": "大话西游的电影时隔20年即将在2017年4月重映" } PUT /news_website/news/2 { "title": "大话西游小说", "content": "某知名网络小说作家已经完成了大话西游同名小说的出版" } PUT /news_website/news/3 { "title": "大话西游手游", "content": "网易游戏近日出品了大话西游经典IP的手游,正在火爆内测中" } GET /news_website/news/_search { "suggest": { "my-suggest" : { "prefix" : "大话西游", "completion" : { "field" : "title.suggest" } } } } 注意这里讲“大话西游”设置成一个热词,会被当成一个分词组。 ``` ###模糊搜索 ```js GET /my_index/my_type/_search { "query": { "fuzzy": { "text": { "value": "surprize", "fuzziness": 2 } } } } surprize --> surprise -> z -> s,纠正一个字母,就可以匹配上,所以在fuziness指定的2范围内 surprize --> surprised -> z -> s,末尾加个d,纠正了2次,也可以匹配上,在fuziness指定的2范围内 surprize --> surprising -> z -> s,去掉e,ing,3次,总共要5次,才可以匹配上,始终纠正不了 fuzziness:不写默认是2 ``` 很赞哦! (1) 上一篇:ElasticSearch之TF/IDF 下一篇:Elasticsearch之nested object 目录 点击排行 Elasticsearch6.3.2之x-pack redis哨兵 2019-07-09 22:05 Redis+Twemproxy+HAProxy+Keepalived 2019-07-12 17:20 GC优化策略和相关实践案例 2019-10-10 10:54 JVM垃圾回收器 2019-10-10 10:23 标签云 Java Spring MVC Mybatis Ansible Elasticsearch Redis Hive Docker Kubernetes RocketMQ Jenkins Nginx 友情链接 郑晓博客 佛布朗斯基 凉风有信 MarkHoo's Blog 冰洛博客 南实博客 Rui | 丁D Java研发工程师 生活可以用「没办法」三个字概括。但别人的没办法是「腿长,没办法」、「长得好看,没办法」、「有才华,没办法」。而你的没办法,是真的没办法。 请作者喝咖啡