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查询一样
posted @ 2018-10-16 15:07  double_yuan  阅读(27869)  评论(6编辑  收藏  举报