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

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM