Elasticsearch 7.x 映射(Mapping)中的字段类型

1 概念
Elasticsearch的映射用来定义一个索引中的文档如何被存储,定义一个映射类似于定义一个Mongo DB集合,在Elasticsearch 7.x中,映射和索引是一对一的关系。映射分为静态映射和动态映射,前者需要用户手动定义,后者则直接在向一个未生成的索引添加文档的时候自动生成。

1.1 动态映射
用户尝试向一个不存在的索引添加文档时,Elasticsearch会新建该索引并根据该文档字段的特性生成一个动态映射,其推断的字段映射类型和JSON文档字段类型的关系如下:

JSON类型    动态映射推断的映射类型
null    没有字段被添加,即不填加该映射
布尔型(true或者false)    boolean类型
浮点类型数字    单精度浮点型(float类型)
数字    长整型(long类型)
内嵌对象    object类型
数组    由数组的第一个非null值决定
字符串类型    根据字符串内容特征而定,有可能为text/keyword/double/long/date类型等
例如向Elasticsearch索引test-index添加以下文档:

{
  "id": 1,
  "name": "Trump",
  "height": 180.52,
  "man": true,
  "country": "USA",
  "born": "1946-6-14",
  "child": ["Donald John Trump", "Ivana Marie Trump", "Eric Frederick Trump"]
}



此时查询动态映射信息:

GET /test-index/_mapping
{
  "test-index" : {
    "mappings" : {
      "properties" : {
        "born" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "child" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "country" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "height" : {
          "type" : "float"
        },
        "id" : {
          "type" : "long"
        },
        "man" : {
          "type" : "boolean"
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}



其中id被推测为long类型,height被推测为float类型,man推测为boolean类型,其余字段为text类型。

1.2 静态映射
静态映射是用户在创建索引时手动指定映射,定义各个字段的属性,类似于MySQL的建表操作。
用户可以通过Rest API的PUT方法定义一个索引的映射,例如:

PUT test-index
{
  "mappings": {
    "properties": {
      "born": {
        "type": "text", 
        "fields": {
          "keyword": {
            "type": "keyword", 
            "ignore_above": 256
          }
        }
      }, 
      "child": {
        "type": "text", 
        "fields": {
          "keyword": {
            "type": "keyword", 
            "ignore_above": 256
          }
        }
      }, 
      "country": {
        "type": "text", 
        "fields": {
          "keyword": {
            "type": "keyword", 
            "ignore_above": 256
          }
        }
      }, 
      "height": {
        "type": "float"
      }, 
      "id": {
        "type": "long"
      }, 
      "man": {
        "type": "boolean"
      }, 
      "name": {
        "_all": {
          "enabled": false
        }, 
        "type": "text", 
        "fields": {
          "keyword": {
            "type": "keyword", 
            "ignore_above": 256
          }
        }
      }
    }
  }
}



2 字段类型
Elasticsearch字段类型类似于MySQL中的字段类型。Elasticsearch字段类型主要有:核心类型、复合类型、地理类型、特殊类型。

2.1 核心类型
核心类型可以划分为字符串类型、数字类型、日期类型、布尔类型、基于BASE64的二进制类型、范围类型:

类型    具体类型
字符串类型    text、keyword
数字类型    long、integer、short、byte、double、float、half_float、scaled_float
日期类型    date、date_nanos
布尔类型    boolean
二进制类型    binary
范围类型    range
字符串类型
Elasticsearch 7.x有两种字符串类型:text和keyword,在Elasticsearch 5.x之后string类型已经不再支持。

text
text类型适用于需要被全文检索的字段,例如新闻正文、邮件内容等比较长的文字。text类型会被Lucene分词器(Analyzer)处理为一个个词项,并使用Lucene倒排索引存储。text字段不能被用于排序。如果需要使用该类型的字段只需要在定义映射时指定JSON中对应字段的type为text。
keyword
keyword适合简短、结构化字符串,例如主机名、姓名、商品名称等,可以用于过滤、排序、聚合检索,也可以用于精确查询。
数字类型
数字类型分为long、integer、short、byte、double、float、half_float、scaled_float,其范围如下:

long,−263-2^{63}−2 
63
  到 263−12^{63} - 12 
63
 −1
integer,−231-2^{31}−2 
31
  到 231−12^{31} - 12 
31
 −1
short,−32768-32768−32768 到 327673276732767
byte,−128-128−128 到 127127127
double,IEEE 754标准双精度浮点类型,8字节
float,IEEE 754标准单精度浮点类型,4字节
half_float,IEEE 754标准半精度浮点类型,2字节
scaled_float,缩放类型浮点类型


数字类型的字段在满足需求的前提下应当尽量选择范围较小的数据类型,字段长度越短,搜索效率越高。对于浮点数,可以优先考虑使用scaled_float类型,该类型可以通过缩放因子来精确浮点数,12.34可以转换为1234来存储。

日期类型
在Elasticsearch中日期可以为以下形式:

格式化的日期字符串,例如2019-01-01 00:00、2019/01/01
时间戳(和1970-01-01 00:00:00 UTC的差值),单位毫秒或者秒
即使是格式化的日期字符串,Elasticsearch底层依然采用的是时间戳的形式存储。

布尔类型
JSON文档中同样存在布尔类型,不过JSON字符串类型也可以被Elasticsearch转换为布尔类型存储,前提是字符串的取值为"true"或者"false"。布尔类型常用于检索中的过滤条件。

二进制类型
二进制类型binary接受BASE64编码的字符串,默认store属性为false,并且不可以被搜索。

范围类型
范围类型可以用来表达一个数据的区间,可以分为5种:

integer_range,可以表示最大的范围为 [−231,231−1][-2^{31},2^{31}-1][−2 
31
 ,2 
31
 −1]
float_range,可以表达IEEE754单精度浮点数范围
long_range,可以表示最大的范围为 [−263,263−1][-2^{63},2^{63}-1][−2 
63
 ,2 
63
 −1]
double_range,可以表达IEEE754双精度浮点数范围
date_range,可以表达64位时间戳(单位毫秒)范围。
如果需要使用范围类型,同样需要手动定义映射,例如定义一个date_range的映射:

{
  "mappings": {
    "properties": {
      "time_area": {
        "type": "date_range",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}


然后添加一份文档:

{
  "time_area": {
    "gte": "2019-11-10 00:00:00",
    "lte": "2019-11-11 15:00:00"
  }
}


gte为区间最小值,lte为区间最大值。

2.2 复合类型
复合类型主要有array、object、nested

数组类型
因为Lucene底层并不真正支持数组类型,所以自然Elasticsearch也没有专用的数组类型。对于文档中的数组而言,每个元素必须是同一种类型。例如:[1,2,3]、["a","b","c"]、[{"name":"Trump"},{"name":"Smith"}]是合法的。

之前提到动态映射会以数组的第一个元素的类型来决定整个数组的类型,如果是空数组,那么会当成null来处理,即忽略该数组字段,不会创建该字段的映射。

object类型
JSON字符串允许嵌套对象,一个文档可以嵌套多个、多层对象。可以通过object类型来存储二级文档,不过由于Lucene并没有内部对象的概念,Elasticsearch会将原JSON文档扁平化,例如文档:

{
    "name": {
        "first": "Donald",
        "last": "Trump"
    }
}



实际上Elasticsearch会将其转换为以下格式,并通过Lucene存储,即使name是object类型:

{
    "name.first": "Donald",
    "name.last": "Trump"
}



对应的动态映射为:

{
  "mappings": {
    "properties": {
      "name": {
        "properties": {
          "first": {"type":"text"},
          "last": {"type":"text"}
        }
      }
    }
  }
}



nested类型
nested类型可以看成是一个特殊的object类型,可以让对象数组独立检索。例如文档:

{
  "group": "a group",
  "member": [
    { "first": "Donald", "last": "Trump"},
    { "first": "Barack", "last": "Obama"},
    { "first": "William", "last": "Clinton"}
  ]
}



member字段是一个JSON数组,并且每个数组对象都是一个JSON对象。如果将member设置为object类型,那么Elasticsearch会将其转换为:

{
  "group": "a group",
  "member.first": ["Donald", "Barack", "William"],
  "member.last": ["Trump", "Obama", "Clinton"]
}



可以看出转换后的JSON文档中first和last的关联丢失了,如果尝试搜索first为Donald,last为Obama的文档,那么成功会检索出上述文档,但是Donald和Obama在原JSON文档中并不属于同一个JSON对象,应当是不匹配的,即检索不出任何结果。nested类型就是为了解决这种问题的。nested类型将数组中的每个JSON对象作为独立的隐藏文档来存储,每个嵌套的对象都能够独立地被搜索,所以上述案例中虽然表面上只有1个文档,但实际上是存储了4个文档。
将member字段改为nested后,检索first为Donald,last为Obama的文档是就会提示不存在。

2.3 地理类型
地理类型字段分为两种:经纬度类型和地理区域类型。

经纬度类型
经纬度类型字段(geo_point)可以存储经纬度相关信息。通过地理类型的字段,可以用来实现诸如查找在指定地理区域内相关的文档、根据距离排序、根据地理位置修改评分规则等需求。

如果需要使用到经纬度类型,需要手动定义映射:

{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      }
    }
  }
}


geo_point接收以下地理位置数据:

经纬度键值对,lat为纬度,lon为经度

{
  "location": {
    "lat": 30.0,
    "lon": -50.0
  }
}



用逗号分割的字符串,前者为纬度,后者为经度
{
  "location": "30.0,-50.0"
}

数组形式座标
{
  "location": [30.0,-50.0]
}

地理区域类型
经纬度类型可以表达一个点,而geo_shape类型可以表达一块地理区域,区域的形状可以是任意多边形,也可以是点、线、面、多点、多线、多面等几何类型。GeoJSON一种表达地理区域的编码规范,具体可参考文档 《Geo格式规范说明》。

2.4 特殊类型
IP类型
IP类型的字段可以用来存储IPv4或者IPv6地址,如果需要存储IP类型的字段,需要手动定义映射:

{
  "mappings": {
    "properties": {
      "my_ip": {
        "type": "ip"
      }
    }
  }
}



然后添加文档:

{
  "my_ip": "10.0.0.1"
}

join类型
join类型是Elasticsearch 6.x引入的类型,以取代淘汰的_parent元字段。用来实现文档的一对一、一对多的关系。
join类型的Mapping如下:

PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": { 
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}



my_join_field为join类型字段的名称
relations指定关系:question是answer的父类
例如定义一个ID为1父文档:

PUT my_join_index/1?refresh
{
  "text": "This is a question",
  "my_join_field": "question" 
}

接下来定义一个子文档:

PUT my_join_index/_doc/3?routing=1&refresh 
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}



该文档指定了父文档ID为1。

更多特殊类型
参考官方文档

3 元字段
元字段是映射中描述文档本身的字段,所有的元字段类型都以下划线开头,元字段可以分为:

文档属性的元字段

字段名    作用
_index    文档所属的索引
_type    文档类型,在ES 7以上版本中类型已被废弃,所以该值一律为_doc
_id    文档唯一ID
源文档的元字段

字段名    作用
_source    文档原始JSON字符串
_size    _source字段的大小
索引的元字段

字段名    作用
_all    包含索引全部字段的超级字段
_field_names    文档中包含非空值的所有字段
路由的元字段

字段名    作用
_routing    将文档路由到特定分片的自定义路由值
自定义元字段:meta,用于自定义元数据。

3.1 _index字段
_index字段为文档所在的索引名称,它是一个虚拟字段,并不会实际存储在Lucene索引中。利用该字段可以用来执行多索引查询,可以通过_index进行term查询、聚合查询、terms查询等,但不支持prefix、regexp、fuzzy、wildcard查询。

terms查询示例如下:

GET index1,index2/_serach
{
  "query": {
    "terms": {
      "_index": ["index1", "index2"]
    }
  },
  "aggs": {
    "indices": {
      "terms": {
        "field": "_index",
        "size": 10
      }
    }
  },
  "sort": [{"_index": {"order": "asc"}}]
}



3.2 _id和_type字段
每条被索引的文档都有一个_type字段和_id字段。前者为类型字段,后者为ID字段,用来标识唯一的在Elasticsearch 5.X版本以前,每个索引可以建立多个类型,到Elasticsearch 6.X后,只能建立一个类型,到7.X之后类型概念被取消,_type字段统一为_doc。

_id字段用于标识索引中唯一的文档,可以用于term、terms、match、query_string、simple_query_string查询,不能用于聚合、脚本和排序。

3.3 _source字段
_source存储文档的原始JSON字符串,update、update_by_query、重建索引、高亮关键字、索引数据备份、修改mapping等一系列操作都需要用到_source。虽然可以在定义mapping时不存储_source,不过最好不要这么做。

3.4 _all字段
_all字段是一个把文档中其它字段拼接在一起之后的字段,使用空格分隔,_all字段会被解析和索引但是不会存储。_all字段典型的用途就是在不知道具体字段的前提下,查找包含某个关键字的文档,例如:

GET index1/_search
{
  "query": {
    "match": {
      "_all": "apple"
    }
  }
}


3.5 _field_names字段
_field_names存储文档中所有非空字段的名字,常用于检索存在某个指定字段的文档:

GET index1/_search
{
  "query": {
    "match": {
      "_field_names": "name"
    }
  }
}



上述示例是查询出具有name字段的文档。

3.6 _routing
Elasticsearch通过以下公式来计算文档应该分配到哪个分片上:
shard_num = hash(_routing) % num_primary_shards

默认的_routing值是文档的_id字段,指定_routing参数可以设置自定义路由,例如:

PUT my_index/_doc/1?routing=user1&refresh=true 
{
  "title": "This is a document"
}



上述示例中指定routing为user1
————————————————
版权声明:本文为CSDN博主「A__Plus」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/abc123lzf/article/details/102957060

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章