Elasticsearch-88-nested object数据结构

普通object类型

用冗余数据的方式来建模,其实用的就是object类型,来看一个数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT /website/blogs/6
{
"title": "花无缺发表的一篇帖子",
"content": "我是花无缺,大家要不要考虑一下投资房产和买股票的事情啊。。。",
"tags": [ "投资", "理财" ],
"comments": [
{
"name": "小鱼儿",
"comment": "什么股票啊?推荐一下呗",
"age": 28,
"stars": 4,
"date": "2016-09-01"
},
{
"name": "黄药师",
"comment": "我喜欢投资房产,风,险大收益也大",
"age": 31,
"stars": 5,
"date": "2016-10-22"
}
]
}

就是一个帖子下的所有评论,这种类型的数据,就是object类型的,然后来做一个搜索

搜索案例

需求: 搜索被年龄是28岁的黄药师评论过的博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET /website/blogs/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "黄药师"
}
},
{
"match": {
"comments.age": "28"
}
}
]
}
}
}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
"took": 21,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1.8022683,
"hits": [
{
"_index": "website",
"_type": "blogs",
"_id": "6",
"_score": 1.8022683,
"_source": {
"title": "花无缺发表的一篇帖子",
"content": "我是花无缺,大家要不要考虑一下投资房产和买股票的事情啊。。。",
"tags": [
"投资",
"理财"
],
"comments": [
{
"name": "小鱼儿",
"comment": "什么股票啊?推荐一下呗",
"age": 28,
"stars": 4,
"date": "2016-09-01"
},
{
"name": "黄药师",
"comment": "我喜欢投资房产,风,险大收益也大",
"age": 31,
"stars": 5,
"date": "2016-10-22"
}
]
}
}
]
}
}

注意,我们在添加数据的时候,黄药师的年龄是31的,但是搜索的时候还是搜索出来了,这个结果就不准确了,在之前有写过object类型的底层存储是怎样的结构, 会将一个数组中的数据,进行扁平化, 我们上面这条数据在底层的存储可能就是这样的

1
2
3
4
5
6
7
8
9
10
{
"title": [ "花无缺", "发表", "一篇", "帖子" ],
"content": [ "我", "是", "花无缺", "大家", "要不要", "考虑", "一下", "投资", "房产", "买", "股票", "事情" ],
"tags": [ "投资", "理财" ],
"comments.name": [ "小鱼儿", "黄药师" ],
"comments.comment": [ "什么", "股票", "推荐", "我", "喜欢", "投资", "房产", "风险", "收益", "大" ],
"comments.age": [ 28, 31 ],
"comments.stars": [ 4, 5 ],
"comments.date": [ 2016-09-01, 2016-10-22 ]
}

所以,我们的查询就会查询到这个document,name=黄药师,age=28,正好符合

那么怎么来解决这种问题呢?

nested object类型

通过使用nested object就可以解决上面出现的问题

首先,需要修改mapping,把之前的index删掉,重新建

1
DELETE /website

然后创建索引,手动设置mapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PUT /website
{
"mappings": {
"blogs": {
"properties": {
"comments":{
"type": "nested",
"properties": {
"name": { "type": "string" },
"comment": { "type": "string" },
"age": { "type": "short" },
"stars": { "type": "short" },
"date": { "type": "date" }
}
}
}
}
}
}

就是把comments的类型设置为nested,然后里面再设置各个属性,创建完成之后把之前那个数据再加进去.

搜索案例

再次按之前的需求搜索,看一下请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
GET website/blogs/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "花无缺"
}
},
{
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "黄药师"
}
},
{
"match": {
"comments.age": "28"
}
}
]
}
}
}
}
]
}
}
}

首先,就是一个bool组合查询,查的是外层的数据,然后里面套了一个nested, 然后里面又是一个bool组合查询

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}

这样的话就是查询不到的了.

nested object底层数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{ 
"comments.name": [ "小鱼儿" ],
"comments.comment": [ "什么", "股票", "推荐" ],
"comments.age": [ 28 ],
"comments.stars": [ 4 ],
"comments.date": [ 2014-09-01 ]
}
{
"comments.name": [ "黄药师" ],
"comments.comment": [ "我", "喜欢", "投资", "房产", "风险", "收益", "大" ],
"comments.age": [ 31 ],
"comments.stars": [ 5 ],
"comments.date": [ 2014-10-22 ]
}
{
"title": [ "花无缺", "发表", "一篇", "帖子" ],
"body": [ "我", "是", "花无缺", "大家", "要不要", "考虑", "一下", "投资", "房产", "买", "股票", "事情" ],
"tags": [ "投资", "理财" ]
}

nested object的底层数据结构是这样的,所以搜 name=黄药师而且age=28 的就搜索不到了

补充

查询请求的时候,nested 里面可以加一个参数,是score_mode,作用是就是,如果搜索命中了多个nested document,如何将多个nested document的分数合并为一个分数.
默认是avg,也可以设置max min none