1.文档字段存储
文档字段指的是一个文档中存储的单元,比如以下文档中的username、age、favor就是一个字段名称,而对应的值则为存储的字段内容。
{
"username":"arong2048",
"age":22,
"favor":"唱跳、Rap、篮球我都不喜欢"
}
文档的具体内容都以字段为单元保存,所以字段决定了文档将以什么样的方式存储和索引。
索引文档和存储文档是两个不同的概念,索引文档是将文档编入倒排索引,而存储文档则是将文档在物理上保存起来。关于倒排索引会在下一次文章中介绍,本次只介绍文档字段的存储、映射及类型。
由于文档中的数据分散在各个字段中,所以索引文档肯定都是针对文档字段进行的。一份文档一般会有多个字段,所以倒排索引一般是多个相互关联的倒排索引。前面所说的索引文档应该是以字段为单位对文档做索引,而并非以整个文档内容做索引。
对于text类型的字段来说,它们会被解析为词项后再以词项为单位编入倒排索引,编入索引的信息包括:文档ID、词项在字段中出现的频率、词项在字段中的次序、词项在字段中的起止偏移量等信息。
- 词项(token):一个完整的text文本被分词器分词后获取的一个个单元化的词
- 文档ID:词项来源文档的编号,是文档的惟一标识,可用于存在性检索。文档ID由文档中的元字段_id保存
- 词频:词项在字段中出现的频率一般称为词频,它可以反映检索结果的相关性,词项在文本中出现的频率越高与检索的相关性也就越高
- 词序(position):词项在字段中的次序记录的是某一词项在所有词项中的次序
- 偏移量(start_offset、end_offset):词项在字段中的起止偏移量给出了词项在字段中的实际位置,一般用于高亮检索结果
比如使用默认分词器来存储Elasticsearch is a search engine
这句话,通过GET _analyze命令指定默认分词器即可查看分词信息存储结果:
GET _analyze
{
"analyzer":"standard",
"text":"Elasticsearch is a search engine"
}
以“Elasticsearch is a search engine”这段文本为例,文档分析后会提取出5个词项并编入索引。如果这5个词项都在索引中保留字段的原始值,那么这段文本就要被保留5次。而对于很多文档来说,它们的文本内容要比这大得多,如果都保留下来这对于存储空间的浪费将是十分惊人的。
所以索引提供了一个叫_source的字段用于存储整个文档的原始值。_source字段有一个特性,那就是这个字段在默认情况下是不会被索引的,但是每个查询默认都会带着_source字段返回。
_source字段保存的源文档信息是在索引文档时以JSON形式传递过来的最原始文档,这在查看文档时比较直观方便,并且在使用高亮查询时,也会在_source字段的原文中体现。
2.字段类型
通过映射类型的properties字段,可以定义映射类型包含的字段及其数据类型。Elasticsearch支持的数据类型包括字符串、数值、日期、布尔、二进制、范围等核心数据类型,还支持数组、对象等衍生类型,也支持嵌套、关联、地理信息等特殊类型。
2.1.字段的核心类型
核心数据类型是字段数据类型的基础,它们涵盖了大多数文档字段的应用场景。核心类型可以分为字符串、数值、日期、布尔等几种大的类型:
1.字符串类型
字符串类型包括text和keyword两种类型,一些特性及区别如下:
- text类型在存储前会做词项分析,而keyword类型则不会
- text类型的字段可以通过analyzer参数设置该字段的分析器,而keyword类型字段则没有这个参数
- text类型字段在编入索引后可通过词项做检索,但不能通过字段整体值做检索,而keyword类型字段只能通过字段整体值来做检索而不能用词项做检索
- text类型的字段一般用于存储全文数据,比如日志信息、文章正文、邮件内容等,keyword类型则用于存储结构化的文本数据,如邮编、地址、电话等
- text类型存储的是全文本数据,所以它编入索引的信息包括文档ID、词频、词序等信息,而keyword类型则只编入文档ID
在存储方面,keyword类型默认就支持通过文档值机制保存字段原始值,而text类型则不支持文档值机制,所以text类型不能参与文档排序、过滤、聚集等操作,除非打开它的fielddata机制,以将各词项加载到内存中记录。
2.数字类型
Elasticsearch支持包括整型、浮点类型在内的8种数值类型,它们的主要区别体现的数值精确度上:
3.日期类型
Elasticsearch有两种日期类型,分别是date和date_nanos。由于JSON并没有日期类型,所以这两种类型在文档中表现出来的仍然是带有日期格式的字符串。但在Elasticsearch内部存储它们时,会将它们转换为该日期与计算机纪元(1970年1月1日0点)的时间差值。其中,date类型会按毫秒计算差值,而date_nanos则会按纳秒计算差值。
4.布尔类型
布尔类型的类型关键字是boolean,它只有两个值,即true和false。但也接收以字符串描述的true和false,即使用 "true" 和 "false" 也是可以的。
5.范围类型
范围类型是ElasticSearch中的一种自有类型,它要求字段的值描述的是一个数值、日期或IP地址的范围,添加文档时可以使用gte、gt、lt、lte分别表示大于等于、大于、小于、小于等于。数
值范围类型包括integer_range、foat_range、long_range、double_range,日期范围类型和IP范围类型分别为date_range和ip_range。
2.2.衍生及多值类型
衍生类型从核心类型衍生而来,包括数组和对象两种。衍生类型并不是独立的数据类型,因为它们并不像核心数据类型那样有专门的类型名称,而是通过特定的JSON格式确认它们的类型。
1.数组类型
如果要定义一个字段为数组类型,不需要使用类似array这样的名称声明它的类型,而是通过在添加文档时使用“[]”来确认该字段为数组。
Elasticsearch没有定义array这种数据类型,数组是在核心数据类型基础上的一种扩展。需要在建立字段映射时将其指定为某种核心数据类型,那么它就可以接收以“[]”表示的数组,限制就是数组中的元素必须是同一种类型,或者它们至少可以转换为同一种类型。
2.对象类型
与数组类似,Elasticsearch中也没有定义object这种数据类型,它是在添加文档时使用“{}”的格式来确认字段类型为对象。
在建立对象的映射配置时,可以通过嵌套的方式去逐层声明字段的类型,比如colleges文档中的address字段是一个对象:
{
"address":{
"conntry":"china",
"city":"guangdong"
}
}
可以用以下方式去声明对象字段的映射:
3.多值类型
有些文档字段可能经常会以不同的方式检索,如果文档字段只以一种方式编入索引,检索性能就会受到影响。
比如文章的标题,在多数情况下可能是通过文章标题中的词项做检索,但在标题比较短并且知道整个标题内容时也是有可能使用整个标题做检索的。如果将标题字段的类型设置为text,那么标题在编入索引时就会被提取词项而不能使用整个标题做检索,而如果设置为keyword则不能使用词项做检索。并且开启fileddata机制又十分耗费内存和性能,所以出现了多值类型, 可以对一个字段制定多个类型。
针对字符串类型text和keyword,Elasticsearch专门提供了一个用于配置字段多数据类型的参数fields,它能让一个字段同时具备两种数据类型的特征。
按下述操作,可以将title字段的类型被设置为text,同时通过fields参数又为该字段添加了两个子字段:
- 子字段名称为raw,它的类型被设置为keyword类型
- 子字段名称为length,它的类型则为token_count
使用fields设置的子字段,在添加文档时不需要单独设置值,它们与title共享相同的字段值,只是会以不同方式处理字段值。同样,在检索时,它们也不会单独显示在结果中。所以它们一般只是在检索中以查询条件的形式出现,以减少检索时的性能开销。
例如对于title.raw来说,title字段在编入索引时会将字段值做分析并提取词项,而title.raw则按keyword类型将整个值编入索引。所以如果需要根据词项做检索时应使用title字段,而如果需要使用整个值做检索或是在排序和聚集时则可以使用title.raw字段。在默认情况下,如果没有明确定义字符串类型时,添加到索引中的字符串都会以上述操作设置为多值类型。