Elasticsearch:在搜索引擎中怎么实现完全匹配(内容精确匹配)查询
本文地址:
http://blog.****.net/hereiskxm/article/details/47979013
在有搜索引擎之前,我们查文档常使用顺序匹配。比如要搜索 "我的祖国是花园",需要在文档中顺序扫描,找到完全匹配的子句。
在有了搜索引擎后,我们对查询语句做的处理就不一样了。我们通常会先分词,然后查找对应的词条索引,最后得到评分由高到低的文档列表。我一度以为没法实现完全匹配了,直到一个硬需求的出现。花了一天时间,把完全匹配用搜索引擎的思维整理出来。
简要描述实现思路,字段按一字一词的形式分词,再利用短语查询来搜索。
ES中,可以实现一字一词的的分词器是NGram。
Ngram分词的官方文档地址: https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-ngram-tokenizer.html
它其实是一个上下文相连续字符的分词工具,可以看官方文档中的例子。当我们将它 min_gram 和 max_gram 都设为1时,它会按一字一词的形式分词。比如“shinyke@189.cn”,分词的结果是["s" , "h" , "i" , "n" , "y" , "k" , "e" , "@" , "1" , "8" , "9" , "." , "c" , "n" ]。
<pre name="code" class="javascript">/index_name/ { "settings": { "analysis": { "analyzer": { "charSplit": { "type": "custom", "tokenizer": "ngram_tokenizer" } }, "tokenizer": { "ngram_tokenizer": { "type": "nGram", "min_gram": "1", "max_gram": "1", "token_chars": [ "letter", "digit", "punctuation" ] } } } } }以上语句中,构建了一个名为“charSplit”的分析器。它使用一个名为“ngram_tokenizer”的Ngram分词器。
可以用如下语句测试charSplit分析器,可以看到一字一词的效果:
curl -POST http://IP:port/{index_name}/_analyze?pretty&analyzer=charSplit "测试语句"
把这个分析器在mapping里用起来:
... "sender": { "type": "string", "store": "yes", "analyzer": "charSplit", "fields": { "raw": { "type": "string", "index": "not_analyzed" } }, ...
接下来就可以用match_phrase来实现完全匹配查询。
/{index_name}/{type_name}/_search { "query": { "multi_match": { "query": "@189.cn", "type": "phrase", //type指定为phrase "slop": 0, //slop指定每个相邻词之间允许相隔多远。此处设置为0,以实现完全匹配。 "fields": [ "sender" ], "analyzer": "charSplit", //分析器指定为charSplit "max_expansions": 1 } }, "highlight": { //测试高亮是否正常 "pre_tags": [ "<b>" ], "post_tags": [ "</b>" ], "fragment_size": 100, "number_of_fragments": 2, "require_field_match": true, "fields": { "sender": {} } } }phrase查询原始的作用是用来做短语查询,它有一个重要的特点:有顺序。我们利用了它匹配的有序性,限制slop为0,则可实现完全匹配查询。
以上语句返回的结果是:
{ "took": 18, "timed_out": false, "_shards": { "total": 9, "successful": 9, "failed": 0 }, "hits": { "total": 1, "max_score": 0.40239456, "hits": [ { "_index": "index_name", "_type": "type_name", "_id": "AU9OLIGOZN4dLecgyoKp", "_score": 0.40239456, "_source": { "sender": "18977314000 <18977314000@189.cn>, 李X <18977314000@189.cn>, 秦X <18977314000@189.cn>, 刘X <18977314000@189.cn>" }, "highlight": { "sender": [ "18977314000 <18977314000<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 李X <18977314000<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 秦纯X <18977314000<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 刘X <189773140" ] } } ] } }
到此,就实现了完全匹配查询。实际环境中用NGram做一字一词分析器的时候会更细致一些,比如有一些字符需要用stop word过滤掉。这些细节可以根据实际需要在构造分析器时添加filter实现,在此不做赘述。
版权声明:本文为博主原创文章,未经博主允许不得转载。