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 干货

Elasticsearch es nested 嵌套类型 详解

canal 系列:ES中nested嵌套类型同步

NoSql的学习

关于小程序的内嵌其他网页

Java子类加static不报错,不加无法解析类型?