在本文开始之前,引入一个问题:如何在索引中某个字段上查找以FD为前缀所有商品?Prefix Query 前缀匹配检索解决类似问题。
Prefix Query属于Term-level Query查询,查找索引中指定的字段上满足指定前缀的文档。类似关系型数据库,如MySQL中Like查询最左匹配原则。脑图如下:
内容说明:
本文内容同微信公众号【凡登】,关注不迷路,学习上高速,欢迎关注共同学习。原文链接:
Terms-level Query之Prefix Query
目录
本文基于Elasticsearch 7.13 版本。官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.10/query-dsl-prefix-query.html
一、语法
GET _search
{
"query":{
"prefix": {
"<field>": { // 待查询字段
"value": "前缀字符",
"case_insensitive":false,
"rewrite":"constant_score"
}
}
}
}
# 简写查询语法
GET _search
{
"query":{
"prefix": { "<field>": "前缀字符" }
}
}
参数说明:
-
"value": 待查询的前缀值 必填
-
"case_insensitive": 是否大小写敏感, 默认 false ,即大小写敏感, 非必填
-
"rewrite": 仅专业用户配置使用,默认constant_score,非必填
二、参数查询示例
# 样例数据
POST prefix_query_demo_index/_bulk
{"index":{}}
{"name":"deng"}
{"index":{}}
{"name":"Deng"}
{"index":{}}
{"name":"fandeng"}
{"index":{}}
{"name":"FanDeng"}
#mapping结构如下
{
"prefix_query_demo_index" : {
"mappings" : {
"properties" : {
"name" : {
"type" : "keyword" // 类型为keyword
}
}
}
}
}
1、默认参数查询
GET prefix_query_demo_index/_search
{
"query":{
"prefix": {
"name": {
"value": "d"
}
}
}
}
结果:
结果仅显示以d为前缀的文档,忽略大写D,即大小写敏感
{
// 省略
"hits" : [
{
"_index" : "prefix_query_demo_index",
"_type" : "_doc",
"_id" : "vnEDjYAB3Oxj0n4YCSWj",
"_score" : 1.0,
"_source" : {
"name" : "deng"
}
}
]
}
2、case_insensitive 参数
忽略大小写检索。于Elasticsearch 7.10.0 引入。
GET prefix_query_demo_index/_search
{
"query":{
"prefix": {
"name": {
"value": "d",
"case_insensitive":true
}
}
}
}
结果:
结果显示以d和D为前缀的文档,忽略大小写
{
// 省略
"hits" : [
{
"_index" : "prefix_query_demo_index",
"_type" : "_doc",
"_id" : "vnEDjYAB3Oxj0n4YCSWj",
"_score" : 1.0,
"_source" : {
"name" : "deng"
}
},
{
"_index" : "prefix_query_demo_index",
"_type" : "_doc",
"_id" : "wXEDjYAB3Oxj0n4YCSWj",
"_score" : 1.0,
"_source" : {
"name" : "Deng"
}
}
]
}
}
3、rewrite 参数
由于elasticsearch底层不支持prefix query,需要将其转换为bool以及term组合查询以期达到与prefix query同样的效果,转换有多种方式,此项提供了可选择的转换方式 默认 constant_score , 非必填。其他有效值设置,可参见:
https://www.elastic.co/guide/en/elasticsearch/reference/7.13/query-dsl-multi-term-rewrite.html
三、利用index_prefixes加速查询
prefix query查询性能较差,且影响集群文档性,为text字段添加index_prefixes参数可提高检索效率。官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.10/index-prefixes.html
示例如下:
# 定义mapping
PUT prefix_query_demo_index
{
"mappings": {
"properties": {
"name":{
"type": "text",
"index_prefixes":{
"min_chars" : 2, // 最小的前缀长度,大于0,默认2
"max_chars" : 5 // 最大的前缀长度,小于20,默认5
}
}
}
}
}
以下示例与上述mapping定义示例效果相同
PUT prefix_query_demo_index
{
"mappings": {
"properties": {
"name":{
"type": "text", // 类型为
"index_prefixes":{}
}
}
}
}
验证:
GET prefix_query_demo_index/_search
{
"query":{
"prefix": {
"name": {
"value": "fande" // 前缀字符串长度必须2~5,否则报错
}
}
}
}
结果:
{
// 省略
"hits" : [
{
"_index" : "prefix_query_demo_index",
"_type" : "_doc",
"_id" : "8vb3o4ABUfwYCvQ_kwUU",
"_score" : 1.0,
"_source" : {
"name" : "fandeng"
}
},
{
"_index" : "prefix_query_demo_index",
"_type" : "_doc",
"_id" : "9fb3o4ABUfwYCvQ_kwUU",
"_score" : 1.0,
"_source" : {
"name" : "FanDeng"
}
}
]
}
注:"value": "fande" // 因为当前name字段类型为text,此时字符长度必须2~5,否则报错。如果字段类型为keyword则无长度限制
问题:上述prefix查询默认大小写敏感,但为什么结果可以匹配到name值为大写的FanDeng 呢?我们再来看一个例子。
GET prefix_query_demo_index/_search
{
"query":{
"prefix": {
"name": {
"value": "Fande"
}
}
}
}
结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
为什么没匹配文档?
因为当前name字段为text类型,在索引过程中将name字段中的值全部转为小写后再索引,而prefix前缀查询在区分大小写下就无法匹配到符合条件的文档;
四、开启expensive_queries查询
Prefix Query查询过程比较慢且影响集群稳定性,expensive_queries配置可控制此类查询是否在集群中执行。如果字段开启index_prefixes属性,则忽略该设置。
# 关闭expensive_queries查询
PUT _cluster/settings
{
"persistent":{
"search.allow_expensive_queries": false
}
}
# 结果:
{
"acknowledged" : true,
"persistent" : {
"search" : {
"allow_expensive_queries" : "false"
}
},
"transient" : { }
}
开始验证
# 验证:
GET prefix_query_demo_index/_search
{
"query": {
"prefix": {
"name": "d"
}
}
}
# 结果报错:
{
"error" : {
"root_cause" : [
{
"type" : "exception",
"reason" : "[prefix] queries cannot be executed when 'search.allow_expensive_queries' is set to false. For optimised prefix queries on text fields please enable [index_prefixes]."
}
],
"type" : "search_phase_execution_exception",
"reason" : "all shards failed",
"phase" : "query",
"grouped" : true,
"failed_shards" : [
// 省略
}
],
"caused_by" : {
"type" : "exception",
"reason" : "[prefix] queries cannot be executed when 'search.allow_expensive_queries' is set to false. For optimised prefix queries on text fields please enable [index_prefixes]."
}
},
"status" : 400
}
五、总结
Prefix Query查询需要注意几点:
1、前缀字符大小写敏感
2、如开启index_prefixs属性,
2.1、字段类型必须为text
2.2、尽量采用小写作为前缀字符串进行查询。
2.3、前缀字符串长度一定控制在min_chars和max_chars之间,否则报错。
2.4、忽略集群expensive_queries配置。
3、Prefix Query查询开销较大且影响集群稳定性,可通过expensive_queries配置控制Prefix Query检索。