父-子關係文檔html
父-子關係文檔 在實質上相似於 nested model :容許將一個對象實體和另一個對象實體關聯起來。 而這兩種類型的主要區別是:在 nested objects 文檔中,全部對象都是在同一個文檔中,而在父-子關係文檔中,父對象和子對象都是徹底獨立的文檔。java
父-子關係的主要做用是容許把一個 type 的文檔和另一個 type 的文檔關聯起來,構成一對多的關係:一個父文檔能夠對應多個子文檔 。與 nested objects 相比,父-子關係的主要優點有:es6
更新父文檔時,不會從新索引子文檔。
建立,修改或刪除子文檔時,不會影響父文檔或其餘子文檔。這一點在這種場景下尤爲有用:子文檔數量較多,而且子文檔建立和修改的頻率高時。
子文檔能夠做爲搜索結果獨立返回。
Elasticsearch 維護了一個父文檔和子文檔的映射關係,得益於這個映射,父-子文檔關聯查詢操做很是快。可是這個映射也對父-子文檔關係有個限制條件:父文檔和其全部子文檔,都必需要存儲在同一個分片中。app
我使用的是es5.6,es6.0版本以上一個索引對應一個type
1.在es中查詢父子級關係elasticsearch
建立父子級關係ide
PUT user_test { "mappings": { "dept": { "properties": { "dname":{ "type": "text" } } }, "user":{ "_parent": { "type": "dept" }, "properties": { "deptId":{ "type": "text" }, "username":{ "type": "text" }, "age":{ "type": "integer" } } } } }
建立了一個公司的索引 包括部門和用戶兩個類型ui
新增一個父級數據es5
POST company/dept/2 { "dname":"開發部" }
新增一個子級數據spa
POST company/user?parent=2 { "uid":"1", "uname":"王五", "age":10 }
以子級查code
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查詢同樣