Elastic search中使用nested类型的内嵌对象

在大数据的应用环境中,往往使用反范式设计来提高读写性能。
假设我们有个类似简书的系统,系统里有文章,用户也可以对文章进行赞赏。在关系型数据库中,如果按照数据库范式设计,需要两张表:一张文章表和一张赞赏历史记录表,赞赏历史记录表包括了赞赏者姓名和赞赏金额。
在Elastic search中,由于都是json格式存储,则可以在一个index存储系统中的文章及其赞赏记录,这种情况下需要在elastic search中使用nested类型的内嵌对象。因为如果使用数组或者object对象的话,赞赏者姓名和赞赏金额是相互独立的进行存储,不能被正确的关联。

建立index

PUT articles
{
  "mappings": {
    "doc": {
      "properties": {
        "payment": {
          "type": "nested",
          "properties": {
            "amount": {
              "type": "integer"
            },
            "name": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

这样articles就有了payment这个nested类型的字段,payment里面的对象有amount和name,表示金额和姓名。

产生数据

产生如下数据,表示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
    }
  ]
}

根据内嵌对象进行查询

现在想查询ross赞赏过的文章,需要使用nested query

GET articles/_search
{
  "query": {
    "nested": {
      "path": "payment",
      "query": {
        "term": {
          "payment.name": {
            "value": "ross"
          }
        }
      }
    }
  }
}

path表示了nested字段的名称,需要注意的是,查询语句中要指定查询字段的全名,所以赞赏者姓名要用"payment.name"
如果在多个index上进行nested查询,没有nested字段的index会报错,这时可以将ignore_unmapped设置为true

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
      }
    }
  }
}

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进行聚合。

作者:大神带我来搬砖
链接:https://www.jianshu.com/p/d685b7b6c9d1
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

原文地址:https://www.cnblogs.com/ExMan/p/11386108.html

时间: 2024-08-02 23:33:11

Elastic search中使用nested类型的内嵌对象的相关文章

Android官方ORM数据库Room技术解决方案:@Embedded内嵌对象(二)

Android官方ORM数据库Room技术解决方案:@Embedded内嵌对象(二) (一)附录1简介了Android Room的基本使用.在附录1例子中,User对象元素均为普通的Java基本数据类型,但是实际的开发中,通常建立的持久化存储对象复杂,且通常是结构化的Java对象,互相之间存在引用或者内嵌关系. Android Room支持数据库表Java对象通过注解符@Embedded内嵌一个Java对象.这样就像过去的ORM数据库一样,比如构造一个名为Info的Java对象,作为一个成员变量

JS中数据类型、原始数据、内置对象、包装类型对象、typeof

JS中数据类型.内置对象.包装类型对象.typeof关系  https://segmentfault.com/a/1190000018275384 JavaScript 数据类型和数据结构   https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures 原始数据 https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive 原文地址:https://www

Elastic Search中mapping的问题

Mapping在ES中是非常重要的一个概念.决定了一个index中的field使用什么数据格式存储,使用什么分词器解析,是否有子字段,是否需要copy to其他字段等.Mapping决定了index中的field的特征.在ES中有一些自动的字段数据类型识别.自动识别标准:数字 -> long 长整数文本 -> text 文本,字符串特殊格式的字符串(如:2018-01-01) -> 对应的特殊类型(如:date)字面值true|false -> boolean类型.3.1 测试搜索

Elastic Search中filter的理解

在ES中,请求一旦发起,ES服务器是按照请求参数的顺序依次执行具体的搜索过滤逻辑的.如何定制请求体中的搜索过滤条件顺序,是一个经验活.类似query(指search中的query请求参数),也是搜索的一种方式.与常见的搜索对比,filter不会计算搜索条件相关度分数,也不会根据相关度分数进行排序,相对效率更高一些.且filter内置cache,自动缓存常用的filter数据,有效提升过滤速度.语法:GET /test_sort/_search{ "query": { "boo

HTMl中的块标签和内嵌标签(内联标签)

块状标签: address - 地址 blockquote - 块引用center - 举中对齐块dir - 目录列表div - 常用块级容易,也是CSS layout的主要标签dl - 定义列表fieldset - form控制组form - 交互表单h1 - 大标题h2 - 副标题h3 - 3级标题h4 - 4级标题h5 - 5级标题h6 - 6级标题hr - 水平分隔线isindex - input promptmenu - 菜单列表noframes - frames可选内容,(对于不支持

elastic search 6.3 nested exist or missing

es,字段是否存在 用exist很方便就能搜索到查询的例子,但是针对nested这种object类型的是否存在的较少,而且6.3版本一些之前的查询是不支持的 如果想要判断某个nested是否存在可以在must_not中加入如下查询 user即作为nested,user节点下仍有多个字段存在 { "nested":{ "path":"user", "query":{ "exists":{ "fiel

学习中 常用到的string内置对象方法的总结

//concat() – 将两个或多个字符的文本组合起来,返回一个新的字符串. var str = "Hello"; var out = str.concat(" World","!"); console.log(str); //Hello console.log(out); //Hello World! //charAt() – 返回指定位置的字符. var str = "HelloString"; var out = st

Salesforce Apex学习 : 利用Schema命名空间中的DescribeSObjectResult类型来获取sObject对象的基本信息

DescribeSObjectResult 对象的取得: //使用getDescribe方法和sObject token Schema.DescribeSObjectResult mySObjDescribe = MySObject__c.sObjectType.getDescribe(); //使用Schema的sObjectType静态变量 Schema.DescribeSObjectResult mySObjDescribe = Schema.SObjectType.MySObject__

js之数据类型(对象类型——单体内置对象——JSON)

JSON(Java Script Object Notation)使用JavaScript语法,是用于存储和传输数据的格式,通常用于服务端向网页传递数据.JSON格式仅仅是一个文本,文本可以被任何编程语言读取及作为数据格式传递(一种类似js中的对象格式的字符串,json它是一个字符串,只不过长的像对象)从上面的描述中我们可以看到如下知识 JSON的规则:数据放在了一对引号当中,用{}或者是[]括起来:里面的每一个数据都是以键值对表示(key:value);key必须放双引号,单引号不行:每个数据