数字类型
数字类型分为 long、integer、short、byte、double、float、half_float、scaled_float。
数字类型的字段在满足需求的前提下应当尽量选择范围较小的数据类型,字段长度越短,搜索效率越高,对于浮点数,可以优先考虑使用 scaled_float 类型,该类型可以通过缩放因子来精确浮点数,例如 12.34 可以转换为 1234 来存储。
日期类型
在 ES 中日期可以为以下形式:
格式化的日期字符串,date类型,例如 2020-03-17 00:00、2020/03/17
时间戳(和 1970-01-01 00:00:00 UTC 的差值),date_nanos类型,单位毫秒或者秒
即使是格式化的日期字符串,ES 底层依然采用的是时间戳的形式存储。
布尔类型
JSON 文档中同样存在布尔类型,不过 JSON 字符串类型也可以被 ES 转换为布尔类型存储,前提是字符串的取值为 true 或者 false,布尔类型常用于检索中的过滤条件。
二进制类型
二进制类型 binary 接受 BASE64 编码的字符串,默认 store 属性为 false,并且不可以被搜索。
范围类型
范围类型可以用来表达一个数据的区间,可以分为5种:integer_range、float_range、long_range、double_range 以及 date_range。
复杂类型
复合类型主要有对象类型(object)和嵌套类型(nested):
对象类型
JSON 字符串允许嵌套对象,一个文档可以嵌套多个、多层对象。可以通过对象类型来存储二级文档,不过由于 Lucene 并没有内部对象的概念,ES 会将原 JSON 文档扁平化,例如文档:
{ "name": { "first": "wu", "last": "px" } }
实际上 ES 会将其转换为以下格式,并通过 Lucene 存储,即使 name 是 object 类型:
{ "name.first": "wu", "name.last": "px" }
嵌套类型
嵌套类型可以看成是一个特殊的对象类型,可以让对象数组独立检索,例如文档:
{ "group": "users", "username": [ { "first": "wu", "last": "px"}, { "first": "hu", "last": "xy"}, { "first": "wu", "last": "mx"} ] }
username 字段是一个 JSON 数组,并且每个数组对象都是一个 JSON 对象。如果将 username 设置为对象类型,那么 ES 会将其转换为:
{ "group": "users", "username.first": ["wu", "hu", "wu"], "username.last": ["px", "xy", "mx"] }
可以看出转换后的 JSON 文档中 first 和 last 的关联丢失了,如果尝试搜索 first 为 wu,last 为 xy 的文档,那么成功会检索出上述文档,但是 wu 和 xy 在原 JSON 文档中并不属于同一个 JSON 对象,应当是不匹配的,即检索不出任何结果。
嵌套类型就是为了解决这种问题的,嵌套类型将数组中的每个 JSON 对象作为独立的隐藏文档来存储,每个嵌套的对象都能够独立地被搜索,所以上述案例中虽然表面上只有 1 个文档,但实际上是存储了 4 个文档。
地理类型
地理类型字段分为两种:经纬度类型和地理区域类型:
经纬度类型
经纬度类型字段(geo_point)可以存储经纬度相关信息,通过地理类型的字段,可以用来实现诸如查找在指定地理区域内相关的文档、根据距离排序、根据地理位置修改评分规则等需求。
地理区域类型
经纬度类型可以表达一个点,而 geo_shape 类型可以表达一块地理区域,区域的形状可以是任意多边形,也可以是点、线、面、多点、多线、多面等几何类型。
特殊类型
特殊类型包括 IP 类型、过滤器类型、Join 类型、别名类型等。特殊类型可以查看官方文档。
字段的公共属性:
- index:该属性控制字段是否编入索引被搜索,该属性共有三个有效值:analyzed、no和not_analyzed:
analyzed:(默认属性)表示该字段被分析,编入索引,产生的token能被搜索到;
not_analyzed:表示该字段不会被分析,使用原始值编入索引,在索引中作为单个词;
no:不编入索引,无法搜索该字段;
其中analyzed是分析,分解的意思,默认值是analyzed,表示将该字段编入索引,以供搜索。 - store:指定是否将字段的原始值写入索引,默认值是no,字段值被分析,能够被搜索,但是,字段值不会存储,这意味着,该字段能够被查询,但是不会存储字段的原始值。
- boost:字段级别的助推,默认值是1,定义了字段在文档中的重要性/权重;
- include_in_all:该属性指定当前字段是否包括在_all字段中,默认值是ture,所有的字段都会包含_all字段中;如果index=no,那么属性include_in_all无效,这意味着当前字段无法包含在_all字段中。
- copy_to:该属性指定一个字段名称,ElasticSearch引擎将当前字段的值复制到该属性指定的字段中;
- doc_values:文档值是存储在硬盘上的索引时(indexing time)数据结构,对于not_analyzed字段,默认值是true,analyzed string字段不支持文档值;
- fielddata:字段数据是存储在内存中的查询时(querying time)数据结构,只支持analyzed string字段;
- null_value:该属性指定一个值,当字段的值为NULL时,该字段使用null_value代替NULL值;在ElasticSearch中,NULL 值不能被索引和搜索,当一个字段设置为NULL值,ElasticSearch引擎认为该字段没有任何值,使用该属性为NULL字段设置一个指定的值,使该字段能够被索引和搜索。
字符串类型常用的其他属性
- analyzer:该属性定义用于建立索引和搜索的分析器名称,默认值是全局定义的分析器名称,该属性可以引用在配置结点(settings)中自定义的分析器;
- search_analyzer:该属性定义的分析器,用于处理发送到特定字段的查询字符串;
- ignore_above:该属性指定一个整数值,当字符串字段(analyzed string field)的字节数量大于该数值之后,超过长度的部分字符数据将不能被analyzer处理,不能被编入索引;对于 not analyzed string字段,超过长度的部分字符将被忽略,不会被编入索引。默认值是0,禁用该属性;
- position_increment_gap:该属性指定在相同词的位置上增加的gap,默认值是100;
- index_options:索引选项控制添加到倒排索引(Inverted Index)的信息,这些信息用于搜索(Search)和高亮显示:
- docs:只索引文档编号(Doc Number)
- freqs:索引文档编号和词频率(term frequency)
- positions:索引文档编号,词频率和词位置(序号)
- offsets:索引文档编号,词频率,词偏移量(开始和结束位置)和词位置(序号)
默认情况下,被分析的字符串(analyzed string)字段使用positions,其他字段使用docs;
dynamic
刚开始使用ES的时候,有一次在调用ES写入数据时,不小心将index_name写错,发现程序并没有报错,并且运行完成后,数据成功写入了错误的index_name里去。这时候我就有以下疑问:
1、为什么ES会自动创建index
2、写入数据完成后,查看当前index的mapping,发现已经根据写入数据的类型自动识别并创建。在没有指明数据结构以及数据类型的情况下,ES为何可以写入数据。其自动创建mapping的依据是什么
1、为什么ES会自动创建index
ES中有一个配置:auto_create_index。用来控制当数据写入时索引不存在,是否可以自动创建索引。默认打开。
get ip:port/_cluster/settings { "persistent": { "action": { "auto_create_index": "true",//当前配置说明此ES集群允许自动创建索引,如果没有出现此配置项,则默认为true。 "destructive_requires_name": "true" }, "search": { "max_buckets": "5000000" } }, "transient": {} }
一般不建议开启自动创建索引,因为会引起索引太多、索引Mapping和Setting不符合预期等问题。可以通过以下请求关闭
PUT ip:port/_cluster/settings { "persistent" : { "action": { "auto_create_index": "false" } } }
2、ES自动写入数据的依据
在创建索引时,我们首先需要指定mapping的映射模式,映射模式由dynamic属性确认。mapping映射模式分为以下三种:
动态映射
动态映射(dynamic mapping):dynamic=true
索引文档前不需要创建索引、类型等信息,在索引的同时会自动完成索引、数据类型的识别、映射的创建。索引创建时,默认会开启动态映射。
创建一个mapping为空的index,名称为liqifeng
PUT ip:port/liqifeng/ { "mappings": { } }
查看当前mapping
GET ip:port/liqifeng/_mapping { "liqifeng": { "mappings": {} } }
上传一条数据
PUT ip:port/liqifeng/_doc/1 { "name": "小白", "age": 16, "sex": "不详" }
查看此时mapping,可以看到已经根据上传的数据自动构建了字段索引信息。
{ "liqifeng": { "mappings": { "properties": { "age": { "type": "long" }, "name": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "sex": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } }