Elasticsearch进阶使用-动态模版

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: Elasticsearch进阶使用-动态模版

总进度

Elasticsearch 8.1 认证工程师学习路线

今天我们来学习Elasticsearch中的动态模版,其实我们在第一课2.2.3章节中就已经学过了类似的了,链接如下

根据给定的需求创建索引

但是今天咱们学点不一样的,上次只是简单的使用,这次咱要深入理解,完美掌控才是第一目标,废话少说,下面开始

什么是动态模版

动态模版允许我们控制动态字段映射规则之外的数据

动态字段的映射我们可以设置dynamic参数为true或者runtime来启用动态映射,也可以自定义动态映射模版自定义映射,自定义之后的模版就可以根据匹配规则应用于动态添加的字段。在上一节中,动态映射规则已经说过,本节直接搬过来,如下

JSON data type "dynamic":"true" "dynamic":"runtime"
null 不添加 不添加
true or false boolean boolean
double float double
long long long
object object 不添加
array 根据数组中第一个非空的值判断 根据数组中第一个非空的值判断
日期类型的字符串 date date
数字类型的字符串 float or long double or long
不是日期也不是数字的字符串 text类型以及.keyword的字类型 keyword

其中自定义动态模版匹配规则方式有如下

  • match_mapping_typeElasticsearch中检测到的数据类型进行操作,参考上方图表
  • matchunmatch 可以使用模式匹配字段名
  • path_matchpath_unmatch 对字段的完整虚线路径进行操作
  • 如果动态模版没有定义march_mapping_typematchpath_match,那么模版不会匹配任何一个字段。但是_bulk 请求时可以通过模版名引用模版

也可以使用{name}{dynamic_type}模版变量作为占位符,比如后文中使用占位符实现分词器的指定等

动态模版的定义是对象数组

"dynamic_templates":[
  {
    "my_template_name":{#1
      ... 匹配条件 ...#2
      "mapping":{...}#3
    }
  },
  ...
]
  1. 自定义模版名称,可以是任意的字符串
  2. 模版的使用匹配条件可以是:match_mapping_type,match,match_pattern,unmatch,path_match,path_unmatch
  3. 匹配字段应该使用的索引映射

通过上面的 学习,我们知道了动态模版的定义,既然定义好了就该有验证,毕竟定义好的模版能不能用,是否定义的正确性还是需要验证的

验证动态模版

如果定义的模版包含无效的映射片段则会返回错误。在index操作时应用动态模版进行验证,但是大多数情况下在动态模版更新的时候会进行验证。提供无效的映射片段可能造成在更新或者某些条件下动态模版的验证失败,比如:

  • 如果没有指定match_mapping_type,但是这个模版提供了最少一个的有效映射,那么这个映射片段是有效的。但是如果将与模版匹配的字段映射为其他类型,则在index时返回验证错误。例如:配置一个动态模版,不包含match_mapping_type,这样就是一个有效的字符串类型,但是如果有一个字段匹配动态模版时被匹配为long,那么在index时将返回验证错误。建议就是将match_mapping_type配置为预期的JSON类型(参考开头的映射关系表)或者在mapping中配置好所需的类型
  • 如果我们在mapping的片段中使用了{name}占位符,那么在动态模版的更新时是跳过验证的。这是因为当时的字段名还是不知道的,所以在index时进行验证

如果有多个模版同时匹配,按照顺序匹配规则处理,第一个匹配的模版具有最高的优先级;当通过update mapping API更新动态模版时,所有的现有模版将会被覆盖,这就允许在最初的创建动态模版之后可以重新排序或者删除它们

动态模版中映射运行时字段

在上一节中我们的本小节内容例子就是使用的这个,有兴趣的可以回过头再看一眼,链接放到文章开头了

如果我们想Elasticsearch将某一种类型的新字段动态映射为运行时字段,那么我们可以通过设置"dynamic":"runtime",这些字段不会被编入索引,并且在查询时是从_source加载

或者我们使用默认的动态映射规则,然后创建动态模版,并将特定的字段映射为运行时字段。我们需要在index mapping中设置"dynamic:"true",然后创建一个动态模版,并将某种类型的新字段映射为运行时字段

举个例子,假设我们有一组数据,其中每个字段都是_ip开头的,根据动态映射规则,Elasticsearch会根据数值检测到的任何的字符串映射为float或者long,此时我们就可以创建一个动态模版,将这个新字符串映射为ip类型的运行时字段

下面是我们的一个例子,大概意思就是当Elasticsearch使用匹配模式是ip*的新字段时,它会将这些字段映射为ip类型的运行时字段。因为这些字段不是动态映射的,所以我们可以使用"dynamic":"true"或者"dynamic":"runtime"结合使用

PUT my-dynamic-template-001/
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_ip": {
          "match_mapping_type": "string",
          "match": "ip*",
          "runtime": {
            "type": "ip"
          }
        }
      }
    ]
  }
}

结合"dynamic":"true"或者"dynamic":"runtime"使用

PUT my-dynamic-template-001/
{
  "mappings": {
    "dynamic":"runtime",
    "dynamic_templates": [
      {
        "strings_as_ip": {
          "match_mapping_type": "string",
          "match": "ip*",
          "runtime": {
            "type": "ip"
          }
        }
      }
    ]
  }
}

上面的语句,我们会把符合匹配模式ip*的新字段映射为运行时字段,但是因为我们设置"dynamic":"runtime",所以后面的新字段我们都会设置为运行时字段,也就是下面这个语句,其中ip_req,ip_res,符合动态模版dynamic_templates的匹配规则ip*,而my_ip使用索引开头设置的"dynamic":"runtime"也会加入到运行时字段

PUT my-dynamic-template-001/_doc/1
{
  "ip_req":"127.0.0.1",
  "ip_res":"127.0.0.1",
  "my_ip":"locahost"
}

此时我们查看索引情况如下

{
  "my-dynamic-template-001" : {
    "mappings" : {
      "dynamic" : "runtime",
      "dynamic_templates" : [
        {
          "strings_as_ip" : {
            "match" : "ip*",
            "match_mapping_type" : "string",
            "runtime" : {
              "type" : "ip"
            }
          }
        }
      ],
      "runtime" : {
        "ip_req" : {
          "type" : "ip"
        },
        "ip_res" : {
          "type" : "ip"
        },
        "my_ip" : {
          "type" : "keyword"
        }
      }
    }
  }
}

上面就是一个简单的使用,其中

  • match_mapping_typestring,也就是字段的值是字符串
  • matchip* 即该字段名为ip开头的
  • runtime 定义被映射的字段类型,在上面例子中,被映射为runtime,类型为ip

match_mapping_type

match_mapping_typeJSON解析器检测到的数据类型。因为JSON不区分longinteger,也不区分doublefloat,所以解析时doublefloat都会被认为是doubleintegerlong都会被认为是long

注意:当使用动态映射的时候,ELasticsearch将始终选择更广泛的数据类型,但是有个例外是float类型,它的需要的存储空间少于double,并且对于大多数的应用程序来说足够准确。但是运行时字段不支持float类型,所以这就是"dynamic":"runtime"使用double的原因

Elasticsearch 会自动检测数据类型,检测规则就是文章开头的那个表格内容,并且我们还可以使用match_mapping_type中使用通配符*来匹配所有的数据类型

举个例子,如果我们想把整数字段映射为integer而不是long类型,字符串字段匹配为textkeyword类型,我们可以使用如下模版

  • 创建一个模版
PUT my-dynamic-template-002
{
  "mappings": {
    "dynamic_templates": [
      {
        "integers": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "integer"
          }
        }
      },
      {
        "strings": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "fields": {
              "raw": {
                "type":  "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}
  • 插入一条测试数据
PUT my-dynamic-template-002/_doc/1
{
  "my_integer": 5, 
  "my_string": "Some string" 
}
  • 查看生成的mapping
GET my-dynamic-template-002/_mapping
  • 返回结果如下,my_integer会被映射为integermy_string会被映射为textkeyword
{
  "my-dynamic-template-002" : {
    "mappings" : {
      "dynamic_templates" : [
        {
          "integers" : {
            "match_mapping_type" : "long",
            "mapping" : {
              "type" : "integer"
            }
          }
        },
        {
          "strings" : {
            "match_mapping_type" : "string",
            "mapping" : {
              "fields" : {
                "raw" : {
                  "ignore_above" : 256,
                  "type" : "keyword"
                }
              },
              "type" : "text"
            }
          }
        }
      ],
      "properties" : {
        "my_integer" : {
          "type" : "integer"
        },
        "my_string" : {
          "type" : "text",
          "fields" : {
            "raw" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

match与unmatch

match 使用模式匹配字段名称,unmatch 使用模式排除匹配字段

match_pattern可以通过设置此参数值调整match参数的行为,使match参数支持与字段名匹配的完整Java正则表达式来替代简单的通配符,如下

"match_pattern": "regex",
  "match": "^profit_\d+$"

如下示例,我们匹配名称以long_开头的所有字符串字段,排出以_text结尾的字符串字段,并将它们映射为long类型的字段

PUT my-dynamic-template-003
{
  "mappings": {
    "dynamic_templates": [
      {
        "longs_as_strings": {
          "match_mapping_type": "string",
          "match":   "long_*",
          "unmatch": "*_text",
          "mapping": {
            "type": "long"
          }
        }
      }
    ]
  }
}
PUT my-dynamic-template-003/_doc/1
{
  "long_num": "5", 
  "long_text": "foo" 
}
GET my-dynamic-template-003/_mapping

在上面例子中,long_num被映射为long类型的字段,这是因为match_mapping_typestring类型,并且是long_开头的。long_text虽然是long_开头的,但是也是_text结尾的,符合unmatch条件,这样的话long_text就按照默认规则string进行映射,生成textkeyword字段

path_match与path_unmatch

path_matchpath_unmatchmatchunmatch原理类似,但是是对字段的完整虚线路径进行匹配,而不仅仅是最终名称,例如some_object.*.some_field

PUT my-dynamic-template-004
{
  "mappings": {
    "dynamic_templates": [
      {
        "full_name": {
          "path_match":   "name.*",
          "path_unmatch": "*.middle",
          "mapping": {
            "type":       "text",
            "copy_to":    "full_name"
          }
        }
      }
    ]
  }
}
PUT my-dynamic-template-004/_doc/1
{
  "name": {
    "first":  "John",
    "middle": "Winston",
    "last":   "Lennon"
  }
}

如上所示,凡是name下的任何字段,除了以middle结尾的字段除外,都会被映射为text类型,并且copy_to带有full_name

执行如下命令查看

GET my-dynamic-template-004/_mapping

显示结果如下

{
  "my-dynamic-template-004" : {
    "mappings" : {
      "dynamic_templates" : [
        {
          "full_name" : {
            "path_match" : "name.*",
            "path_unmatch" : "*.middle",
            "mapping" : {
              "copy_to" : "full_name",
              "type" : "text"
            }
          }
        }
      ],
      "properties" : {
        "full_name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "properties" : {
            "first" : {
              "type" : "text",
              "copy_to" : [
                "full_name"
              ]
            },
            "last" : {
              "type" : "text",
              "copy_to" : [
                "full_name"
              ]
            },
            "middle" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        }
      }
    }
  }
}

从上面的结果可知,我们能够看出来,name之下的middle没有copy_to,它的映射按照默认的string映射规则,生成text以及keyword

需要注意的是,除了leaf字段外,path_matchpath_unmatch参数匹配对象路径,如下对文档索引将产生错误,因为name.title不能映射为文本类型

PUT my-dynamic-template-004/_doc/2
{
  "name": {
    "first":  "Paul",
    "last":   "McCartney",
    "title": {
      "value": "Sir",
      "category": "order of chivalry"
    }
  }
}

报错如下

{
  "error" : {
    "root_cause" : [
      {
        "type" : "mapper_parsing_exception",
        "reason" : "failed to parse field [name.title] of type [text] in document with id '2'. Preview of field's value: '{category=order of chivalry, value=Sir}'"
      }
    ],
    "type" : "mapper_parsing_exception",
    "reason" : "failed to parse field [name.title] of type [text] in document with id '2'. Preview of field's value: '{category=order of chivalry, value=Sir}'",
    "caused_by" : {
      "type" : "illegal_state_exception",
      "reason" : "Can't get text on a START_OBJECT at 5:14"
    }
  },
  "status" : 400
}

template variables (模版变量)

{name}{dynamic_type}占位符在映射中被替换为字段名称和检测到的动态类型

如下示例,将所有的字符串字段使用与字段同名的分析器,并禁用所有不是字符串字段的doc_values

PUT my-dynamic-template-005
{
  "mappings": {
    "dynamic_templates": [
      {
        "named_analyzers": {
          "match_mapping_type": "string",
          "match": "*",
          "mapping": {
            "type": "text",
            "analyzer": "{name}"
          }
        }
      },
      {
        "no_doc_values": {
          "match_mapping_type":"*",
          "mapping": {
            "type": "{dynamic_type}",
            "doc_values": false
          }
        }
      }
    ]
  }
}
PUT my-dynamic-template-005/_doc/1
{
  "english": "Some English text", 
  "count":   5 
}
GET my-dynamic-template-005/_mapping

在上面例子中,{name} 被替换为field name,而{dynamic_type}被替换为有JSON解析器检测到的数据类型

english字段分析器设置为english,而count被检测为long类型,并且doc_values设置为false

动态模版的例子

结构化搜索

当我们设置"dynamic":"true"时,Elasticsearch会将字符串字段映射为text类型,并带有一个keyword类型的子字段,如果我们只是结构化的搜索,对全文检索不需要,那么我们就可以让Elasticsearch映射为keyword字段,但是这样做的话,必须搜索与索引完全相同的值才能搜索这些字段,也就是精确匹配

PUT my-dynamic-template-006
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

字符串的纯文本映射

与上面结构化搜索相反,如果我们只关心全文检索,并且不会对字段进行聚合、排序和精确查找,那么我们让Elasticsearch将字符串映射为text类型

PUT my-dynamic-template-007
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_text": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text"
          }
        }
      }
    ]
  }
}

对于最近版本增加的运行时字段,我们呢还可以创建一个动态模版,将字符串映射为运行时的keyword类型,虽说该字段不会被索引,但是它们的值是存在_source中,并且可以用于搜索、聚合、过滤和排序

例如如下示例,将创建一个动态模版,将string字段映射为keyword运行时字段,虽然runtime定义是空的,但是Elasticsearch会使用文章开头的匹配规则添加,任何一个未通过时间或者数字检测的字符串都会被映射为keyword类型

PUT my-dynamic-template-008
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "runtime": {}
        }
      }
    ]
  }
}

此时我们索引一个文档

PUT my-dynamic-template-008/_doc/1
{
  "english": "Some English text",
  "count":   5
}

查看映射关系时,可以看到english被映射为keyword类型的运行时字段

GET my-dynamic-template-008/_mapping
{
  "my-dynamic-template-008" : {
    "mappings" : {
      "dynamic_templates" : [
        {
          "strings_as_keywords" : {
            "match_mapping_type" : "string",
            "runtime" : { }
          }
        }
      ],
      "runtime" : {
        "english" : {
          "type" : "keyword"
        }
      },
      "properties" : {
        "count" : {
          "type" : "long"
        }
      }
    }
  }
}

Disable norms

如果我们不按照评分进行排序,那么可以禁用索引中的评分因子以节省空间

PUT my-dynamic-template-009
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "norms": false,
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}

上面模版中出现的keyword的关键字字段是与动态映射的默认规则一致的,如果我们不需要可以按照上面的例子将其删除

时间序列

在使用Elasticsearch进行时间序列的分析时,通常会有很多的数字字段,但是这些数字字段一般不会进行过滤通常都是进行聚合。在这种情况下我们可以禁用这些字段的索引以节省磁盘空间,或许也可能获得一些索引的速度

PUT my-dynamic-template-010
{
  "mappings": {
    "dynamic_templates": [
      {
        "unindexed_longs": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "long",
            "index": false
          }
        }
      },
      {
        "unindexed_doubles": {
          "match_mapping_type": "double",
          "mapping": {
            "type": "float", 
            "index": false
          }
        }
      }
    ]
  }
}

与默认的动态规则一样,double被映射为float,因为他可以满足绝大多数的请求。并且只需要一半的磁盘空间

总结

好了关于动态模版的知识到这就结束了,从开始介绍什么是动态模版,其次是动态模版如何使用以及最后的动态模版的常用示例,那么你掌握了多少呢,快去尝试一下吧,下一篇预告《为时间序列索引定义索引生命周期策略ILM》敬请期待

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
6月前
|
索引
elasticsearch 创建索引模版template
elasticsearch 创建索引模版template
121 0
|
6月前
|
数据采集 机器学习/深度学习 自然语言处理
Elasticsearch 8.X 如何动态的为正文添加摘要字段?
Elasticsearch 8.X 如何动态的为正文添加摘要字段?
40 0
|
6月前
|
JSON 自然语言处理 负载均衡
数据库-Elasticsearch进阶学习笔记(集群、故障、扩容、简繁体、拼音等)
数据库-Elasticsearch进阶学习笔记(集群、故障、扩容、简繁体、拼音等)
86 0
|
存储 SQL 自然语言处理
ElasticSearch进阶
ElasticSearch进阶
|
SQL 缓存 JavaScript
ElasticSearch进阶:一文全览各种ES查询在Java中的实现(上)
ElasticSearch进阶:一文全览各种ES查询在Java中的实现(上)
|
SQL 消息中间件 JavaScript
ElasticSearch进阶:一文全览各种ES查询在Java中的实现(下)
ElasticSearch进阶:一文全览各种ES查询在Java中的实现(下)
|
Java 测试技术 开发工具
spring data elasticsearch:动态配置实体类索引名称indexName
最近接到一个需要,需要在spring data elasticsearch关联的实体类中动态的根据配置文件动态创建索引名称,比如开发环境下索引名称为user-dev,测试环境下为user-test,生产环境为user-prod 一开始接到这个需要觉得很怪,因为不同环境的区分直接搭建不同的es服务器环境不就行了吗,为什么要这么麻烦来处理呢?后来了解到是因为当前项目成本受限,需要隔离开发环境和测试环境用的同一台服务器,于是就产生了这个奇怪的需求。 其实想想,不排除某些场景下,就需要动态的创建索引名称。于是今天我们就来看看怎么实现这个需求
509 0
spring data elasticsearch:动态配置实体类索引名称indexName
|
存储 自然语言处理 Ubuntu
elasticsearch的字符串动态映射
在使用elasticsearch的动态映射能力时,字符串内容映射有些要注意的地方,咱们通过实战一起了解
155 0
elasticsearch的字符串动态映射
|
JSON 数据格式 索引
elasticsearch使用指南之Elasticsearch Dynamic Mapping(动态映射机制)
本文详细介绍了elasticsearch的动态类型映射机制,并详细介绍了动态映射模板(Dynamic templates)。
4813 0
|
JSON Java API
Elasticsearch Dynamic Mapping(动态映射机制)
Elasticsearch Dynamic Mapping(动态映射机制)
Elasticsearch Dynamic Mapping(动态映射机制)