父子文檔的特點
1. 父/子文檔是完全獨立的。
2. 父文檔更新不會影響子文檔。
3. 子文檔更新不會影響父文檔或者其它子文檔。
父子文檔的映射與索引
1. 父子關系 type 的建立必須在索引新建或 update-mapping 時候確定好
PUT /company { "mappings": { "branch": {}, //父文檔 type "employee": { "_parent": { "type": "branch" //子文檔 type } } } }
2. 父文檔的索引和普通文檔索引一樣。
POST /company/branch/_bulk { "index": { "_id": "london" }} { "name": "London Westminster", "city": "London", "country": "UK" }
3. 子文檔索引必須指定其對應的父文檔 ID,作用:
- 建立父子文檔之間的關聯
- 確保子文檔能夠被索引到父文檔所在分片(parent id 作為 route)
PUT /company/employee/1?parent=london //指定 id = london 的父文檔 { "name": "Alice Smith", "dob": "1970-10-24", "hobby": "hiking" }
4. 如果要更改文檔的父文檔,不能僅僅 update 或者 reindex 舊文檔(新的父文檔可能在不同分片上),需要先刪除舊文檔再重新索引。
父子關系的應用
看到 parent-child 關系,我們很容易想到的是像 SQL 那樣的各種 JOIN 操作——比如查詢某個文檔並一並取回所有的父或子文檔等。
然而,ES 中不支持類似的 JOIN 查詢。即便 child aggregation 也不能做到像 SQL 那樣的 JOIN 操作!
在 ES 中的 parent-child 關系基本可以理解為是一個過濾條件,如下:
//查詢某文檔,只有該文檔有"父文檔"且滿足一定條件才算匹配 {"has_parent": { //文檔是否有 parent "type": "branch", //其 parent 所在 type 必須是 branch "query": { //其 parent 必須滿足以下 query 條件 "match": { "country": "UK" } } } //如果滿足以上條件,hit 該文檔 } //查詢某文檔,只有該文檔有"子文檔"且滿足一定條件才算匹配 { "has_child": { //文檔是否有 child "type": "employee", //其 child所在 type 必須是 employee "query": { //其 parent 必須滿足以下 query 條件 "match": { "name": "Alice Smith" } } } //如果滿足以上條件,hit 該文檔 }
1. has_child:基於子文檔的內容,查找父文檔
//請求 GET /company/branch/_search { "query": { "has_child": { //基於 child 的內容,查詢滿足條件的 parent 文檔 "type": "employee", "query": { //在 child 中執行 match query操作 "match": { "name": "Alice Smith" } } } } } //結果 { "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 1, "max_score": 1, "hits": [ { "_index": "company", "_type": "branch", //注意!!!返回的是 parent 的文檔 "_id": "london", "_score": 1, "_source": { "name": "London Westminster", "city": "London", "country": "UK" } } ] } }
2. has_parent:基於父文檔的內容,查找子文檔
//請求 GET /company/employee/_search { "query": { "has_parent": { //基於 parent 的內容,查詢滿足條件的 child 文檔 "type": "branch", "query": { //在 parent 中執行 match query 查詢 "match": { "country": "UK" } } } } } //結果 { "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 1, "max_score": 1, "hits": [ { "_index": "company", "_type": "employee", //注意!!!返回的是 child 的文檔 "_id": "1", "_score": 1, "_routing": "london", "_parent": "london", "_source": { "name": "Alice Smith", "dob": "1970-10-24", "hobby": "hiking" } } ] } }
3. children aggregation:對關聯的 child 文檔進行聚合操作
//請求 GET /company/branch/_search { "size" : 0, "aggs": { "country": { "terms": { "field": "country" //以不同的 country 來分組(桶分) }, "aggs": { "employees": { "children": { //children aggregation,子 type 為 employee "type": "employee" }, "aggs": { "hobby": { "terms": { "field": "hobby" //以不同的 hobby 來分組(桶分) } } } } } } } } //結果 "aggregations": { "country": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ //country 聚合結果 { "key": "uk", "doc_count": 2, "employees": { //children aggregation 聚合 "doc_count": 1, "hobby": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ //hobby 聚合結果 { "key": "hiking", "doc_count": 1 } ] } } } ] } }