模糊查询处理Elasticsearch输入的错词

Elasticsearch 模糊查询

用户会经常不小心或者记不太清自己所要搜索的词,而导致在查询过程中输入一些错字,这时通过Elasticsearch的模糊查询来处理输入的错字,非常有利于提高用户的搜索体验。

匹配查询

在查询过程中,如果没有使用模糊查询,如果我输入的词中有错词,我们一般是很难通过匹配查询到想要的结果。

让我们首先举例说明错字”elastoc”:

请求

1
2
3
4
5
6
7
8
9
10
GET /test_index/_search
{
"query": {
"match": {
"content": {
"query": "elastoc"
}
}
}
}

响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}

匹配查询使用 fuzziness

使用Fuzzy Query时,查询的词可以不用与倒排索引中词完全匹配。

请求

1
2
3
4
5
6
7
8
9
10
11
GET /test_index/_search
{
"query": {
"match": {
"content": {
"query": "elastoc",
"fuzziness": "auto"
}
}
}
}

响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"took" : 48,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.24658465,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.24658465,
"_source" : {
"content" : "elastic"
}
}
]
}
}

"AUTO""fuzziness"字段中使用值,Elasticsearch 将确定合适的模糊距离。对于6个字符,默认情况下,Elasticsearch将允许2个编辑距离。"AUTO" 模糊性较为可取,但您可以根据需要使用准确的数字进行调整。

你也可以指定 "fuzziness"字段中的值:

1
2
3
4
5
6
7
8
9
10
11
GET /test_index/_search
{
"query": {
"match": {
"content": {
"query": "elastoc",
"fuzziness": "1"
}
}
}
}

设置合适 fuzziness

只要 Elasticsearch 中的词与你要查询的词之间的编辑距离在你的设置的 “fuzziness” 值的范围,都可以查询到。

但如果不在这个范围内,那就不会查询到对应的值,如下:

请求

1
2
3
4
5
6
7
8
9
10
11
GET /test_index/_search
{
"query": {
"match": {
"content": {
"query": "elastoc",
"fuzziness": "0"
}
}
}
}

响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}

工作原理

fuzzy query 的工作原理与 term query 类似,对所要查询的内容不会进行分析。

例如,我们在索引中添加内容 “elastic kibana” ,通过分析器查看,standard_analyzer 会产生两个项,”elastic” 和 “kibana”

使用 _analyze查看 “elastic kibana”

请求
1
2
3
4
5
POST /test_index/_analyze
{
"analyzer": "standard",
"text":"elastic kibana"
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"tokens" : [
{
"token" : "elastic",
"start_offset" : 0,
"end_offset" : 7,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "kibana",
"start_offset" : 8,
"end_offset" : 14,
"type" : "<ALPHANUM>",
"position" : 1
}
]
}

模糊查询 “elastoc kibaoa”

如果这个使用fuzzy query进行查询会的得不到想要的结果,如下:

请求
1
2
3
4
5
6
7
8
9
10
11
POST /test_index/_search
{
"query": {
"fuzzy": {
"content": {
"value":"elastoc kibaoa",
"fuzziness":"auto"
}
}
}
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"took" : 15,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}

模糊查询 “elastoc”

请求
1
2
3
4
5
6
7
8
9
10
11
POST /test_index/_search
{
"query": {
"fuzzy": {
"content": {
"value":"elastoc",
"fuzziness":"1"
}
}
}
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"took" : 11,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.24658465,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.24658465,
"_source" : {
"content" : "elastic kibana"
}
}
]
}
}

匹配查询结合模糊参数

如果在匹配查询中结合模糊参数是非常方便查询的事。分析器将先分析您的查询,然后再将其搜索到倒排索引中。这样在相应的 fuzziness值下可以得到我们想要的结果:

请求
1
2
3
4
5
6
7
8
9
10
11
GET /test_index/_search
{
"query": {
"match": {
"content": {
"query": "elastoc kibano",
"fuzziness": "2"
}
}
}
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.48631972,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.48631972,
"_source" : {
"content" : "elastic kibana"
}
}
]
}
}

重点:查询的term和倒排索引中的term是区分大小写,比如:Kibana与kibano的距离为: 2

Tips:

如果我们查询 elastoc kibano,它与elastic kibana之间相差 2:但是请注意我们使用如下是可以查询到相应结果。

请求
1
2
3
4
5
6
7
8
9
10
11
GET /test_index/_search
{
"query": {
"match": {
"content": {
"query": "elastoc kibano",
"fuzziness": "1"
}
}
}
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.48631972,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.48631972,
"_source" : {
"content" : "elastic kibana"
}
}
]
}
}

我们可以尝试查询"elastoc kibano""elastic kibana"之间编辑距离差为:2,再查询中"fuzziness":1,也返回了结果,这是因为查询是根据词条"elastoc""kibano"分析器进行分析的。

调整模糊查询参数

适当调整模糊查询的参数,可以提高查询中的用户体验,查询到想要的结果。

fuzzy query 参数

参数名 含义
fuzziness 定义最大的编辑距离,默认为AUTOfuzziness值为0、1、2,编辑距离最大只能设置为2。AUTO策略:在AUTO模式下,根据输入查询的term的长度决定编辑距离大小。用户也可以自定义term长度边界的最大和最小值,AUTO:[low],[high],如果没有定义的话,默认值为:36,即等价于 AUTO:3,6
prefix_length 定义最初始不会被“模糊”term的数量。这是基于用户的输入一般不会在最开始犯错误的设定的基础上设置的参数。这个参数的设定将减少去召回限定编辑距离的term时,默认参数为0
max_expansions 定义fuzzy query会扩展的最大term的数量。默认为 50
transpositions 定义在计算编辑聚利时,是否允许term的交换(例如ab->ba),默认参数为false

模糊性 fuzziness

  1. fuzziness 是fuzzy query的核心。

  2. 我们传递给此参数的值是允许的最大距离。

  3. 我们可以传递两种类型的值,即用于精确最大距离的整数和"AUTO"

  4. "AUTO"值允许查询中的模糊性是动态的。

​ 我们可以在"AUTO"值中调整2个参数并将其写为"AUTO:[low],[high]"。如果字词长度低于下限值,则查询会将模糊性设置为0。如果term长度在低值和高值之间,则查询将模糊性设置为1。最后,如果term长度大于高值,则查询将fuzziness设置为 2。如果未确定低值和高值,使用默认值36

让我们进一步认识auto,假如索引内容存在这些内容:elfuzzyelasticsearch

  • "el""eo"的编辑距离为:1。
  • "fyzzy":与 "fuzzy"的编辑距离为:1 。
  • "fyzyy""fuzzy"的编辑距离为:2。
  • "elasticsearhc"elasticsearch:的编辑距离为:2。
  • "elasticseaooo"elasticsearch:的编辑距离为:3。

使用 auto 后,只有 “fyzzy” 和 “elasticsearhc” 会产生结果。

换位 transpositions

transpositions 将允许您的查询将两个相邻字符(ab-> ba)的换位计算为1个距离。

例如,test_index中content中有这一段内容:"The elasticsearch is excellent sofeware",如果将transpositions设置为true,那么"leasticsearcc"也能得到匹配结果。

请求

1
2
3
4
5
6
7
8
9
10
11
12
GET test_index/_search
{
"query": {
"fuzzy": {
"content": {
"value": "leasticsearcc",
"fuzziness": "auto",
"transpositions":true
}
}
}
}

响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"took" : 780,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.49901885,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.49901885,
"_source" : {
"content" : "The elasticsearch is excellent sofeware"
}
}
]
}
}

transpositions:1 fuzziness:2

最大扩展 max_expansions

max_expansions 将确定您从查询中获得的最大结果。

如果max_expansions设置为:1,并且Elasticsearch中有2个文档匹配到查询,则Elasticsearch将仅返回其中一个文档。 请注意,这max_expansions适用于分片级别。因此,如果Elasticsearch中有很多分片,即使max_expansion为1,查询也可能返回更多结果。(默认值为max_expansions50)

前缀长度 prefix_length

prefix_length 是模糊查询中不考虑的前缀字符数。

例如,在test_index索引中content存在"The elasticsearch is excellent sofeware"

如果将设置prefix_length为1,则查询不会得到任何结果"llasticsearch"

prefix_length设置默认为:0

查询 “llasticsearch”

请求
1
2
3
4
5
6
7
8
9
10
11
GET test_index/_search
{
"query": {
"fuzzy": {
"content": {
"value": "llasticsearch",
"prefix_length":1
}
}
}
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}

查询 “eelasticsearch”

会得到相应返回结果:

请求
1
2
3
4
5
6
7
8
9
10
11
12
GET test_index/_search
{
"query": {
"fuzzy": {
"content": {
"value": "eelasticsearch",
"fuzziness": "auto",
"prefix_length":1
}
}
}
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.3194875,
"hits" : [
{
"_index" : "test_index",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.3194875,
"_source" : {
"content" : "The elasticsearch is excellent sofeware"
}
}
]
}
}
---- The end of this article ----