• elasticsearch 学习之父子关联查询 parent/child


    parent-child 关系

    关联关系,可以为两个完全分开的文档,可以将一种文档类型以一对多的关系关联到另一个上

    优点:

    1.parent文档的更新不需要重新为子文档重建索引

    2.对子文档进行添加,更改,删除操作室不会影响父文档或者其他子文档

    3.子文档可以被当做查询请求的结果返回

    Elasticsearch 维护了一个父文档和子文档的映射关系,得益于这个映射,父-子文档关联查询操作非常快。但是这个映射也对父-子文档关系有个限制条件:父文档和其所有子文档,都必须要存储在同一个分片中。

    parent-child映射

    为了建立parent-child关系,需要在索引创建的时候指定父文档

    建立索引
    PUT /company
    {
      "mappings": {
        "dept": {},
        "user": {
          "_parent": {
            "type": "dept" 
          }
        }
      }
    }  

    通过 child 找到 parents

    查询child返回的是parents文档

    查询child  uname为 "里斯"的员工 部门
    GET company/dept/_search { "query": { "has_child": { "type": "user", "query": { "match": { "uname":"里斯" } },"inner_hits":{} //inner_hits可以将子文档带出 默认只查3条 可以自己设置 size,from
    } } }

        

    has_child 查询可以匹配多个 child 文档,每个都有相应的相关分数。这些分数如何化归为针对 parent 文档的单独分数取决于 score_mode 参数。默认设置是 none,这会忽视 child 分数并给 parents 分配了 1.0 的分值,不过这里也可以使用 avg,min,max 和 sum

    开发部的下的员工王五评分更高,会更好匹配

    GET company/dept/_search
    {
         "query": {
       "has_child": {
       "type": "user",
             "score_mode": "max",   //默认为none  此时明显快于其他模式,因为不需要计算每个child文档的分值,只有在乎分值的时候才需要设置
            "query": {
            "match": {
                "uname":"王五"
        }
      },"inner_hits":{}
    }}}

    不带子级查询
     @Test
        public void test1(){
            QueryBuilder qb = JoinQueryBuilders.hasChildQuery(
                    "user",                         //要查询的子类型
                    QueryBuilders.matchQuery("uname","张"),
                    ScoreMode.None
            );
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(qb)
                    .build();
            List<Dept> depts =elasticsearchTemplate.queryForList(searchQuery,Dept.class);
            System.out.println(depts);
        }
    

      

    带子级查询
        @Test
        public void test1(){
            QueryBuilder qb = JoinQueryBuilders.hasChildQuery(
                    "user",                         //要查询的子类型
                    QueryBuilders.matchQuery("uname","张"),
                    ScoreMode.None
            ).innerHit(new InnerHitBuilder);
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(qb)
                    .build();
            List<Dept> depts= elasticsearchTemplate.query(searchQuery, searchResponse -> {
                SearchHits hits = searchResponse.getHits();
                List<Dept> list = new ArrayList<>();
                Arrays.stream(hits.getHits()).forEach(h -> {
                    Map<String, Object> source = h.getSource();
                    SearchHits innerHitsMap=h.getInnerHits().get("user");//获取子级数据
                    List<User> users=Arrays.stream(innerHitsMap.getHits()).map(innerH -> {
                        Map<String, Object> innerSource = innerH.getSource();
                        return new User(innerSource.get("uname").toString(),Integer.valueOf(innerSource.get("age").toString()));
                    }).collect(Collectors.toList());
                    list.add(new Dept(source.get("dname").toString(),users));
                });
                return list;
            });
            System.out.println(depts);
        }
    

      

    min_children 和 max_children

    has_child 查询和过滤器都接受 min_children 和 max_children 参数,仅当匹配 children 的数量在指定的范围内会返回 parent 文档。

    查询至少有三个员工的部门
    
    GET company/dept/_search
    {
      "query": {
        "has_child": {
          "type": "user",
          "min_children": 4,
    "max_children":10, "query": { "match_all": {} } } } }
        @Test
        public void test1() {
           QueryBuilder qb = JoinQueryBuilders.hasChildQuery(
                    "user",                 
                    QueryBuilders.matchAllQuery(),
                    ScoreMode.None
            ).minMaxChildren(4,10);
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(qb)
                    .build();
            List<Dept> depts = elasticsearchTemplate.queryForList(searchQuery, Dept.class);
        }
    

      

    通过 parents 找到 child 

    parent-child 文档本身是独立的,每个可以独立地进行查询。has_child 查询允许我们返回基于在其 children 的数据上 parents 文档,has_parent 查询则是基于 parents 的数据返回 children。
    has_children 查询也支持 score_mode,但是仅仅会接受两个设置:none(默认)和 score. 查询部门名称有"开"的员工 GET company/user/_search { "query": { "has_parent": { "parent_type": "dept", "query": { "match": { "dname":"开" } } } } }
    不带父级
        @Test
        public void test2() {
            QueryBuilder qb = JoinQueryBuilders.hasParentQuery(
                    "dept",
                    QueryBuilders.matchQuery("dname", "开"),
                    true
            );
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(qb)
                    .build();
           List<User> depts =elasticsearchTemplate.queryForList(searchQuery,User.class);
            System.out.println(depts);
        }
    

      

    带父级
    
        @Test
        public void test2() {
            QueryBuilder qb = JoinQueryBuilders.hasParentQuery(
                    "dept",
                    QueryBuilders.matchQuery("dname", "开"),
                    true
            ).innerHit(new InnerHitBuilder());
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(qb)
                    .build();
            List<User> depts = elasticsearchTemplate.query(searchQuery, searchResponse -> {
                SearchHits hits = searchResponse.getHits();
                List<User> list = new ArrayList<>();
                Arrays.stream(hits.getHits()).forEach(h -> {
                    Map<String, Object> source = h.getSource();
                    SearchHits innerHitsMap = h.getInnerHits().get("dept");
                    List<Dept> users = Arrays.stream(innerHitsMap.getHits()).map(innerH -> {
                        Map<String, Object> innerSource = innerH.getSource();
                        return new Dept(innerSource.get("dname").toString());
                    }).collect(Collectors.toList());
                    list.add(new User(source.get("uname").toString(), Integer.valueOf(source.get("age").toString()), users));
                });
                return list;
            });
            System.out.println(depts);
        }
    

      

     children 聚合

    parent-child 支持 children 聚合,parent 聚合不支持。
    按员工名称分组,年龄的和 GET company/user/_search { "size": 0, "aggs": { "name": { "terms": { "field": "uname.keyword", "size": 2 }, "aggs": { "sum": { "sum": { "field": "age" } } } } } }

      

        @Test
        public void test3() {
            TermsAggregationBuilder tb = AggregationBuilders.terms("name").field("uname.keyword");
            SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum("sum").field("age");
            tb.subAggregation(sumAggregationBuilder);
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .addAggregation(tb)
                    .build();
            Aggregations aggregations = elasticsearchTemplate.query(searchQuery, searchResponse -> {
                return searchResponse.getAggregations();
            });
            Terms terms = aggregations.get("name");
            if (terms.getBuckets().size() > 0) {
                for (Terms.Bucket bucket : terms.getBuckets()) {
                    long ss = bucket.getDocCount();
                    Sum sum = (Sum) bucket.getAggregations().asMap().get("sum");
                    System.out.println(ss + "   " + sum);
                }
            }
        }
    

      

  • 相关阅读:
    [灵魂拷问]MySQL面试高频100问(工程师方向)
    前后端分离模式下的权限设计方案
    Netty实战:设计一个IM框架
    超实用,Linux中查看文本的小技巧
    Java面试,如何在短时间内做突击
    挑战10个最难回答的Java面试题(附答案)
    SpringBoot是如何动起来的
    Lab_2_SysOps_VPC_Linux_v2.5
    Lab_1_SysOps_Compute_Linux_v2.5
    change-resource-tags.sh
  • 原文地址:https://www.cnblogs.com/xiaoxiaoliu/p/9916472.html
Copyright © 2020-2023  润新知