一个blog
可能对应于很多的comments
,或者一个员工对应于很多的经验。,
这种数据就是关系数据。
object数据类型
json文档本质上市分成的:文档可能包含内部对象,而内部对象又可能包含内部对象的本身;
- 外部文档也是一个
json
对象 - 它包含一个名为
manager
的内部对象 - 其中又包含一个名为
name
的内部对象
在elasticsearch
内部,该文档被索引为一个简单的键值对应列表,如下所示:
{
"region": "US",
"manager.age": 30,
"manager.name.first": "John",
"manager.name.last": "Smith"
}
接下来,我们首选创建一个叫做developer
的索引,并输入如下的两个数据:
POST developer/_doc/101
{
"name": "zhang san",
"skills": [
{
"language": "ruby",
"level": "expert"
},
{
"language": "javascript",
"level": "beginner"
}
]
}
POST developer/_doc/102
{
"name": "li si",
"skills": [
{
"language": "ruby",
"level": "beginner"
}
]
}
object query
这个时候我们想搜一个skill:language
是ruby
,并且level
是beginner
的文档。
我们可能想到的办法是:
GET developer/_search
{
"query": {
"bool": {
"filter": [
{
"match": {
"skills.language": "ruby"
}
},
{
"match": {
"skills.level": "beginner"
}
}
]
}
}
}
通过上面的搜寻,我们得到的结果是:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "developer",
"_type" : "_doc",
"_id" : "101",
"_score" : 0.0,
"_source" : {
"name" : "zhang san",
"skills" : [
{
"language" : "ruby",
"level" : "expert"
},
{
"language" : "javascript",
"level" : "beginner"
}
]
}
},
{
"_index" : "developer",
"_type" : "_doc",
"_id" : "102",
"_score" : 0.0,
"_source" : {
"name" : "li si",
"skills" : [
{
"language" : "ruby",
"level" : "beginner"
}
]
}
}
]
}
}
可以看到,我们得到两个结果。
但是我们仔细查看以下发现得到的结果并不是我们想得到的。
从原意来说,我们想得到的是li si
,因为只有li si
这个人的language
是ruby
,并且他的level
是beginner
zhang san
这个文档,应该不在搜寻之列。这是为什么呢?
原来,language
及level
是skill
的json
内部数组项。
当json
对象被lucene
扁平化后,我们失去了language
和level
之间的对应关系
取而代之的是如下的这种关系:
{
"name": "zhang san",
"skills.language" :["ruby", "javascript"],
"skills.level": ["expert", "beginner"]
}
如上所示,我们看到的是一个扁平化的数组。
之前的那种language
和level
之间的对应关系已经不存在了。
object aggregation
同样的问题也存在于aggregation
中,比如我们想做以下的aggregation
GET developer/_search
{
"size": 0,
"aggs": {
"languages": {
"terms": {
"field": "skills.language.keyword"
},
"aggs": {
"level": {
"terms": {"field": "skills.level.keyword"}
}
}
}
}
}
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"languages" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "ruby",
"doc_count" : 2,
"level" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "beginner",
"doc_count" : 2
},
{
"key" : "expert",
"doc_count" : 1
}
]
}
},
{
"key" : "javascript",
"doc_count" : 1,
"level" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "beginner",
"doc_count" : 1
},
{
"key" : "expert",
"doc_count" : 1
}
]
}
}
]
}
}
}
显然,对于 key javascript
来说,它并没有 expert
对应的 level
,但是在我们的 aggregation
里显示出来了。
这个结果显然是错误的。
nested数据类型
nested
类型是object
数据类型的特殊版本,它允许对象数组以一种可以彼此独立查询的方式进行索引。
在内部,嵌套对象将数组中的每个对象索引为单独的隐藏文档,这意味着每个嵌套对象都可以使用nested query
独立于其他对象的查询。
每个nested
对象都被索引为一个单独的lucene
文档。
nested
数据类型能够让我们对object
数组建立索引,并且分别进行查询
如果需要维护数组中每个对象的关系,请使用nested数据类型
为了能够把我们的数据定义为nested
,必须修改之前的索引为mapping
为:
DELETE developer
PUT developer
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"skills": {
"type": "nested",
"properties": {
"language": {
"type": "keyword"
},
"level": {
"type": "keyword"
}
}
}
}
}
}
经过这样的改造之后,重新把我们之前的数据输入到index
里:
POST developer/_doc/101
{
"name": "zhang san",
"skills": [
{
"language": "ruby",
"level": "expert"
},
{
"language": "javascript",
"level": "beginner"
}
]
}
POST developer/_doc/102
{
"name": "li si",
"skills": [
{
"language": "ruby",
"level": "beginner"
}
]
}
针对101,在lucene
中的数据结构变为:
{
"name": "zhang san",
{
"skills.language": "ruby",
"skills.level": "expert"
},
{
"skills.language": "javascript",
"skills.level", "beginner"
}
}
nested query
我们来重新做之前的搜索:
GET developer/_search
{
"query": {
"nested": {
"path": "skills",
"query": {
"bool": {
"filter": [
{
"match": {
"skills.language": "ruby"
}
},
{
"match": {
"skills.level": "beginner"
}
}
]
}
}
}
}
}
注意上面的nested
字段。显示的结果是:
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "developer",
"_type" : "_doc",
"_id" : "102",
"_score" : 0.0,
"_source" : {
"name" : "li si",
"skills" : [
{
"language" : "ruby",
"level" : "beginner"
}
]
}
}
]
}
}
显然,我们只得到了一个我们想要的结果。
nested aggregation
同样,我们可以对索引来做一个aggregation
:
GET developer/_search
{
"size": 0,
"aggs": {
"nested_skills": {
"nested": {
"path": "skills"
},
"aggs": {
"languages": {
"terms": {
"field": "skills.language"
},
"aggs": {
"levels": {
"terms": {
"field": "skills.level"
}
}
}
}
}
}
}
}
显示的结果是:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"nested_skills" : {
"doc_count" : 3,
"languages" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "ruby",
"doc_count" : 2,
"levels" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "beginner",
"doc_count" : 1
},
{
"key" : "expert",
"doc_count" : 1
}
]
}
},
{
"key" : "javascript",
"doc_count" : 1,
"levels" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "beginner",
"doc_count" : 1
}
]
}
}
]
}
}
}
}
从上面显示的结果,可以看出来对于 ruby
来说,它分别对应于一个 bigginer
及一个 expert
。
这个和我们之前的数据是一样的。
对于 javascript
来说,它只有一个 beginner
的 level
。
留言