返回目录:word文档
在有搜索引擎之前,我们查文档常使用顺序匹配。我们需要在文档中顺序扫描,找到完全匹配的子句。
有的情况精确匹配比搜索引擎的查找有优势,比如这样的内容”chinese:1388838245“,如果用户输入”883“希望搜到这则内容,在常规的情况下是搜不到的。
这是因为在有了搜索引擎后,我们对查询语句做的处理就不一样了。我们通常会先分词,然后查找对应的词条索引,最后得到评分由高到低的文档列表。上面的例句在常规的分词情况下,没有也e799bee5baa6e79fa5e98193e59b9ee7ad94336不可能有”883“这个词条,因此搜索不到这则内容。
我一度以为没法实现完全匹配了,直到一个硬需求的出现。花了一天时间,把完全匹配用搜索引擎的思维整理出来。
简要描述实现思路,字段按一字一词的形式分词,再利用短语查询来搜索。
ES中,可以实现一字一词的的分词器是NGram。
它其实是一个上下文相连续字符的分词工具,可以看官方文档中的例子。当我们将它 min_gram 和 max_gram 都设为1时,它会按一字一词的形式分词。比如“”,分词的结果是["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": " <>, 李X <>, 秦X <>, 刘X <>"
},
"highlight": {
"sender": [
" <<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 李X <<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 秦纯X <<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实现,在此不做赘述。
字段按一字一词的形式分词,再利用短语查询来搜索。
ES中,可以实现一字一词的的分词器是NGram。
Ngram分词的官方文档地址:
https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-ngram-tokenizer.html
它其实是一个e68a84e8a2ade799bee5baa6e79fa5e98193362上下文相连续字符的分词工具,可以看官方文档中的例子。当我们将它 min_gram 和 max_gram 都设为1时,它会按一字一词的形式分词。比如“”,分词的结果是["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": " <>, 李X <>, 秦X <>, 刘X <>"
},
"highlight": {
"sender": [
" <<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 李X <<b>@</b><b>1</b><b>8</b><b>9</b><b>.</b><b>c</b><b>n</b>>, 秦纯X <<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实现,在此不做赘述。
stop up
是英语短语
短语词义是:
堵塞,盖住(洞或缝隙)
在中文网站里面其实也存在大量的stop word。比如,我们前面这句话,“在”、“里面”、“也”、“的”、“它”、“为”这些词都是停止词。这些词因为使用频率过高,几乎每个网页上都存在,所以搜索引擎开发人员都将这一类词语全部忽略掉。如果我们的网站上存在大量这样的词语,那么相当于浪费了很多资源。原本可以添加一个关键词,排名就可以上升一名的,为什么不留着添加为关键词呢?停止词对SEO的意义不是越多越好,而是尽量的减少为宜。既然问的是stop words, 我想主要是针对英文吧,也叫common words,(Stop Words. Most Search Engines do not consider extremely common words in order to save disk space or to speed up search results.) google中提出的stop words的概念是把一些对短语表述不构成直接影响的单词的的搜索结果直接过滤掉,包括a,an,the等冠词,in, at, of等介词, 一些人称代词,时态的助动词等,如果需要我这有一个停止词的大e799bee5baa6e79fa5e98193e4b893e5b19e334概列表可以发到你邮箱,这些词应为使用频率过高,所以搜索引擎把这些词的搜索结果会直接过滤掉,针对seo的话,这些词尽量少用较好,但如果不用对原来意思造成比较大的歪曲的话还是坚持使用吧,毕竟写的东西搜索只是抓取,最后看内容的还是人类。