ElasticSearch映射的数据类型分析
ElasticSearch 映射基础
在 ElasticSearch 中,映射(Mapping)定义了文档(Document)及其包含的字段(Field)如何被存储和索引。映射就像是数据库中的表结构定义,但 ElasticSearch 的映射更加灵活,支持动态映射(Dynamic Mapping),即在文档被索引时,如果某个字段之前未定义过,ElasticSearch 会自动推断其数据类型并添加到映射中。
每个索引(Index)都有一个映射,它可以定义文档的字段名、数据类型、是否索引、是否存储等属性。映射主要包含以下几个重要部分:
- 元字段(Meta - fields):用于描述文档的元数据,如
_index
(文档所属的索引名)、_type
(文档类型,在 ElasticSearch 7.0 及以上版本逐渐弃用)、_id
(文档的唯一标识)等。 - 字段映射(Field Mappings):定义文档中每个字段的数据类型和相关属性。
核心数据类型
-
字符串类型(Text 和 Keyword)
- Text:用于全文搜索的字符串类型。当一个字段被定义为
text
类型时,ElasticSearch 会对其进行分词(Tokenization)处理,将文本拆分成一个个单词(Tokens),然后对这些单词进行索引。这使得在进行全文搜索时可以高效地匹配文本中的各个单词。
在上述示例中,PUT my_index { "mappings": { "properties": { "content": { "type": "text" } } } }
content
字段被定义为text
类型,适合存储文章内容、评论等需要进行全文搜索的文本。 - Keyword:适用于精确匹配和聚合操作的字符串类型。
keyword
类型的字段不会进行分词,而是将整个字符串作为一个整体进行索引。常用于存储标识符、标签、状态等字段。
这里的PUT my_index { "mappings": { "properties": { "product_id": { "type": "keyword" } } } }
product_id
字段适合用于精确查找特定产品,例如通过产品 ID 进行查询。
- Text:用于全文搜索的字符串类型。当一个字段被定义为
-
数值类型
- Byte:8 位有符号整数,范围是 -128 到 127。
- Short:16 位有符号整数,范围是 -32768 到 32767。
- Integer:32 位有符号整数,范围是 -2147483648 到 2147483647。
- Long:64 位有符号整数,范围是 -9223372036854775808 到 9223372036854775807。
- Float:32 位单精度浮点数。
- Double:64 位双精度浮点数。
- Half - Float:16 位半精度浮点数,适用于对精度要求不高但对存储空间敏感的场景。
- Scaled - Float:用于存储浮点数,通过指定一个缩放因子(Scaling Factor)来减少存储空间。例如:
上述PUT my_index { "mappings": { "properties": { "price": { "type": "scaled_float", "scaling_factor": 100 } } } }
price
字段使用scaled_float
类型,假设实际价格为 123.45,存储时会乘以缩放因子 100 变为 12345 存储为整数,查询时再进行相应的转换,这样可以在一定程度上节省存储空间。
-
日期类型(Date) ElasticSearch 支持多种日期格式,包括
strict_date_optional_time
(例如:2023 - 01 - 01T12:00:00Z
)、epoch_millis
(毫秒级时间戳)等。日期类型可以用于时间序列数据的存储和查询,比如日志记录的时间、订单创建时间等。PUT my_index { "mappings": { "properties": { "create_time": { "type": "date" } } } }
可以通过
format
参数指定日期格式,例如:PUT my_index { "mappings": { "properties": { "create_time": { "type": "date", "format": "yyyy - MM - dd HH:mm:ss" } } } }
-
布尔类型(Boolean) 用于表示 true 或 false 的值,常用于存储逻辑判断结果,如
is_published
(文章是否发布)、is_active
(用户是否活跃)等字段。PUT my_index { "mappings": { "properties": { "is_published": { "type": "boolean" } } } }
-
二进制类型(Binary) 用于存储二进制数据,如图片、音频、视频等。但需要注意的是,ElasticSearch 并不擅长处理大规模的二进制数据存储,通常只是在必要时存储一些小型的二进制文件片段。二进制数据以 Base64 编码的字符串形式存储。
PUT my_index { "mappings": { "properties": { "small_image": { "type": "binary" } } } }
复杂数据类型
-
数组类型(Array) ElasticSearch 中没有专门的数组数据类型定义,任何字段都可以包含多个值,即形成数组。例如,一个用户可能有多个兴趣爱好,我们可以这样定义映射:
PUT my_index { "mappings": { "properties": { "hobbies": { "type": "text" } } } }
然后在文档中可以这样插入数据:
POST my_index/_doc { "hobbies": ["reading", "swimming", "traveling"] }
当查询时,ElasticSearch 会将数组中的每个元素当作独立的值进行处理,支持对数组中任何一个值的匹配查询。
-
对象类型(Object) 用于表示 JSON 对象,文档中的字段可以包含嵌套的对象结构。例如,一个订单文档可能包含客户信息,客户信息本身又是一个对象:
PUT my_index { "mappings": { "properties": { "order_info": { "type": "object", "properties": { "customer_name": { "type": "text" }, "customer_email": { "type": "keyword" } } } } } }
插入文档如下:
POST my_index/_doc { "order_info": { "customer_name": "John Doe", "customer_email": "john@example.com" } }
-
嵌套类型(Nested) 嵌套类型是对象类型的一种特殊情况,用于处理对象数组。当一个对象数组中的每个对象需要独立进行查询和聚合时,就需要使用嵌套类型。例如,一个商品文档可能包含多个评论,每个评论都是一个对象,并且需要对每个评论进行独立的查询:
PUT my_index { "mappings": { "properties": { "reviews": { "type": "nested", "properties": { "author": { "type": "text" }, "content": { "type": "text" }, "rating": { "type": "integer" } } } } } }
插入文档如下:
POST my_index/_doc { "reviews": [ { "author": "Alice", "content": "Great product!", "rating": 4 }, { "author": "Bob", "content": "Not bad.", "rating": 3 } ] }
这样在查询时,可以精确地针对某个评论进行过滤和聚合操作,例如查询评分大于 3 的评论。
地理空间数据类型
-
地理点类型(Geo - Point) 用于存储地理位置的经纬度信息,常用于位置相关的应用,如附近的商店查找、轨迹跟踪等。可以通过多种方式定义地理点字段:
- 数组形式:
[longitude, latitude]
- 对象形式:
{"lat": latitude, "lon": longitude}
PUT my_index { "mappings": { "properties": { "location": { "type": "geo_point" } } } }
插入文档如下:
POST my_index/_doc { "location": [116.3975, 39.9085] }
ElasticSearch 提供了丰富的地理空间查询功能,如查询某个范围内的地理点等。
- 数组形式:
-
地理形状类型(Geo - Shape) 用于存储复杂的地理形状,如多边形、线等。例如,定义一个区域的边界多边形:
PUT my_index { "mappings": { "properties": { "area_boundary": { "type": "geo_shape" } } } }
插入文档时,需要按照 GeoJSON 格式定义地理形状:
POST my_index/_doc { "area_boundary": { "type": "Polygon", "coordinates": [ [ [116.388, 39.907], [116.392, 39.905], [116.395, 39.908], [116.388, 39.907] ] ] } }
可以通过地理形状查询来判断某个地理点是否在该区域内等操作。
特殊数据类型
-
IP 类型(IP) 用于存储 IP 地址,无论是 IPv4 还是 IPv6 地址都可以存储。例如,记录用户访问网站的 IP 地址:
PUT my_index { "mappings": { "properties": { "visitor_ip": { "type": "ip" } } } }
插入文档如下:
POST my_index/_doc { "visitor_ip": "192.168.1.1" }
ElasticSearch 支持对 IP 地址进行范围查询等操作,比如查询某个 IP 段内的访问记录。
-
Completion 类型 主要用于实现自动完成(Autocomplete)功能。它基于一种特殊的数据结构,能够高效地提供前缀匹配建议。例如,实现一个搜索框的自动完成功能,当用户输入部分关键字时,提供相关的建议:
PUT my_index { "mappings": { "properties": { "suggestion": { "type": "completion" } } } }
插入文档如下:
POST my_index/_doc { "suggestion": "apple" }
在查询时,可以通过
suggest
API 来获取自动完成的建议。 -
Token Count 类型 用于统计文本字段中的词元(Tokens)数量。例如,统计文章的字数(假设以词元数量近似表示):
PUT my_index { "mappings": { "properties": { "article_content": { "type": "text" }, "word_count": { "type": "token_count", "counter_field": "article_content" } } } }
这里
word_count
字段通过counter_field
关联到article_content
字段,在文档索引时会自动统计article_content
字段的词元数量并存储到word_count
字段中。
映射属性详解
-
索引属性(index)
true
:表示该字段会被索引,默认值为true
。只有被索引的字段才能被搜索。例如:PUT my_index { "mappings": { "properties": { "title": { "type": "text", "index": true } } } }
false
:表示该字段不会被索引,不能被搜索,但仍然会存储在文档中。常用于存储一些不需要搜索但需要在查询结果中展示的信息,如图片的原始文件名等。PUT my_index { "mappings": { "properties": { "image_original_name": { "type": "keyword", "index": false } } } }
-
存储属性(store)
true
:表示该字段的值会被单独存储,默认值为false
。当store
为true
时,可以在查询结果中直接获取该字段的值,而不需要从_source 字段中提取。例如:PUT my_index { "mappings": { "properties": { "summary": { "type": "text", "store": true } } } }
false
:表示该字段的值不会被单独存储,需要从_source
字段中获取。通常,对于大多数字段,不需要设置store
为true
,因为_source
字段已经包含了文档的原始内容。
-
分析器属性(analyzer) 分析器(Analyzer)用于在索引和查询阶段对文本进行分词等处理。对于
text
类型的字段,可以指定分析器。例如,使用standard
分析器(默认分析器):PUT my_index { "mappings": { "properties": { "content": { "type": "text", "analyzer": "standard" } } } }
也可以自定义分析器,通过组合字符过滤器(Character Filter)、分词器(Tokenizer)和词元过滤器(Token Filter)来满足特定的文本处理需求。例如,创建一个自定义分析器,先将文本转换为小写,然后进行分词:
PUT my_index { "settings": { "analysis": { "analyzer": { "my_analyzer": { "tokenizer": "standard", "filter": "lowercase" } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "my_analyzer" } } } }
-
多字段属性(multi - fields) 有时一个字段需要以不同的方式进行索引和查询。例如,一个
title
字段,既需要进行全文搜索,又需要进行精确匹配。这时可以使用多字段特性:PUT my_index { "mappings": { "properties": { "title": { "type": "text", "fields": { "keyword": { "type": "keyword" } } } } } }
这样,
title
字段可以用于全文搜索,而title.keyword
字段可以用于精确匹配,如查找特定标题的文档。
动态映射规则
-
基本类型推断
- 当 ElasticSearch 遇到一个新的字段时,会根据字段值来推断其数据类型。例如,如果字段值是一个整数,会推断为
long
类型;如果是浮点数,会推断为double
类型。如果是字符串,会根据字符串的内容和格式来判断,如果字符串看起来像日期,会推断为date
类型;如果字符串包含多个单词且需要全文搜索,会推断为text
类型;如果字符串是单个值且用于精确匹配,会推断为keyword
类型。 - 例如,插入如下文档:
ElasticSearch 会自动推断POST my_index/_doc { "age": 30, "name": "John Doe", "is_active": true, "create_time": "2023 - 01 - 01" }
age
为long
类型,name
为text
类型(同时自动创建name.keyword
字段用于精确匹配),is_active
为boolean
类型,create_time
为date
类型。
- 当 ElasticSearch 遇到一个新的字段时,会根据字段值来推断其数据类型。例如,如果字段值是一个整数,会推断为
-
动态模板(Dynamic Templates) 动态模板允许用户自定义动态映射规则。通过动态模板,可以根据字段名的模式、数据类型等条件来定义映射。例如,定义一个动态模板,让所有以
_id
结尾的字段都被映射为keyword
类型:PUT my_index { "mappings": { "dynamic_templates": [ { "id_template": { "match": "*_id", "mapping": { "type": "keyword" } } } ] } }
这样,当插入包含
user_id
、product_id
等字段的文档时,这些字段会被自动映射为keyword
类型。
映射更新注意事项
-
字段添加 可以随时向现有映射中添加新的字段,只要索引处于打开状态。例如,向已有的
my_index
索引添加一个新的description
字段:PUT my_index/_mapping { "properties": { "description": { "type": "text" } } }
-
字段修改 修改现有字段的数据类型通常是不允许的,因为这可能会导致已索引的数据无法正确查询。如果确实需要修改字段类型,一种常见的方法是创建一个新的索引,将旧索引的数据迁移到新索引,并使用新的映射。例如:
- 首先创建新索引并定义新映射:
PUT new_my_index { "mappings": { "properties": { "old_field": { "type": "new_type" } } } }
- 然后使用
reindex
API 将旧索引的数据迁移到新索引:POST _reindex { "source": { "index": "my_index" }, "dest": { "index": "new_my_index" } }
- 首先创建新索引并定义新映射:
-
映射删除 无法直接删除单个字段的映射,但可以删除整个索引,然后重新创建索引并定义新的映射。
DELETE my_index
然后重新创建:
PUT my_index { "mappings": { "properties": { // 新的映射定义 } } }
通过深入理解 ElasticSearch 映射的数据类型及其相关属性和操作,开发人员可以根据具体的业务需求,灵活、高效地设计索引结构,从而充分发挥 ElasticSearch 的强大搜索和数据分析能力。无论是简单的文本搜索,还是复杂的地理空间查询、聚合分析等场景,合理的映射设计都是实现高性能应用的关键基础。在实际应用中,还需要结合数据量、查询频率、存储成本等多方面因素进行综合考量,不断优化映射和索引设置,以满足业务的发展和变化。同时,随着 ElasticSearch 版本的不断更新,一些特性和功能可能会有所变化,需要持续关注官方文档以获取最新信息。