声明:以下只针对es6.0以下版本,原因是阿里云上售卖的es服务器是5.5.3版本,所以在写业务的时候是针对6.0以下版本的操作!
6.0以下版本如果想实现关系型数据库中一对多的操作,有两种常用方法:
1. 非规范化数据
比如mysql描述订单以及订单详情 : order(id, order_no, amount) -> order_detail(id, order_id, commodity, price) , 在关系型数据库中一个订单对应多个订单详情,详情表通过order_id与订单表关联。
那么在es中可以通过冗余数据描述这种关系, 索引如下:
PUT order
{
"mappings": {
"order": { --> 指定文档名(订单表)
"properties": { --> 指定字段以及类型
"id": {
"type": "integer"
},
"order_no": {
"type": "long"
},
"amount": {
"type": "double"
},
"order_detail": { --> 描述订单详情
"properties": {
"id": { "type": "integer" },
"order_id": { "type": "integer" },
"commodity": { "type": "text" },
"price": { "type": "double" }
}
}
}
}
}
}
该索引就是把订单以及订单详情冗余在一起。订单详情可以理解成java实体对象中的集合对象。
2. 父子文档关联
父子文档关联类似于join的操作,通过建立索引的时候描述文档之间的关系
父-子关系文档 在实质上类似于 nested model :允许将一个对象实体和另外一个对象实体关联起来。而这两种类型的主要区别是:在 nested
objects 文档中,所有对象都是在同一个文档中,而在父-子关系文档中,父对象和子对象都是完全独立的文档。
父-子关系的主要作用是允许把一个 type 的文档和另外一个 type 的文档关联起来,构成一对多的关系:一个父文档可以对应多个子文档 。与 nested
objects 相比,父-子关系的主要优势有:
更新父文档时,不会重新索引子文档。
创建,修改或删除子文档时,不会影响父文档或其他子文档。这一点在这种场景下尤其有用:子文档数量较多,并且子文档创建和修改的频率高时。
子文档可以作为搜索结果独立返回。
Elasticsearch 维护了一个父文档和子文档的映射关系,得益于这个映射,父-子文档关联查询操作非常快。但是这个映射也对父-子文档关系有个限制条件:父文档和其所有子文档,都必须要存储在同一个分片中。
父-子文档ID映射存储在 Doc Values 中。当映射完全在内存中时, Doc Values 提供对映射的快速处理能力,另一方面当映射非常大时,可以通过溢出到磁盘提供足够的扩展能力
建立索引:
PUT order { "mappings": { "order": { --> 父文档 "properties": { --> 父文档字段属性 "id": { "type": "integer" }, "order_no": { "type": "long" }, "amount": { "type": "double" } } }, "order_detail": { --> 子文档 "_parent": { --> 指定子文档的父亲 "type": "order" }, "properties": { "id": { "type": "integer" }, "order_id": { "type": "integer" }, "commodity": { "type": "text" }, "price": { "type": "double" } } } } }
创建父文档:
PUT /order/order/1 --> PUT /索引/文档/唯一标识(不写默认生成) { "id": "1", "order_no": "123456", "amount": "20" }
创建子文档:
PUT /order/order_detail/1?parent=1 --> 在创建子文档的时候需要使用parent=?指定父文档是谁!!!该处指定父文档为标识为1的order { "id": "1", "order_id": "1", "commodity": "小米", "price": "15" } PUT order/order_detail/2?parent=1 { "id": "2", "order_id": "1", "commodity": "番茄", "price": "5" }
查询父文档,并显示所有子文档:
GET order/order/_search { "query": { "has_child": { "type": "order_detail", "query": { "match_all": {} }, "inner_hits": {} --> 显示所有子文档 } } }
查询结果:
{ "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 1, "hits": [ { "_index": "order", "_type": "order", "_id": "1", "_score": 1, "_source": { "id": "1", "order_no": "123456", "amount": "20" }, "inner_hits": { "order_detail": { "hits": { "total": 2, "max_score": 1, "hits": [ { "_type": "order_detail", "_id": "1", "_score": 1, "_routing": "1", "_parent": "1", "_source": { "id": "1", "order_id": "1", "commodity": "小米", "price": "15" } }, { "_type": "order_detail", "_id": "2", "_score": 1, "_routing": "1", "_parent": "1", "_source": { "id": "2", "order_id": "1", "commodity": "番茄", "price": "5" } } ] } } } } ] } }
复杂父子文档聚合查询:
GET order/order/_search { "size": 0, "aggs": { "sum_amount": { -->指定聚合文档order,求amount字段sum值,并取名为sum_amount "sum": { "field": "amount" } }, "detail": { "children": { --> 指定子文档为order_detail "type": "order_detail" }, "aggs": { --> 子文档聚合操作,求子文档price字段sum值,并取名为sum_price "sum_price": { "sum": { "field": "price" } } } } } }
查询结果:
{ "took": 0, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, --> order文档一共一条记录 "max_score": 0, "hits": [] }, "aggregations": { "sum_amount": { --> 父文档sum(amount)结果 "value": 20 }, "detail": { --> 子文档 "doc_count": 2, --> 子文档一共两条记录 "sum_price": { --> 子文档sum(price)结果 "value": 20 } } } }
注意:es不支持聚合后的结果进行分页操作!!!