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
}
}
}
}