Elasticsearch(简称 ES)是基于 Apache Lucene 构建的分布式搜索与分析引擎,广泛应用于日志检索、业务搜索、数据统计和监控分析。
在 ES 中,数据通常以 JSON 文档的形式写入索引,但 JSON 本身只有字符串、数字、布尔值、数组和对象等基本结构,并不能直接表达字段应该如何参与搜索、排序、聚合或地理位置计算。
在我之前的一个项目中,因为字段类型设置的不合理,字段安排的不正确,导致了 mapping 爆炸的问题,使得性能下降,后续花费了很大功夫去优化这方面的内容。
Mapping 就是解决这个问题的机制。它定义字段的数据类型、分析方式以及相关索引行为。一个字段选择 text 还是 keyword,一个对象选择 object、nested 还是 flattened,不仅会影响查询结果,还会影响索引大小、写入吞吐量、内存占用和查询性能。
因此,理解 Mapping 的关键不只是认识字段类型,而是知道每种类型适合什么场景、有哪些限制,以及应该如何根据业务需求选择。
一、Mapping 的基本结构
创建一个简单的日志索引:
PUT api-logs
{
"mappings": {
"properties": {
"requestId": {
"type": "keyword"
},
"status": {
"type": "integer"
},
"duration": {
"type": "long"
},
"createdAt": {
"type": "date"
},
"message": {
"type": "text"
}
}
}
}
这里的 properties 用来定义文档中的字段及其类型。
写入文档:
POST api-logs/_doc/1
{
"requestId": "req-001",
"status": 500,
"duration": 126,
"createdAt": "2026-10-09T08:30:00+08:00",
"message": "User authentication failed"
}
ES 会根据 Mapping 对字段进行处理,并建立相应的索引结构。
常见操作包括:
GET api-logs/_mapping
查看 Mapping。
GET api-logs/_field_caps?fields=*
查看字段在目标索引中的类型及相关能力,尤其适合排查跨索引查询时字段类型不一致的问题。
需要注意,Mapping 不是关系型数据库中要求每一列都存在的表结构。文档可以缺少某些字段,一个字段也可以包含多个值。不过,普通数组中的值应与字段定义的类型兼容,不能把 Mapping 当成可以任意混装不同类型数据的容器。
二、字符串类型:text、keyword 与其他类型
字符串是最容易出现 Mapping 设计问题的一类数据。
1. text:全文检索
text 用于需要分词的文本,例如文章内容、错误消息、商品描述和用户评论。
{
"message": {
"type": "text"
}
}
写入:
{
"message": "User authentication failed"
}
经过分析器处理后,文本会被拆分成词项,并建立倒排索引。查询时,ES 可以通过词项查找包含相关内容的文档。
GET api-logs/_search
{
"query": {
"match": {
"message": "authentication failed"
}
}
}
text 的主要用途是全文检索。对于普通的精确匹配、排序和聚合,通常不应该直接使用 text 字段。
如果需要中文全文检索,还要考虑分词器是否适合实际语料。默认分析器并不一定符合中文搜索需求。
2. keyword:精确匹配
keyword 适用于不需要全文分词的字符串,例如:
- 用户 ID、应用 ID、订单号。
- 请求 ID、Trace ID。
- 状态、类别、标签。
- 邮箱、域名和枚举值。
{
"userId": {
"type": "keyword"
},
"appId": {
"type": "keyword"
}
}
查询:
GET api-logs/_search
{
"query": {
"term": {
"userId": "10086"
}
}
}
keyword 通常还适用于排序、聚合和去重统计。
一个容易忽略的细节是:数字形式的 ID 不一定应该使用数字类型。如果它只是标识符,不需要数值范围查询或数学计算,使用 keyword 往往更合理。
3. text 和 keyword 同时使用
如果同一个字段既需要全文搜索,又需要精确匹配和聚合,可以使用多字段(Multi-fields)。
PUT applications
{
"mappings": {
"properties": {
"appName": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
这样就有两种访问方式:
appName 全文检索
appName.keyword 精确匹配、排序、聚合
但多字段也有成本。每增加一种字段映射,就可能增加索引和写入开销,因此只应该为实际需要的查询方式增加子字段。
ignore_above 用于限制 keyword 子字段索引的字符串长度。超过限制的值仍可能保留在 _source 中,但无法通过这个子字段正常匹配或排序。
4. constant_keyword:固定值字段
constant_keyword 适用于某个索引中的所有文档都具有同一个固定值的字段。
例如,一个专门保存生产环境日志的索引:
{
"environment": {
"type": "constant_keyword",
"value": "production"
}
}
如果索引中的每条文档都属于生产环境,就不必为每条文档重复存储和索引不同的环境标识。
它适合数据集具有明确固定属性的场景,但不适合一个索引中存在多种环境值的情况。
5. wildcard:适合特定的通配符检索
wildcard 是针对特定字符串检索场景设计的类型,尤其适用于较长字符串上的通配符查询,例如日志内容或路径模式匹配。
{
"logContent": {
"type": "wildcard"
}
}
它并不是所有字符串字段的通用替代品。普通精确匹配和聚合通常仍然适合使用 keyword,全文搜索则更适合 text。
如果大量使用 *error* 这样的包含式通配符查询,即使字段类型合适,也仍然可能产生较高的查询成本。
6. match_only_text:降低部分全文字段的存储成本
match_only_text 适用于主要需要全文匹配,但不需要完整相关性评分能力的文本字段。
{
"message": {
"type": "match_only_text"
}
}
它适合某些日志消息和低成本文本检索场景。
但如果业务需要精细的相关性评分、复杂排序或其他完整 text 能力,就不应该直接用它替换 text。
7. search_as_you_type:输入即搜索
如果业务需要用户输入几个字符后就能逐步显示搜索结果,可以考虑 search_as_you_type。
{
"title": {
"type": "search_as_you_type"
}
}
它针对逐步输入的搜索场景提供相应的索引结构,适用于搜索框、标题联想和部分前缀匹配场景。
它并不等同于所有场景下的自动补全。对于预先维护候选词、名称或热门搜索词的场景,还可以评估 completion。
8. completion:搜索建议
completion 用于专门的自动补全场景。
PUT search-suggestions
{
"mappings": {
"properties": {
"suggest": {
"type": "completion"
}
}
}
}
写入:
POST search-suggestions/_doc/1
{
"suggest": [
"Elasticsearch Mapping",
"Elasticsearch Query"
]
}
查询:
POST search-suggestions/_search
{
"suggest": {
"title-suggest": {
"prefix": "Elas",
"completion": {
"field": "suggest"
}
}
}
}
completion 和 search_as_you_type 都能用于搜索建议,但实现方式和适用场景不同。选择时需要结合候选词规模、匹配方式、权重和查询延迟进行测试。
9. 其他文本相关类型
| 类型 | 用途 |
|---|---|
annotated-text | 带有特殊标记的文本,常用于实体标注相关场景 |
pattern_text | 在支持该类型的版本中,用于特定模式驱动的文本处理场景 |
token_count | 保存文本经分析器处理后的词项数量 |
semantic_text | 通过推理模型实现文本语义检索的字段类型 |
semantic | 支持相应推理能力的多模态语义检索字段 |
这些类型并不是所有 ES 版本都支持。尤其 semantic_text 和 semantic 涉及推理模型、向量索引和版本或许可要求,使用前必须确认目标集群的实际能力。
三、数值类型
数值类型主要用于范围查询、数值排序、统计和计算。
| 类型 | 适用场景 |
|---|---|
byte | 较小范围的整数 |
short | 较小范围的整数 |
integer | 状态码、数量、普通整数 |
long | 大整数、较大计数值 |
unsigned_long | 非负 64 位整数范围内的值 |
half_float | 对精度要求较低的浮点数 |
float | 常见浮点数 |
double | 需要更大数值范围或更高浮点精度的场景 |
scaled_float | 通过缩放因子存储的数值 |
例如:
{
"status": {
"type": "integer"
},
"duration": {
"type": "long"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
scaled_float 可以用缩放因子处理固定小数位的数据,例如价格。
不过,不能只因为字段的实际数值较小,就随意把类型从 long 改成 byte。还需要考虑未来数据范围、聚合、精度以及应用程序的写入行为。
数值字段的类型设计应当由业务语义和数据范围决定,而不是只追求最小的类型。
四、日期和时间类型
1. date
date 是常规日期时间字段,适合日志时间、创建时间、更新时间等。
{
"createdAt": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
}
}
查询某一天的数据:
GET api-logs/_search
{
"query": {
"range": {
"createdAt": {
"gte": "2026-10-09T00:00:00+08:00",
"lt": "2026-10-10T00:00:00+08:00"
}
}
}
}
这里使用左闭右开的时间范围,方便连续时间段的拼接。
2. date_nanos
date_nanos 支持纳秒精度的日期时间。
{
"eventTime": {
"type": "date_nanos"
}
}
它适合确实需要更高时间精度的事件数据,例如高频事件和精细排序场景。
如果业务只精确到毫秒,通常没有必要为了“更精确”而全部使用 date_nanos。还需要考虑目标版本、查询能力和数据存储成本。
五、布尔、二进制与专用字符串类型
1. boolean
用于布尔值:
{
"enabled": {
"type": "boolean"
}
}
适合启用状态、是否成功、是否删除等字段。
2. binary
用于 Base64 编码的二进制数据:
{
"payload": {
"type": "binary"
}
}
binary 并不是用来替代普通文件存储或对象存储的通用方案。它适合特定的二进制数据需求,通常不具备普通字符串字段那样的检索能力。
3. ip
用于 IPv4 和 IPv6 地址:
{
"clientIp": {
"type": "ip"
}
}
相比将 IP 存成普通字符串,ip 更适合 IP 地址相关的查询和范围筛选。
4. version
在支持该类型的版本中,version 用于软件版本号:
{
"softwareVersion": {
"type": "version"
}
}
它适合需要按照软件版本语义进行比较的场景。不要把版本号简单当作浮点数处理,例如 1.10 与 1.9 的比较并不符合通常的软件版本语义。
5. murmur3
murmur3 用于计算并保存值的 MurmurHash3 哈希,通常与特定的哈希比较或去重类场景有关。
它不是普通业务字段的默认选择,也不应仅为了让查询更快就随意添加。需要先确认目标版本支持情况及实际收益。
六、对象类型:object、nested、flattened
这是 Mapping 中最容易影响查询正确性和字段数量的部分。
假设一个用户拥有多个应用角色:
{
"userId": "10086",
"roles": [
{
"name": "admin",
"level": 1
},
{
"name": "viewer",
"level": 5
}
]
}
如果需要按照 name 和 level 的组合进行查询,就必须考虑对象数组的语义。
1. object:普通 JSON 对象
普通对象通常是默认的对象映射方式。
{
"profile": {
"type": "object",
"properties": {
"name": {
"type": "keyword"
},
"age": {
"type": "integer"
}
}
}
}
查询时可以使用点号路径:
{
"query": {
"term": {
"profile.name": "Alice"
}
}
}
但普通 object 有一个重要限制:当字段是对象数组时,数组中每个对象的字段关系不会被独立保留用于查询。
例如:
{
"roles": [
{
"name": "admin",
"level": 1
},
{
"name": "viewer",
"level": 5
}
]
}
如果 roles 是普通 object,查询 roles.name = admin 且 roles.level = 5 时,可能匹配到这条文档,因为查询条件可以分别命中不同数组元素中的值。
如果业务要求两个条件必须来自同一个角色对象,就不应该直接使用普通 object。
2. nested:保留对象数组的关联关系
nested 用于需要独立保留对象数组内部字段关系的场景。
PUT users
{
"mappings": {
"properties": {
"userId": {
"type": "keyword"
},
"roles": {
"type": "nested",
"properties": {
"name": {
"type": "keyword"
},
"level": {
"type": "integer"
}
}
}
}
}
}
查询某个角色同时满足两个条件:
GET users/_search
{
"query": {
"nested": {
"path": "roles",
"query": {
"bool": {
"filter": [
{
"term": {
"roles.name": "admin"
}
},
{
"term": {
"roles.level": 5
}
}
]
}
}
}
}
}
由于原始数据中 admin 对应 level = 1,这条文档不会因为另一个角色的 level = 5 而满足查询。
nested 的优势是保留关联关系,代价则是索引和查询处理更复杂。每个嵌套对象会以独立的隐藏文档参与 Lucene 索引,因此大量嵌套对象会增加索引规模和查询成本。
如果对象数组只是用于简单筛选,不需要保证字段必须属于同一个数组元素,那么 nested 可能是不必要的复杂度。
3. flattened:解决动态对象字段膨胀
假设业务中存在一个不固定的扩展属性对象:
{
"userId": "10086",
"attributes": {
"department": "engineering",
"region": "beijing",
"customFieldA": "valueA",
"customFieldB": "valueB"
}
}
如果每个扩展属性都被映射为独立字段,字段数量可能随着业务发展持续增长,最终造成 Mapping 膨胀。
flattened 可以把整个对象作为一个字段处理:
PUT users
{
"mappings": {
"properties": {
"userId": {
"type": "keyword"
},
"attributes": {
"type": "flattened"
}
}
}
}
查询:
GET users/_search
{
"query": {
"term": {
"attributes.department": "engineering"
}
}
}
flattened 适合键名和结构不固定、只需要基本键值检索的对象。
但是它的子字段通常按照字符串值处理,不具备普通数值字段的完整语义。例如,对数值进行范围查询时,不应假设它会像 integer 一样执行数值比较;排序也可能按字符串字典序进行。
因此,flattened 不是所有 JSON 对象的替代品,而是对动态键值对象的一种折中方案。
4. 三者如何选择
| 对比项 | object | nested | flattened |
|---|---|---|---|
| 主要用途 | 普通对象 | 对象数组及关联查询 | 动态键值对象 |
| 子字段独立 Mapping | 是 | 是 | 默认整体映射 |
| 保留对象数组元素关联 | 否 | 是 | 不提供 nested 语义 |
| 数值范围查询 | 由子字段类型决定 | 由子字段类型决定 | 默认按字符串处理 |
| 字段膨胀风险 | 动态子字段多时较高 | 动态子字段多时较高 | 通常更低 |
| 查询复杂度 | 通常较低 | 通常更高 | 适合基本键值检索 |
还有一个与 nested 不同的关系类型:join。它用于在同一个索引中建立父子文档关系,例如一条产品文档关联多条评论文档。它并不等价于普通对象数组,且父子查询有额外成本,只有在确实需要独立更新或查询父子实体时才值得考虑。
七、范围类型
如果业务存储的不是一个单独数值,而是一个区间,可以考虑范围类型。
常见类型包括:
| 类型 | 表示的范围 |
|---|---|
integer_range | 整数范围 |
long_range | 长整数范围 |
float_range | 单精度浮点范围 |
double_range | 双精度浮点范围 |
date_range | 日期时间范围 |
ip_range | IP 地址范围 |
例如,某个服务支持的有效时间区间:
{
"validPeriod": {
"type": "date_range"
}
}
写入:
POST contracts/_doc/1
{
"validPeriod": {
"gte": "2026-01-01",
"lt": "2027-01-01"
}
}
范围字段可以用于表示有效期、价格区间、IP 地址段和数值区间等。
它与普通 date 或 long 字段的区别在于:普通字段通常表示一个值,而范围字段表示一个区间。查询时需要根据区间重叠、包含等业务语义选择合适的范围查询方式。
八、地理位置类型
ES 支持地理坐标和几何形状相关的类型。
1. geo_point
geo_point 表示地理坐标点:
{
"location": {
"type": "geo_point"
}
}
例如:
{
"location": {
"lat": 40.123,
"lon": 116.456
}
}
适用于附近搜索、按距离排序和地理范围筛选。
2. geo_shape
geo_shape 用于表示地理形状,例如多边形、线段和复杂地理区域。
适用于判断坐标是否位于某个区域、区域相交等地理空间查询。
3. point 和 shape
这两个类型用于笛卡尔坐标空间,而不是地球经纬度。
point:表示平面上的点。shape:表示平面中的复杂几何形状。
如果业务处理的是地图经纬度,通常优先考虑 geo_point 和 geo_shape;如果是游戏地图、平面图或者自定义坐标空间,则可以评估 point 和 shape。
九、向量与语义搜索类型
随着语义搜索和 RAG 应用越来越普遍,ES 也经常被用于向量检索。
1. dense_vector
dense_vector 用于存储稠密向量,通常是 embedding 模型生成的浮点数数组。
{
"embedding": {
"type": "dense_vector",
"dims": 384
}
}
这里的维度必须与所用模型输出一致。向量检索可以用于文档语义搜索、相似内容推荐和 RAG 检索。
不同 ES 版本支持的向量索引算法、量化类型、维度限制和查询语法有所不同,不能把较新版本的配置直接套用到 ES 7.x。
2. sparse_vector
sparse_vector 用于稀疏向量表示,适合相应的稀疏语义检索模型。
它与普通数值字段不同,也不应该被当成一个普通的数组字段随意聚合。
3. rank_feature 和 rank_features
这两个类型用于将某些数值特征用于搜索相关性排序。
例如,文章的热度、点击权重或业务评分可能影响搜索结果的排名。与直接把这些字段用于普通排序相比,Rank Feature 查询可以按照专门的评分逻辑影响相关性得分。
4. rank_vectors
在支持该类型的较新版本中,rank_vectors 用于存储每个文档的多个向量,以支持 late-interaction 等重排序场景。它不是普通向量检索的通用替代品。
5. semantic_text 与 semantic
较新版本还提供了更高层的语义字段类型:
semantic_text:主要面向文本语义搜索,可以自动处理部分向量生成、分块和索引细节。semantic:面向支持的多模态语义检索场景,可以处理文本之外的内容类型,具体取决于推理端点能力。
这些类型依赖对应版本、推理服务和许可条件。对于传统 ES 7.x 集群,应先确认版本能力,不能因为 Mapping JSON 能写出来就认为集群可以正常执行。
十、聚合指标与直方图类型
对于已经预先计算好的指标数据,ES 提供了一些专门的类型。
1. aggregate_metric_double
用于存储已经聚合的数值指标,例如最小值、最大值、总和以及数量等。它适合上游已经完成部分聚合、希望在 ES 中保存聚合结果的场景。
2. histogram
用于存储直方图数据,通常包括数值样本的分布信息。它适合指标系统将一批数值预聚合后再写入 ES 的场景。
3. exponential_histogram
用于保存指数直方图形式的聚合指标数据,适用于相应的可观测性和指标处理工作流。
4. tdigest
在支持该类型的版本中,用于表示 T-Digest 相关的数据结构,常用于近似分位数等统计场景。
这些类型并不是常规业务文档的默认字段类型。如果每条请求日志只保存一个耗时,通常直接使用 long 即可;只有在数据已经以特定聚合结构产生时,才应该考虑对应的聚合类型。
十一、其他特殊类型
还有一些不属于普通业务字段的类型,了解它们的用途即可。
| 类型 | 用途 |
|---|---|
alias | 为现有字段创建字段别名,方便统一访问 |
passthrough | 在支持该功能的版本中,让指定子字段以别名形式暴露 |
percolator | 索引 Query DSL 查询,让文档可以匹配预先保存的查询条件 |
join | 建立同一索引内的父子文档关系 |
这些类型通常针对特定架构需求。
例如,percolator 适合“预先保存很多规则,收到新文档后判断哪些规则匹配”的场景,例如规则引擎或订阅匹配。它不是普通全文检索的替代方案。
alias 则可以帮助应用程序解耦实际字段名称与查询使用的名称,但它不能凭空创建新的数据,也不代表可以随意改变底层字段的类型。
十二、数组与多字段不是独立字段类型
有两个概念经常和 Mapping 类型混淆:数组和多字段。
1. 数组
ES 没有独立的 array 字段类型。一个字段可以包含一个值,也可以包含多个值:
{
"tags": [
"nodejs",
"elasticsearch",
"nestjs"
]
}
只需要将 tags 映射为适当的类型:
{
"tags": {
"type": "keyword"
}
}
但如果数组元素是对象,并且需要保证多个字段必须来自同一个数组元素,就要考虑前面介绍的 nested。
2. 多字段
多字段通过 fields 将同一份源数据以多种方式索引:
{
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
多字段本身不是一种独立的 Mapping 类型,而是一种字段设计机制。
十三、Mapping 的性能优化
了解字段类型之后,下一步是根据实际访问模式优化 Mapping。
1. 减少不必要的索引
如果某个字段只用于返回展示,而不需要检索、排序或聚合,可以考虑关闭索引能力。
{
"userAgent": {
"type": "keyword",
"index": false,
"doc_values": false
}
}
这种配置适合只需要在 _source 中保留值、不需要通过该字段检索或聚合的场景。
但不要把所有字段的 index 和 doc_values 都关闭。关闭索引会影响查询能力,关闭 doc_values 会影响排序、聚合及部分脚本访问。
2. 避免 Mapping 膨胀
对于动态字段很多的对象,可以考虑:
- 显式定义固定字段。
- 对未知字段使用适当的动态模板。
- 使用
dynamic: false保留未知字段但不自动创建 Mapping。 - 在适合的动态键值对象上使用
flattened。 - 控制允许写入的字段数量和嵌套深度。
如果业务需要严格限制字段结构,可以使用:
{
"mappings": {
"dynamic": "strict",
"properties": {
"requestId": {
"type": "keyword"
}
}
}
}
strict 会拒绝未知字段,因此必须提前确认数据生产端不会发送合法但未定义的字段。
3. 控制 nested 使用范围
nested 不是免费的。每个嵌套对象都会增加内部文档和索引处理成本。
只在需要保留数组对象之间的字段关联时使用它。如果普通对象查询就能满足需求,没有必要把所有对象都改成 nested。
4. 合理使用 doc_values
doc_values 主要服务于排序、聚合和字段值读取。对于需要聚合的 keyword 和数值字段,通常应保留默认行为。
不要轻易给 text 开启 fielddata 来解决聚合问题。它可能消耗大量 JVM 堆内存。通常应为需要聚合的文本增加 keyword 子字段。
5. 控制高基数聚合
对状态码、应用 ID 等字段聚合通常比较常见,但对每条文档都不同的请求 ID 进行高基数聚合,可能带来较大开销。
应只聚合真正需要统计的维度,并限制返回桶的数量。需要遍历大量唯一值时,可以评估 Composite Aggregation。
6. 优化分片规划
分片数量需要根据数据规模、查询负载、保留周期和节点资源来确定。
分片太少可能限制并行处理,分片太多则会增加集群元数据、内存、文件句柄和调度开销。
GET _cat/indices?v
GET _cat/shards?v
GET _cluster/health
先观察现有分片大小和分布,再决定是否需要调整索引生命周期或重新规划索引。
7. 使用 Bulk 并合理设置 Refresh
批量写入通常比逐条请求更高效:
POST _bulk
{"index":{"_index":"api-logs"}}
{"requestId":"req-001","status":200}
{"index":{"_index":"api-logs"}}
{"requestId":"req-002","status":500}
批次大小和并发数需要通过压测确定,不能一味增大。
如果业务允许更长的搜索可见延迟,可以适当延长 refresh_interval,减少 Refresh 频率。对于要求近实时查询的业务,则需要保留相应的延迟目标。
8. 避免深分页
from + size 的方式适合较浅的分页。对于大量数据的深分页,可以考虑 search_after,并在需要稳定快照时结合 Point in Time(PIT)。
同时应控制每次返回的文档数量,避免无意义地读取大量字段。
9. 监控集群资源
常用检查接口:
GET _nodes/stats/jvm,process,fs,indices,thread_pool
GET _cat/nodes?v
GET _cat/indices?v
GET _cat/shards?v
结合查询延迟、写入吞吐量、CPU、堆内存、磁盘 I/O、分片分布和线程池拒绝情况定位瓶颈。
不要在没有证据的情况下直接增加节点或修改 JVM 参数。先判断问题来自 Mapping、查询、分片、写入链路还是硬件资源,再进行针对性优化。
十四、如何选择字段类型
在实际项目中,可以按以下思路快速选择:
| 业务需求 | 优先考虑 |
|---|---|
| 全文检索 | text |
| 精确匹配、排序和聚合 | keyword |
| 固定值字段 | constant_keyword |
| 通配符模式检索 | wildcard |
| 搜索框逐字输入 | search_as_you_type |
| 候选词自动补全 | completion |
| 数值范围和统计 | 合适的数值类型 |
| 日期时间查询 | date / date_nanos |
| IP 地址检索 | ip |
| 普通 JSON 对象 | object |
| 需要保留关联关系的对象数组 | nested |
| 动态键值对象 | flattened |
| 父子文档关系 | join |
| 地理坐标点 | geo_point |
| 地理区域和复杂形状 | geo_shape |
| 平面坐标点和形状 | point / shape |
| 稠密向量搜索 | dense_vector |
| 稀疏向量搜索 | sparse_vector |
| 文本语义检索 | semantic_text 或适合的向量类型 |
| 预先聚合的指标 | 对应聚合指标类型 |
| 预先保存的查询规则 | percolator |
最后要特别强调:字段类型列表会随着 Elasticsearch 版本演进而变化。 ES 7.x、8.x 和 9.x 并不能简单地视为完全相同的 Mapping 能力集合。升级或迁移前,应根据目标版本的官方文档确认具体类型是否可用、参数是否变化,以及索引是否需要重新创建。
好的 Mapping 设计并不是把所有类型都用一遍,而是让字段类型与业务语义、查询方式和数据规模保持一致。选对类型是基础,控制字段数量、查询成本和分片规模,才是让 Elasticsearch 长期稳定运行的关键。
