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的二进制类型、范围类型:

类型 具体类型
字符串类型 textkeyword
数字类型 longintegershortbytedoublefloathalf_floatscaled_float
日期类型 datedate_nanos
布尔类型 boolean
二进制类型 binary
范围类型 range
字符串类型

Elasticsearch 7.x有两种字符串类型:textkeyword,在Elasticsearch 5.x之后string类型已经不再支持。

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

数字类型分为longintegershortbytedoublefloathalf_floatscaled_float,其范围如下:

  • long 2 63 -2^{63} 2 63 1 2^{63} - 1
  • integer 2 31 -2^{31} 2 31 1 2^{31} - 1
  • short 32768 -32768 32767 32767
  • byte 128 -128 127 127
  • 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:002019/01/01
  • 时间戳(和1970-01-01 00:00:00 UTC的差值),单位毫秒或者秒

即使是格式化的日期字符串,Elasticsearch底层依然采用的是时间戳的形式存储。

布尔类型

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

二进制类型

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

范围类型

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

  • integer_range,可以表示最大的范围为 [ 2 31 , 2 31 1 ] [-2^{31},2^{31}-1]
  • float_range,可以表达IEEE754单精度浮点数范围
  • long_range,可以表示最大的范围为 [ 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 复合类型

复合类型主要有arrayobjectnested

数组类型

因为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存储,即使nameobject类型:

{
	"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文档中firstlast的关联丢失了,如果尝试搜索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_fieldjoin类型字段的名称
  • relations指定关系:questionanswer的父类

例如定义一个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"
}

上述示例中指定routinguser1

参考资料

Elasticsearch 7.4 官方文档

发布了117 篇原创文章 · 获赞 96 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/abc123lzf/article/details/102957060
今日推荐