除了我们提到的简单标量数据类型,JSON还有null值,数组,对象,这些Elasticsearch都是支持的。
多值域
很有可能,我们希望tag域包含多个标签。我们可以以数组的形式索引标签:
{ "tag": [ "search", "nosql" ]}
对于数组,没有特殊的映射需求。任何域都可以包含0,1或者多个值,就像全文域分析得到多个词条。
这暗示,数组中所有的值必须是相同数据类型的。你不能将日期和字符串混在一起。如果你通过索引数组来创建新的域,Elasticsearch会用数组中第一个值的数据类型作为这个域的类型。
NOTE
当你从Elasticsearch得到一个文档,每个数组的顺序和你当初索引文档时一样。你得到的_source域,包含与你索引的一模一样的JSON文档。
但是,数组是以多值域 索引的——可以搜索,但是无序的。在搜索的时候,你不能指定“第一个”或者“最后一个”。更确切的说,把数组想象成 装在袋子里的值。
空域
当然,数组可以为空。这相当于存在零值。事实上,在Lucene中是不能存储null 值的,所以我们认为存在null 值的域为空域。
下面三种域被认为是空的,他们将不会被索引:
"null_value": null,
"empty_array": [],
"array_with_null_value": [ null ]
多层级对象
我们讨论的最后一个JSON 原生数据类是 对象—在其他语言中称为哈希,哈希map,字典或者关联数组。
内部对象经常用于嵌入一个实体或对象到其他对象中。例如,与其在tweet 文档中包含 user_name和user_id 域,我们也可以这样写:
{
"tweet": "Elasticsearch is very flexible",
"user": {
"id": "@johnsmith",
"gender": "male",
"age": 26,
"name": {
"full": "John Smith",
"first": "John",
"last": "Smith"
}
}
}
内部对象的映射
Elasticsearch 会动态监测新的对象域并映射他们为对象,在properties属性下列出内部域:
{
"gb": {
"tweet": { //1
"properties": {
"tweet": { "type": "string" },
"user": { //2
"type": "object",
"properties": {
"id": { "type": "string" },
"gender": { "type": "string" },
"age": { "type": "long" },
"name": { //2
"type": "object",
"properties": {
"full": { "type": "string" },
"first": { "type": "string" },
"last": { "type": "string" }
}
}
}
}
}
}
}
}
- 根对象
- 内部对象
user 和name 域的映射结构域 tweet 类型的相同。事实上,type映射只是一种特殊的 对象映射,我们称之为 根对象。除了他有一些文档元数据的特殊顶级域,例如_source 和 _all 域,他和其他对象一样。
内部对象是如何索引的
Lucene 不理解内部对象。Lucene 文档是由一组键值对列表组成的。为了能让Elasticsearch有效的索引内部类,他把我们的文档转化成这样:
{
"tweet": [elasticsearch, flexible, very],
"user.id": [@johnsmith],
"user.gender": [male],
"user.age": [26],
"user.name.full": [john, smith],
"user.name.first": [john],
"user.name.last": [smith]
}
内部域可以通过名称引用(例如,first)。为了区分同名的两个域,我们可以使用全路径(例如,user.name.first)或type名加路径(tweet.user.name.first)。
NOTE
在前面简单扁平的文档中,没有user和user.name域。Lucene索引只有标量和简单值,没有复杂数据结构。
内部对象数组
最后,考虑包含内部对象的数组是如何被索引的。假设我们有个 followers 数组:
{
"followers": [
{ "age": 35, "name": "Mary White"},
{ "age": 26, "name": "Alex Jones"},
{ "age": 19, "name": "Lisa Smith"}
]
}
这个文档会像我们之前描述的那样被扁平化处理,结果如下所示:
{
"followers.age": [19, 26, 35],
"followers.name": [alex, jones, lisa, smith, mary, white]
}
{age:35} 和{name:Mary White} 之间的相关性已经丢失了,因为每个多值域只是一包无序的值,而不是有序数组。这足以让我们问,“有一个26岁的追随者?”
但是我们不能得到一个准确的答案:“是否有一个26岁名字叫做Alex Jones 的追随者?”
相关内部对象被称为nested 对象,可以回答上面的查询,我们稍后会在嵌套对象 中介绍他。
