zoukankan      html  css  js  c++  java
  • elasticsearch 关联查询

    父-子关系文档

    父-子关系文档 在实质上类似于 nested model :允许将一个对象实体和另外一个对象实体关联起来。 而这两种类型的主要区别是:在 nested objects 文档中,所有对象都是在同一个文档中,而在父-子关系文档中,父对象和子对象都是完全独立的文档。

    父-子关系的主要作用是允许把一个 type 的文档和另外一个 type 的文档关联起来,构成一对多的关系:一个父文档可以对应多个子文档 。与 nested objects 相比,父-子关系的主要优势有:

    更新父文档时,不会重新索引子文档。
    创建,修改或删除子文档时,不会影响父文档或其他子文档。这一点在这种场景下尤其有用:子文档数量较多,并且子文档创建和修改的频率高时。
    子文档可以作为搜索结果独立返回。
    Elasticsearch 维护了一个父文档和子文档的映射关系,得益于这个映射,父-子文档关联查询操作非常快。但是这个映射也对父-子文档关系有个限制条件:父文档和其所有子文档,都必须要存储在同一个分片中。

    我使用的是es5.6,es6.0版本以上一个索引对应一个type
    1.在es中查询父子级关系

    创建父子级关系

    PUT user_test
    {
      "mappings": {
        "dept": {
          "properties": {
            "dname":{
              "type": "text"
            }
          }
        },
        "user":{
          "_parent": {
            "type": "dept"
          }, 
          "properties": {
            "deptId":{
              "type": "text"
            },
            "username":{
              "type": "text"
            },
            "age":{
              "type": "integer"
            }
          }
        }
      }
    }

    创建了一个公司的索引 包括部门和用户两个类型

    新增一个父级数据

    POST company/dept/2
    {
      "dname":"开发部"
    }

    新增一个子级数据

    POST company/user?parent=2
    {
      "uid":"1",
      "uname":"王五",
      "age":10  
      
    }

    以子级查

    GET company/user/_search
    {
      "query": {
        "has_parent": {
          "parent_type": "dept",
          "query": {
            "term": {
              "dname":""
            }
          },"inner_hits":{}
        }
      }
    }

    以父级查

    GET company/dept/_search
    {
      "query": {
        "has_child": {
          "type": "user",
          "query": {
            "match": {
              "uname":"里斯"
            }
          },"inner_hits":{}
        }
      }
    }
    用inner_hits则可以把父子文档同时返回——既返回,不加inner_hits只返回一个type里的数据。inner_hits默认只查询3条数据,可以自定义设置from 和size。
    如果父子级同时有查询条件,用bool作为复合查询
    GET company/dept/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "dname": {
                  "value": ""
                }
              }
            },{
              "has_child": {
                "type": "user",
                "query": {
                  "match": {
                    "uname":""
                  }
                },"inner_hits":{}
              }
            }
            
          ]
        }
      }
    }

    2.java项目中查询

    实体类

    1.部门

    @Document(indexName = "user_test",type = "dept")
    public class Department {
        @Id
        private String id;
        private String dname;
    }

    2.用户

    @Document(indexName = "user_test",type = "user")
    public class User {
        @Id
        private String id;
        @Parent(type = "dept")
        private String deptId;
        private String uname;
        private Integer age;
    }

    使用ElasticsearchTemplate

    1查询父级数据

    QueryBuilder qb=JoinQueryBuilders.hasChildQuery("user",QueryBuilders.matchAllQuery(),ScoreMode.None);
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withIndices("user_test")
                    .withQuery(qb)
                    .build();
            List<Department> depts= elasticsearchTemplate.queryForList(searchQuery,Department.class);
    ScoreMode:评分模式minmaxsumavgnone

    2.查询子级数据

    QueryBuilder qb=JoinQueryBuilders.hasParentQuery("dept",QueryBuilders.matchQuery("dname","开"),false);
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withIndices("user_test")
                    .withQuery(qb)
                    .build();
            List<User> users= elasticsearchTemplate.queryForList(searchQuery,User.class);

     评分功能:这has_parent也有得分支持。默认值是false忽略父文档的分数

     官方参考:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-has-parent-query.html

    查询父子级全部数据

     QueryBuilder qb = JoinQueryBuilders.hasChildQuery(
                    "user",                         //要查询的子类型
                    QueryBuilders.matchQuery("uname.keyword","张三"),
                    ScoreMode.None
            ).innerHit(new InnerHitBuilder());
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withIndices("user_test")
                    .withQuery(qb)
                    .build();
            List<DeptVO> depts= elasticsearchTemplate.query(searchQuery, searchResponse -> {
                SearchHits hits = searchResponse.getHits();
                List<DeptVO> list = new ArrayList<>();
                Arrays.stream(hits.getHits()).forEach(h -> {
                    Map<String, Object> source = h.getSource();
                    SearchHits innerHitsMap=h.getInnerHits().get("user");//获取子级数据
                    List<User> user1s=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 DeptVO(source.get("dname").toString(),user1s));
                });
                return list;
            });
    JoinQueryBuilders.hasChildQuery().innerHit(new InnerHitBuilder())的.innerHit(new InnerHitBuilder())与es查询一样
  • 相关阅读:
    多态的详解
    Java继承详解
    static关键字特点
    数组(相关知识的整理)
    杨辉三角(用for循环)
    Jmeter接口测试案例实践(一)
    组合测试方法:配对测试实践
    用例设计方法:判定表驱动法实践
    sso系统登录以及jsonp原理
    单点登录--sso系统
  • 原文地址:https://www.cnblogs.com/double-yuan/p/9798103.html
Copyright © 2011-2022 走看看