Loading... # Elasticsearch(八) ## 模糊搜索 ### 前缀搜索 匹配以搜索关键词开头的索引,不计算相关度评分,和filter比,没有bitcache。前缀搜索性能差,应该尽量把前缀长度设置的更长。 ``` PUT my_index { "mappings": { "properties": { "text": { "type": "text", "analyzer": "ik_max_word", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } }, "index_prefixes": { "min_chars": 1, "max_chars": 10 } } } } } ``` - min_chars: 索引的最小前缀长度。必须大于0,默认为2。 - max_chars: 索引的最大前缀长度。必须小于20,默认为5。 示例: ``` GET my_index/_search { "query": { "prefix": { "text": { "value": "xx" } } } } ``` ### 通配符搜索 用通配符来匹配一个或多个字符的占位符。例如:*通配符匹配零个或多个字符,?通配符表示一个字符占位符。 ``` GET my_index/_search { "query": { "wildcard": { "text": { "value": "Engl?sh" } } } } ``` ### 正则搜索 regexp查询的性能根据提供的正则表达式而有所不同。为了提高性能,应避免使用通配符模式,如\.\*或\.\*?+未经前缀或后缀。 ``` GET my_index/_search { "query": { "regexp": { "name": { "value": "[\\s\\S]*nfc[\\s\\S]*", "flags": "ALL", "max_determinized_states": 10000, "rewrite": "constant_score" } } } } ``` - flags: (可选,字符串)为正则表达式启用可选运算符。 - ALL: 默认,启用所有可选操作符。 - COMPLEMENT: 启用~操作符,可以使用~对后面的最短模式进行否定。例如a~bc.表示匹配adc,aec等,就是不能匹配abc。 - INTERVAL: 启用<>操作符。可以使用<>匹配数值范围。例如:foo<1-100>,foo<01-100>。 - INTERSECTION: 启用&操作符,它充当AND操作符。如果左边和右边的模式都匹配,则匹配成功。例如: aaa.+&.+bbb - ANYSTRING: 启用@操作符,可以使用@来匹配任何整个字符串。可以将@操作符与&操作符组合起来,创建一个everything except逻辑。例如: @&~(abc.+),表示匹配除了以abc开头的任何词项。 - max_determinized_states: (可选,整数)查询所需的最大自动机状态数。用以限制复杂的正则表达式消耗过多的资源。 - rewrite: (可选,字符串)用于重写查询方法。 ### Fuzzy模糊查询 用以查询混淆字符(box->fox),缺少字符(black->lack),多出字符(sic->sick),颠倒次序(act->cat)。 #### 语法 ``` GET my_index/_search { "query": { "fuzzy": { "text": { "value": "见义勇位", "fuzziness": 2 } } } } ``` 结果: ``` { "took" : 2, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.0102019, "hits" : [ { "_index" : "my_index", "_type" : "_doc", "_id" : "5", "_score" : 1.0102019, "_source" : { "text" : "黑人见义勇为阻止抢劫反被铐住" } } ] } } ``` #### 参数 - value: (必需,字符串)要搜索的关键词。 - fuzziness: (可选,字符串)最大误差,并非越大越好,越大召回率越高,但结果越不准确。误差也就是距离,两段文本之间的Damerau-Levenshtein距离是使一个字符串与另一个字符串匹配所需插入、删除、替换和调换的数量。距离公式luence使用的是Levenshtein,ES改进为Damerau-Levenshtein,比如: axe与aex,用Levenshtein计算距离是2,用Damerau-Levenshtein计算距离是1。 - max_expansions: (可选,整数)匹配的最大词项数量,默认50。最大词项数量也就是索引数量,并不是返回结果的数量,因为一个索引可能对应多个doc,而且ES有多个分片,有可能每个分片都会匹配到一定数量的索引。 - prefix_length: (可选,整数)创建扩展时保留不变的开始的字符数,默认为0。避免在max_expansions中使用较高的值,尤其是在prefix_length参数值为0时。max_expansions由于检查的词项数量过多,导致性能不佳。 - transpositions: (可选,布尔值)指示编辑是否包括两个相邻字符的变位(ab->ba),默认为true。 - rewrite: (可选,字符串)用于重写查询的方法。 也可以使用match来实现模糊查询。 语法: ``` GET my_index/_search { "query": { "match": { "text": { "query": "见义勇位 夫妻", "fuzziness": "AUTO", "operator": "OR" } } } } ``` 结果: ``` { "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.3469357, "hits" : [ { "_index" : "my_index", "_type" : "_doc", "_id" : "4", "_score" : 1.3469357, "_source" : { "text" : "夫妻结婚30多年AA制,被城管抓" } }, { "_index" : "my_index", "_type" : "_doc", "_id" : "5", "_score" : 1.3469357, "_source" : { "text" : "黑人见义勇为阻止抢劫反被铐住" } } ] } } ``` operator参数表示所有词项之间的关系,and表示与,or表示或。默认or。 ### match_phrase_prefix: 最简陋的Suggest match_phrase_prefix与match_phrase相同,但是它多了一个特性,就是它允许最后一个词项(term)上的前缀匹配。如果是一个单词,比如a,它会匹配所有以a开头的文档,如果是一个短语,比如"this is ma",它会现在倒排索引中以ma做前缀搜索,然后在匹配到的doc中做match_phrase查询(网上有说是先match_phrase,然后再进行前缀搜索是不对的)。 #### 参数 - analyzer: 指定使用何种分词器来对该短语进行分词处理。 - max_expansions: 限制匹配的最大词项,同Fuzzy。 - boost: 用于设置该查询的权重。 - slop: 允许短语间的词项的间隔,默认0。 slop参数告诉match_phrase查询词条相隔多远时,仍然能将文档视为匹配。也就是为了让查询和文档匹配需要移动词条多少次。 ``` GET my_index/_search { "query": { "match_phrase_prefix": { "text": { "query": "夫妻", "max_expansions": 1 } } } } ``` **无论是前缀搜索,通配符搜索,正则搜索还是fuzzy,它们所匹配的都是倒排索引,也就是分词结果,并非真正文本中的值,比如: doc中某个字段值是"I am a Chinese."默认会按照空格分词,我们用前缀搜索搜"Chi"也是可以匹配到结果的,因为它匹配到了分词"Chinese",而Chinese的索引对应了该doc。因为模糊匹配不能精准定位一个分词,所以需要扫描整个倒排索引,以确定可以匹配到多少个索引项,效率很低,在大型项目中,这些查询方法都不建议使用!** ### n-gram n-gram是一个token filter,他会将分词结果再进行一次更细粒度的拆分。 ``` PUT my_index { "settings": { "analysis": { "filter": { "2_3_grams":{ "type": "ngram", "min_gram": 2, "max_gram": 3 } }, "analyzer": { "my_ngram":{ "type":"custom", "tokenizer":"standard", "filter":["2_3_grams"] } } } }, "mappings": { "properties": { "text":{ "type": "text", "analyzer": "my_ngram", "search_analyzer": "standard" } } } } ``` - min_gram: 最小拆分字符长度,默认1。 - max_gram: 最大拆分字符长度,默认2。 例如,"my English is good",按照默认分词器拆分之后是my、English、is、good,如果min_gram为1,max_gram为2,进一步拆分成m、y、E、n、g、l、i、s、h、i、s、g、o、o、d、my、En、ng、gl、li、is、sh、is、go、oo、od。这样当我们使用"my En is od"关键词进行match_phrase搜索时,依然可以匹配到结果,因为进行了更细粒度的分词,但是会消耗更多的磁盘空间,为何单独设置search_analyzer?因为如果搜索词也按照n-gram进行分词,会导致结果不准确。ES还提供了一种相似的token filter,叫做Edge n-gram token filter。 #### Edge n-gram token filter 使用 ``` GET _analyze { "tokenizer": "standard", "filter": [ { "type": "edge_ngram", "min_gram": 1, "max_gram": 2 } ], "text": "the quick brown fox jumps" } ``` 与n-gram基本相同,不同的是Edge n-gram只会分割分词的前缀,同样是"my English is good",使用Edge n-gram只会拆分成m、E、i、g、my、En、is、go,分词结果更少,占用磁盘空间更小,但是只支持前缀了,一般我们都会使用Edge n-gram,因为大多数场景,使用的都是前缀搜索。 Last modification:July 23rd, 2020 at 07:32 pm © 允许规范转载