概要
在Elasticsearch实战场景中,我们或多或少会遇到嵌套文档的组合形式,反映在ES中称为父子文档。
父子文档的实现,至少包含以下两种方式:
1)父子文档
父子文档在5.X版本中通过parent-child父子type实现,即:1个索引对应多个type;
6.X+版本已经不再支持一个索引多个type,6.X+的父子索引的实现改成Join。
2)Nested嵌套类型
问题引出
示例:
{
"age": 10,
"name": "libai",
"objs": [
{
"field1": "aaa1",
"field2": "bbb"
},
{
"field1": "aaa",
"field2": "bbb1"
}
]
}
新建索引
PUT /blog_new
{
"mappings": {
"properties" : {
"age" : {
"type" : "long"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"nickname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"sex" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"objs":{
"properties" : {
"field1": {
"type" :"text"
},
"field2": {
"type" :"text"
}
}
}
}
}
}
添加数据
POST /blog_new/_doc/1
{
"age": 10,
"name":"libai",
"objs":
[{
"field1": "aaa1",
"field2": "bbb"
},
{
"field1": "aaa",
"field2": "bbb1"
}]
}
查看数据:
GET /blog_new/_search
{
"query": {
"match_all":{
}
}
}
条件查询数据: field1: aaa1 field2: bbb2
GET /blog_new/_search
{
"query": {
"bool": {
"must": [
{
"match":{
"objs.field1": "aaa1"
}
},
{
"match":{
"objs.field2": "bbb1"
}
}
]
}
}
}}
原因分析
这就是为什么我说:
elasticsearch中的内部对象无法按预期工作
这里的问题是elasticsearch(lucene)使用的库没有内部对象的概念,因此内部对象被扁平化为一个简单的字段名称和值列表。
我们的文档内部存储为:
{
"name": [ "李白" ],
"age": [ 10 ],
"objs.field1": [ "aaa1", "bbb" ],
"objs.field2": [ "aaa", "bbb1" ]
}
如何解决呢
我们需要更新它的类型为nested。
查询
GET /blog_new/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "objs",
"query": {
"bool": {
"must": [
{
"match": {
"objs.field1": "aaa1"
}
}
]
}
}
}
}
]
}
}
}
GET /blog_new/_search?pretty
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "objs",
"query": {
"bool": {
"must": [
{
"match": {
"objs.field1": "aaa1"
}
},
{
"match": {
"objs.field2": "bbb1"
}
}
]
}
}
}
}
]
}
}
}
GET /blog_new/_search?pretty
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "objs",
"query": {
"bool": {
"must": [
{
"match": {
"objs.field1": "aaa1"
}
},
{
"match": {
"objs.field2": "bbb"
}
}
]
}
}
}
}
]
}
}
}
原理
{
"name": [ "李白" ],
"age": [ 10 ],
{
"objs.field1":: [ "aaa1"],
"objs.field2": [ "bbb" ]
},
{
"objs.field1":: [ "aaa" ],
"objs.field2": [ "bbb1"]
}
}
Nested类型的作用?
从上一小节,可以清晰的看出nested类型的特别之处。
nested类型是对象数据类型的专用版本,它允许对象数组以可以彼此独立查询的方式进行索引。
4、Nested类型的适用场景
图片来自:rockybean教程
Nested类型的增、删、改、查、聚合操作详解
Nested类型——增
POST /blog_new/_doc/1
{
"age": 10,
"name":"libai",
"objs":
[
{
"field1": "aaa1",
"field2": "bbb"
},
{
"field1": "aaa",
"field2": "bbb1"
}
]
}
仅增加objs一个元素
POST blog_new/_update_by_query
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "libai"
}
}
]
}
},
"script": {
"lang": "painless",
"params": {
"obj": {
"field1": "000",
"field2": "111"
}
},
"source": "ctx._source.objs.add(params.obj)"
}
}
Nested类型——删
序号为1的评论原来有两条,现在删除field1 =aaa 的数据,删除后objs数为一条。
POST blog_new/_doc/_update
{
"script": {
"lang": "painless",
"source": "ctx._source.objs.removeIf(it -> it.field1 == 'aaa');"
}
}
5.3 Nested类型——改
POST blog_new/_update_by_query
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "objs",
"query": {
"bool": {
"must": [
{
"match": {
"objs.field1": "aaa1"
}
}
]
}
}
}
}
]
}
},
"script": {
"source": "for(e in ctx._source.objs){if (e.field1 == 'aaa1') {e.field1 = 25; e.field2= 'very very good article...';}}"
}
}
5.4 Nested类型——查
如前所述,查询评论字段中评论姓名=William并且评论age=34的blog信息。
GET /blog_new/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "objs",
"query": {
"bool": {
"must": [
{
"match": {
"objs.field1": "aaa1"
}
}
]
}
}
}
}
]
}
}
}
5.5 Nested类型——聚合
GET blog_new/_search
{
"size": 0,
"aggs": {
"comm_aggs": {
"nested": {
"path": "objs"
},
"aggs": {
"min_age": {
"cardinality":{"field":"objs.field1.keyword"}
}
}
}
}
}



