前言:在elasticsearch中,结合业务场景与数据值的特点,在索引的字段类型配置中设置合理的字段类型是十分有必要的。例如:我们将field类型设置为text,配合分词器,我们可以实现全文检索。如果将field类型设置为keyword,我们就可以对数据实现精确查询聚合排序。
一.elasticsearch的字段类型
binary
可以存储编码为base64的编码的二进制值。
应用场景:
- 二进制文件存储:例如:图片,PDF文档,音频文件等可以通过二进制的方式在elasticsearch中进行存储。
- 加密数据存储:如果需要再索引中存储敏感数据,可以使用第三方加密工具对数据进行加密,然后将加密后的二进制数据使用binary字段类型进行存储。
- 序列化对象存储:可以将对象进行二进制序列化后,使用binary进行存储。
注意事项:
使用binary存储字段数据后,数据只是以二进制的形式存储于elasticsearch中。在我们操作数据时,并不能对数据进行检索,聚合或分析。如果需要对binary类型的字段进行数据则需要结合其他索引字段或对binary字段的数据进行反序列化来实现。
boolean
布尔类型,用于存储true或false;
应用场景:
- 状态标记:用于标记文档中某个属性或者状态的真假。例如:行为日志中的是否已读,流程日志中的是否审批等场景。
- 过滤查询:可以通过boolean类型对数据进行特定状态的过滤查询操作。来筛选符合条件的文档。
- 聚合分析:boolean类型可以用于聚合分析,例如分析某个特定状态值群体的占比情况。
Keywords
属于keyword字段族,其中包含了keyword,constant_keyword和wildcard;
keyword
用于存储结构化数据。使用keyword类型存储的数据不会被分词,而是将整个字段值作为一个关键字进行处理。例如:我们日常使用中的邮箱地址,手机号,用户ID,等数据都可以用keyword类型进行存储。
应用场景:
- 精确查询:当我们需要精确匹配某个关键字时,使用keyword字段类型可以确保我们完全匹配到该条件。类似于关系型数据库中的条件查询。例如:我们队邮件地址进行查询。where email_address='xxx@qq.com'
- 聚合排序:keyword类型可以用于聚合操作。例如:计算某个字段进行特定关键字的分布统计,多为分析等。同时可以对结果基于某个字段进行排序。
- 过滤查询:keyword类型字段可以用于对数据进行过滤筛选。通过精确匹配关键字来对数据数据进行条件查询或多条件查询。
注意事项:
keyword类型由于是将整个字段值当做一个关键字进行处理,所以不适用于全文检索,模糊匹配等需要对文本内容进行分析的场景。
constant_keyword
用于存储常量关键字。constant_keyword字段类型的值在所有文档中都是相同的,它不会根据文档内容而变化。主要在索引中存储固定的元数据或者标记。
应用场景:
- 字段标记:标记文档的属性或者状态。例如:软件发版流程日志中,用于记录是否发版,需求是否审核等场景。
- 元数据存储:用于存储索引数据中的元数据。例如:数据的创建日期,版本号等这类字段值相同的数据。
- 过滤筛选:由于使用该字段类型的字段值都是相同的,所以我们可以对其进行过滤筛选,筛选出特定属性的文档数据。
wildcard
通配符字段类型,主要用于存储准备使用通配符形式检索的字段数据。使用该字段类型,我们可以通过通配符的形式对数据进行检索。例如:使用(*或?)来匹配具有特定模式的文本。
应用场景:
模糊搜索:我们可以在搜索数据时使用通配符的形式对数据进行模糊匹配。来匹配包含搜索关键字的数据。例如:使用"success*"进行搜索,那么搜索结果则会返回"success","successful","successor"等以"success"关键字开头的文本。
多字符匹配:我们可以使用?来匹配一个字符。例如我们使用"he?p"关键字进行搜索,则会匹配"help","heap"等符合匹配规则的数据。
高级搜索:可以使用通配符根据特定规则对数据进行匹配,例如根据规则匹配URL,文件路径等。
注意事项:
由于wildcard使用的是字符串匹配这种方式对数据进行查询,在大规模数据集索引中,容易产生慢查询,造成性能问题。因此,在使用上仍需考虑其他搜索方式。
具体可以参考我的另一篇文章:https://cloud.tencent.com/developer/article/2357251
Numbers
数值字段类型,其中包含integer,long,double,float,byte等长整型,单双精度浮点型数值字段类型。
主要用于我们存储数值类型数据,例如:金额,long类型时间戳,统计指标数值,商品数量等。根据字段值大小,选择合适的数值字段类型,能够有效的节约磁盘存储空间,提高存储效率与数据检索效率。需要注意的是,elasticsearch在进行存储空间优化时主要根据存储的实际数值来进行存储优化,并不是根据我们选择的字段类型进行针对性优化。
应用场景:
- 整数与浮点数存储:我们可以使用整数类型
integer
或浮点数类型,例如:float
或double
对数据进行存储,这些字段类型适合我们存储,例如:订单金额,商品库存等数据。 - 范围查询:可以使用数字字段类型,对数据进行范围查询。例如根据查询大于或小于某个特定值的文档。例如:查询价格范围,时间范围等。
- 数值聚合:数值类型数据我们可以对其进行数学运算,例如:计算平均值,最大值,最小值等。
- 数据分析:可以对字段存储的数字进行分析,例如百分比计算等操作。
alias
对现有字段定义别名。当对字段进行别名定义后,我们也可以通过别名来对字段进行检索。在搜索当中所有的请求都可以使用别名,不论是精确查询还是聚合查询,都可以使用字段的别名。
我们可以通过以下这个样例,对字段别名进行定义,并进行搜索。
代码语言:javascript复制PUT trips
{
"mappings": {
"properties": {
"distance": {
"type": "long"
},
"route_length_miles": {
"type": "alias",
"path": "distance"
},
"transit_mode": {
"type": "keyword"
}
}
}
}
GET _search
{
"query": {
"range" : {
"route_length_miles" : {
"gte" : 39
}
}
}
}
注意事项:
- 在对字段配置别名时,该字段必须是一个具体的字段,不能是一个对象或者其他字段的别名。
- 在配置字段别名时,该字段必须是已经存在的字段。
- 如果是针对嵌套对象字段进行别名配置,则别名必须拥有与嵌套对象字段一样的对象范围。
object
用于存储json嵌套对象,当我们需要将整个json以对象的形式进行存储时,可以选择该类型。
应用场景:
- 嵌套文档存储:使用object类型,我们可以在文档中存储嵌套文档或对象,在表示层次结构或多属性文档数据时非常实用。例如存储一对多的关系,例如一个人对应的姓名,性别,银行卡号,手机号等属性。
- 复杂对象存储:我们可以用该类型来存储复杂的json对象,不用在针对json其中的字段进行解析,将其拆分为单独的字段进行存储。可以直接将整个json对象进行完成的存储。更加便于检索其中复杂的嵌套数据结构。
- 子字段操作:我们可以通过定义嵌套字段中的子字段类型,来实现对嵌套数据中某个子字段的操作。也可以针对子字段进行单独的搜索查询,聚合排序。例如:在嵌套的地址对象中,我们可以针对子字段的"城市","区县","街道",分别进行查询操作。
- 动态映射:当我们将字段设置为object类型后,elasticsearch可以自动检测和映射嵌套对象的字段。不用针对数据中的字段进行预先定义。
flattened
用于存储json对象数据。通过使用该类型,将整个json扁平化的映射为一个字段。然后解析出json中的键值对。一般多用于存储含有大量字段或未知字段的json对象。使用该类型存储的json数据只允许使用基础查询。
在以下示例中,我们将bug_reports索引的labels设置为flattened类型。然后我们在手动插入一条数据,可以看到在labels字段中,我们手动插入了一条json嵌套数据。
代码语言:javascript复制PUT bug_reports
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"labels": {
"type": "flattened"
}
}
}
}
POST bug_reports/_doc/1
{
"title": "Results are not sorted correctly.",
"labels": {
"priority": "urgent",
"release": ["v1.2.5", "v1.3.0"],
"timestamp": {
"created": 1541458026,
"closed": 1541457010
}
}
}
在下面的样例中,在我们可以直接对labels字段,以"labels":"urgent"为条件,直接进行精确查询;也可以用"labels.release":"v1.3.0" json内部某个子字段作为查询条件进行精确查询。
代码语言:javascript复制
POST bug_reports/_search
{
"query": {
"term": {"labels": "urgent"}
}
}
POST bug_reports/_search
{
"query": {
"term": {"labels.release": "v1.3.0"}
}
}
注意事项:
当字段设置为flattened类型后,仅支持以下查询方式。
term
,terms
,terms_set
prefix
range
match
,multi_match
query_string
,simple_query_string
exists
Nested
专门用于存储嵌套对象的字段类型。如果我们需要存储内部包含了大量键值对的json对象或其他嵌套对象数据时,我们可以使用Nested类型。反之建议使用flattened字段类型。对于嵌套字段类型数据的存储与查询所消耗的资源相较于其他字段类型是更加高昂的。所以需要在存储嵌套对象数据时选择合适的字段类型。
在以下样例中,我们将user字段的类型设置为了Nested。然后在该字段,插入了一个存储json对象的数组。
代码语言:javascript复制PUT my-index-000001
{
"mappings": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
PUT my-index-000001/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
我们可以使用一下方式对嵌套的数据进行查询。将嵌套对象中的子字段作为条件进行查询。
代码语言:javascript复制GET my-index-000001/_search
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "Alice" }},
{ "match": { "user.last": "Smith" }}
]
}
}
}
}
}
当我们使用Nested类型时,嵌套对象中的子字段在查询时可以进行聚合排序等操作。
Join
连接数据类型:主要用于在同一索引的文档中,创建父/子关系,通过添加Join字段,我们可以将文档定义为父级文档和子级文档,来表示文档建的关系。当我们的数据存在着一对多的关系时,我们就可以通过Join类型来为这些数据创建父子关系。例如:文章主体与文章评论之间的关系。其中文章是父级文档,评论是子级文档。便于我们对有父子关系或嵌套关系的数据进行标识与建模。
在以下样例中:我们在创建my-index-000001索引时,添加了一个Join字段类型的my_join_field字段,关系为"问题与答案"。我们插入了id为1的问题。同时创建了id为3的答案。我们指定了其父级文档的id为1。此时我们就可以理解为id为3的这条数据是id为1这条数据的子文档。也就是id为3的这条数据是id为1这条数据中所描述问题的答案。
代码语言:javascript复制PUT my-index-000001
{
"mappings": {
"properties": {
"my_id": {
"type": "keyword"
},
"my_join_field": {
"type": "join",
"relations": {
"question": "answer"
}
}
}
}
}
代码语言:javascript复制PUT my-index-000001/_doc/1?refresh
{
"my_id": "1",
"text": "This is a question",
"my_join_field": "question"
}
代码语言:javascript复制PUT my-index-000001/_doc/3?routing=1&refresh
{
"my_id": "3",
"text": "This is an answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
Structured data types
结构化数据类型,主要包含range,ip,version,murmur3.
Range
范围字段类型:用于表示字段的上限与下限的范围。例如:时间范围10月1日~10月15日。数字范围0~9。
其中包含:integer_range,float_range,long_range,double_range,date_range,ip_range
在以下样例中,我们在range_index中将expected_attendees设置为integer_range,将time_frame设置为date_range。然后我们插入了一条expected_attendees的大于等于10,小于20,time_frame大于等于"2015-10-31 12:00:00",小于等于"2015-11-01"的数据。
代码语言:javascript复制PUT range_index
{
"settings": {
"number_of_shards": 2
},
"mappings": {
"properties": {
"expected_attendees": {
"type": "integer_range"
},
"time_frame": {
"type": "date_range",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
PUT range_index/_doc/1?refresh
{
"expected_attendees" : {
"gte" : 10,
"lt" : 20
},
"time_frame" : {
"gte" : "2015-10-31 12:00:00",
"lte" : "2015-11-01"
}
}
然后我们可以在查询时,以range字段为条件,根据传入的value直接进行范围匹配,判断该值是否在range类型数据的范围内。
代码语言:javascript复制GET range_index/_search
{
"query" : {
"term" : {
"expected_attendees" : {
"value": 12
}
}
}
}
ip
ip类型:主要用于存储IPV4,IPV6类型的IP数据。
version
版本类型:主要用于记录软件的版本。
murmur3
哈希类型:用于存储计算中的hash值。
murmur3 哈希函数是一种快速、高效的哈希算法,用于将数据转换为固定长度的哈希值。
应用场景:
- 数据一致性检测:使用murmur3哈希函数来检查数据的一致性,通过数据哈希值比较来判断两条数据是否完全相同。
- 随机散列排序:murmur3函数生成的哈希值是随机的,可以用于对文档的随机化排序。在随机访问场景中较为便捷。
注意事项:
murmur3字段类型不适合直接存储元数据。只存储哈希值。
Aggregate data types
聚合字段类型:主要包含aggregate_mertric_double,histogram。
Histogram
histogram 字段类型:主要用于存储和分析数值数据的分布情况。它将数值范围划分为桶(buckets),并统计每个桶中的文档数量。
应用场景:
- 数值分布统计:使用该类型可以将数值划分为等宽的桶,并计算每个桶的数据量,便于了解数据分布情况。
- 直方图聚合:使用该类型可以执行直方图聚合,该聚合会将文档分组至不同的桶中,并计算每个桶的文档数量,生成直方图。
- 范围查询:使用该类型字段可以根据桶的范围来查询或过滤特定范围内的文档,不用对每个文档的数据进行比较。
text
文本字段类型:主要用于存储需要进行全文检索的数据。例如:文档内容,商品简介等信息。在搜索时需要配合分词器使用。分词器会根据词典与分词算法对文本进行切分,将一大段文本切分为若干个词项。当我们使用全文检索时,便于返回相关的结果。text字段不会用于聚合,大部分情况下也不会用于排序场景。
使用以下方式,我们可以将full_name的字段类型设置为text。此时该字段就可以被用于全文检索。
代码语言:javascript复制PUT my-index-000001
{
"mappings": {
"properties": {
"full_name": {
"type": "text"
}
}
}
}
当我们希望字段同时具备text与keyword的特点时,我们可以通过以下方式进行mapping设置:
代码语言:javascript复制PUT my-index-000001
{
"mappings": {
"properties": {
"my_field": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
Geopoint field
经纬度类型:主要用于存储经纬度坐标。一般多用于记录地理位置的经纬度。当我们需要针对数据绘制热力图,轨迹图等需要使用到经纬度坐标的图表时,我们就需要在索引中将相应字段设置为该类型。
我们可以使用以下方式将索引字段类型设置为geopoint。并提供了两种方式对经纬度类型的字段进行数据插入。
代码语言:javascript复制PUT my-index-000001
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
PUT my-index-000001/_doc/1
{
"text": "Geopoint as an object",
"location": {
"lat": 41.12,
"lon": -71.34
}
}
PUT my-index-000001/_doc/2
{
"text": "Geopoint as a string",
"location": "41.12,-71.34"
}
注意事项:
在使用geopoint类型时,使用字符串形式插入坐标时,需要按照lat,lon排序。如果使用坐标数组形式插入数据,则需要按照lon,lat形式插入数据。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!