1.类型和映射
类型 在 Elasticsearch 中表示一类相似的文档。 类型由 名称 —比如 user
或 blogpost
—和 映射 组成。
映射, 就像数据库中的 schema ,描述了文档可能具有的字段或 属性 、每个字段的数据类型—比如 string
, integer
或 date
—以及Lucene是如何索引和存储这些字段的。
类型可以很好的抽象划分相似但不相同的数据。但由于 Lucene 的处理方式,类型的使用有些限制。
1.1Lucene 如何处理文档
在 Lucene 中,一个文档由一组简单的键值对组成。 每个字段都可以有多个值,但至少要有一个值。 类似的,一个字符串可以通过分析过程转化为多个值。Lucene 不关心这些值是字符串、数字或日期—所有的值都被当做 不透明字节 。
当我们在 Lucene 中索引一个文档时,每个字段的值都被添加到相关字段的倒排索引中。你也可以将未处理的原始数据 存储 起来,以便这些原始数据在之后也可以被检索到。
1.2类型是如何实现的
Elasticsearch 类型是以 Lucene 处理文档的这个方式为基础来实现的。一个索引可以有多个类型,这些类型的文档可以存储在相同的索引中。
Lucene 没有文档类型的概念,每个文档的类型名被存储在一个叫 _type
的元数据字段上。 当我们要检索某个类型的文档时, Elasticsearch 通过在 _type
字段上使用过滤器限制只返回这个类型的文档。
Lucene 也没有映射的概念。 映射是 Elasticsearch 将复杂 JSON 文档 映射 成 Lucene 需要的扁平化数据的方式。
1.3避免类型陷阱
这导致了一个有趣的思想实验: 如果有两个不同的类型,每个类型都有同名的字段,但映射不同(例如:一个是字符串一个是数字),将会出现什么情况?
简单回答是,Elasticsearch 不会允许你定义这个映射。当你配置这个映射时,将会出现异常。
详细回答是,每个 Lucene 索引中的所有字段都包含一个单一的、扁平的模式。一个特定字段可以映射成 string 类型也可以是 number 类型,但是不能两者兼具。因为类型是 Elasticsearch 添加的 优于 Lucene 的额外机制(以元数据 _type
字段的形式),在 Elasticsearch 中的所有类型最终都共享相同的映射。
以 data
索引中两种类型的映射为例:
{
"data": {
"mappings": {
"people": {
"properties": {
"name": {
"type": "string",
},
"address": {
"type": "string"
}
}
},
"transactions": {
"properties": {
"timestamp": {
"type": "date",
"format": "strict_date_optional_time"
},
"message": {
"type": "string"
}
}
}
}
}
}
每个类型定义两个字段 (分别是 "name"
/"address"
和 "timestamp"
/"message"
)。它们看起来是相互独立的,但在后台 Lucene 将创建一个映射,如:
{
"data": {
"mappings": {
"_type": {
"type": "string",
"index": "not_analyzed"
},
"name": {
"type": "string"
}
"address": {
"type": "string"
}
"timestamp": {
"type": "long"
}
"message": {
"type": "string"
}
}
}
}
1.4类型结论
那么,这个讨论的结论是什么?技术上讲,多个类型可以在相同的索引中存在,只要它们的字段不冲突(要么因为字段是互为独占模式,要么因为它们共享相同的字段)。
重要的一点是: 类型可以很好的区分同一个集合中的不同细分。在不同的细分中数据的整体模式是相同的(或相似的)。
类型不适合 完全不同类型的数据 。如果两个类型的字段集是互不相同的,这就意味着索引中将有一半的数据是空的(字段将是 稀疏的 ),最终将导致性能问题。在这种情况下,最好是使用两个单独的索引。
2.根对象
映射的最高一层被称为 根对象 ,它可能包含下面几项:
- 一个 properties 节点,列出了文档中可能包含的每个字段的映射
- 各种元数据字段,它们都以一个下划线开头,例如
_type
、_id
和_source
- 设置项,控制如何动态处理新的字段,例如
analyzer
、dynamic_date_formats
和dynamic_templates
- 其他设置,可以同时应用在根对象和其他
object
类型的字段上,例如enabled
、dynamic
和include_in_all
2.1属性
我们已经在 核心简单域类型 和 复杂核心域类型 章节中介绍过文档字段和属性的三个最重要的设置:
type
字段的数据类型,例如 string
或 date
index
字段是否应当被当成全文来搜索( analyzed
),或被当成一个准确的值( not_analyzed
),还是完全不可被搜索( no
)
analyzer
确定在索引和搜索时全文字段使用的 analyzer
2.2元数据: _source 字段
默认地,Elasticsearch 在 _source
字段存储代表文档体的JSON字符串。和所有被存储的字段一样, _source
字段在被写入磁盘之前先会被压缩。
这个字段的存储几乎总是我们想要的,因为它意味着下面的这些:
- 搜索结果包括了整个可用的文档——不需要额外的从另一个的数据仓库来取文档。
- 如果没有
_source
字段,部分update
请求不会生效。 - 当你的映射改变时,你需要重新索引你的数据,有了_source字段你可以直接从Elasticsearch这样做,而不必从另一个(通常是速度更慢的)数据仓库取回你的所有文档。
- 当你不需要看到整个文档时,单个字段可以从
_source
字段提取和通过get
或者search
请求返回。 - 调试查询语句更加简单,因为你可以直接看到每个文档包括什么,而不是从一列id猜测它们的内容。
在一个搜索请求里,你可以通过在请求体中指定 _source
参数,来达到只获取特定的字段的效果:
GET /_search
{
"query": { "match_all": {}},
"_source": [ "title", "created" ]
}
这些字段的值会从 _source
字段被提取和返回,而不是返回整个 _source
。
2.3元数据: _all 字段
在 轻量 搜索 中,我们介绍了 _all
字段:一个把其它字段值当作一个大字符串来索引的特殊字段。 query_string
查询子句(搜索 ?q=john
)在没有指定字段时默认使用 _all
字段。
_all
字段在新应用的探索阶段,当你还不清楚文档的最终结构时是比较有用的。你可以使用这个字段来做任何查询,并且有很大可能找到需要的文档:
GET /_search
{
"match": {
"_all": "john smith marketing"
}
}
如果你不再需要 _all
字段,你可以通过下面的映射来禁用:
PUT /my_index/_mapping/my_type
{
"my_type": {
"_all": { "enabled": false }
}
}
2.4元数据:文档标识
文档标识与四个元数据字段相关:
_id
:文档的 ID 字符串
_type
:文档的类型名
_index
:文档所在的索引
_uid:_type
和 _id
连接在一起构造成 type#id
默认情况下, _uid
字段是被存储(可取回)和索引(可搜索)的。 _type
字段被索引但是没有存储, _id
和 _index
字段则既没有被索引也没有被存储,这意味着它们并不是真实存在的。
备注:文章参考Elasticsearch: 权威指南
https://www.elastic.co/guide/cn/elasticsearch/guide/current/mapping.html