ES中使用nested类型的内嵌对象
Posted 爱上口袋的天空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES中使用nested类型的内嵌对象相关的知识,希望对你有一定的参考价值。
1、简介
在大数据的应用环境中,往往使用反范式设计来提高读写性能。
假设我们有个类似简书的系统,系统里有文章,用户也可以对文章进行赞赏。在关系型数据库中,如果按照数据库范式设计,需要两张表:一张文章表和一张赞赏历史记录表,赞赏历史记录表包括了赞赏者姓名和赞赏金额。
在Elastic search中,由于都是json格式存储,则可以在一个index存储系统中的文章及其赞赏记录,这种情况下需要在elastic search中使用nested类型的内嵌对象。因为如果使用数组或者object对象的话,赞赏者姓名和赞赏金额是相互独立的进行存储,不能被正确的关联。
2、建立index
PUT articles "mappings": "properties": "payment": "type": "nested", "properties": "amount": "type": "integer" , "name": "type": "keyword"
这样articles就有了payment这个nested类型的字段,payment里面的对象有amount和name,表示金额和姓名。
3、产生数据
产生如下数据,表示jack给文章1赞赏了29元,ross给文章1赞赏30元,ross给文章2赞赏31元。
POST articles/_doc/1 "payment": [ "name": "jack", "amount": 29 , "name": "ross", "amount": 30 ] POST articles/_doc/2 "payment": [ "name": "ross", "amount": 31 ]
4、根据内嵌对象进行查询
现在想查询jack赞赏过的文章,需要使用nested query
GET articles/_search "query": "nested": "path": "payment", "query": "term": "payment.name": "value": "jack"
path表示了nested字段的名称,需要注意的是,查询语句中要指定查询字段的全名,所以赞赏者姓名要用"payment.name"
如果在多个index上进行nested查询,没有nested字段的index会报错,这时可以将ignore_unmapped设置为true案例如下,查询名称是ross,金额大于20的数据:
GET /articles/_search "query": "nested": "path": "payment", "query": "bool": "must": [ "term": "payment.name": "value": "ross" , "range": "payment.amount": "gte": 20 ]
效果:
"took" : 0, "timed_out" : false, "_shards" : "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 , "hits" : "total" : "value" : 2, "relation" : "eq" , "max_score" : 1.4700036, "hits" : [ "_index" : "articles", "_type" : "_doc", "_id" : "1", "_score" : 1.4700036, "_source" : "payment" : [ "name" : "jack", "amount" : 29 , "name" : "ross", "amount" : 30 ] , "_index" : "articles", "_type" : "_doc", "_id" : "2", "_score" : 1.4700036, "_source" : "payment" : [ "name" : "ross", "amount" : 31 ] ]
5、nested对象聚合
如果想查看赞赏的平均金额,需要用nested aggregation
GET articles/_search "size": 0, "aggs": "nested": "nested": "path": "payment" , "aggs": "amount_avg": "avg": "field": "payment.amount"
同样注意要用path指定字段名称。返回的数据中,比普通的聚合查询多了一层嵌套
返回结果为"took": 1, "timed_out": false, "_shards": "total": 5, "successful": 5, "skipped": 0, "failed": 0 , "hits": "total": 2, "max_score": 0, "hits": [] , "aggregations": "nested": "doc_count": 3, "amount_avg": "value": 30
6、nested对象聚合和过滤
如果想看ross赞赏过的总金额,一开始写出query如下
GET articles/_search "size": 0, "query": "nested": "path": "payment", "query": "term": "payment.name": "value": "ross" , "aggs": "nested": "nested": "path": "payment" , "aggs": "sum": "sum": "field": "payment.amount"
此时结果并不是正确的,因为上面的query过滤的是ross赞赏过的文章,下面的聚合操作sum的是文章里所有的赞赏,包括了jack的赞赏。
所以需要在sum聚合操作之前,需要用Filter Aggregation筛选ross的赞赏。GET articles/_search "size": 0, "query": "nested": "path": "payment", "query": "term": "payment.name": "value": "ross" , "aggs": "payment": "nested": "path": "payment" , "aggs": "payer": "filter": "term": "payment.name": "value": "ross" , "aggs": "sum": "sum": "field": "payment.amount"
最外层的query筛选出ross赞赏过的文章。
第一层的aggs表示进行内嵌聚合。
第二层的aggs用Filter Aggregation筛选出表示ross赞赏行为的nested对象。
第三层的aggs进行聚合。
以上是关于ES中使用nested类型的内嵌对象的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch 7.x Nested 嵌套类型查询 ES 干货