ES嵌套(Nested)文档使用

1、nested介绍

nested:嵌套对象,,用于数组中的元素是对象的[{}, {}],该nested类型是object数据类型的专用版本,它允许可以彼此独立地查询它们的方式对对象数组进行索引。
其他相关ES操作及介绍请参考《ElasticSearch6.5.4快速入门》

2、对象数组如何展平

内部对象字段数组的工作方式与预期不同。Lucene没有内部对象的概念,因此Elasticsearch将对象层次结构扁平化为一个简单的字段名和值列表。例如下面的文档:
PUT user/user_info/1
{
  "group" : "man",
  "userName" : [ 
    {
      "first" : "张",
      "last" :  "三"
    },
    {
      "first" : "李",
      "last" :  "四"
    }
  ]
}
userName字段被动态添加为type的字段object,会在内部转换成以下方式的文档:
{
  "group" :        "man",
  "userName.first" : [ "张", "李" ],
  "userName.last" :  [ "三", "四" ]
}
userName.firstuserName.last字段平面化为多值字段,之间的关联丢失,查询就不会得到预期的结果。

3、将nested字段用于对象数组

如果需要为对象数组建立索引并保持数组中每个对象的独立性,则应使用nested数据类型而不是object数据类型。在内部,嵌套对象索引阵列作为一个单独的隐藏文档中的每个对象,这意味着每个嵌套的对象可以被查询独立于其它的。
嵌套文档看似与文档内有一个集合字段类似,但是实现有很大的区别,以下面图中嵌套文档为例,留言1,留言2,留言3虽然都在当前文章所在的文档内,但是在内部其实存储为4个独立文档:

在这里插入图片描述

注意:设置成nested后的文档不能被直接查询,需要使用nested查询

4、创建嵌套类型索引

PUT /user
{
  "mappings": {
    "user_info":{
      "properties": {
        "userName":{
          "type": "text"
        },
        "sex":{
          "type": "keyword"
        },
        "age":{
          "type": "long"
        },
        # 手机号集合
        "phones":{
          "type": "nested",
          "properties": {
            "phone":{
              "type":"keyword"
            },
            "addr":{
              "type":"text"
            }
          }
        }
      }
    }
  }
}
## user:索引名
## user_info:类型名

5、添加文档

PUT /user/user_info/1
{
  "userName":"张三",
  "sex":"男",
  "age":20,
  "phones":[
    {
      "phone":"19986262233",
      "addr":"湖北黄冈"
    },
    {
      "phone":"17762554531",
      "addr":"湖北武汉"
    }
  ]
}
PUT /user/user_info/2
{
  "userName":"李四",
  "sex":"女",
  "age":20,
  "phones":[
    {
      "phone":"19975262233",
      "addr":"湖北宜昌"
    },
    {
      "phone":"17762556931",
      "addr":"深圳南山"
    }
  ]
}

6、Kibana中实现检索文档

## 查询手机号为19975262233,地址为湖北宜昌
POST /user/user_info/_search
{
  "query": {
    "nested": {
      "path": "phones",
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "phones.phone": "19975262233"
              }
            },
            {
              "match": {
                "phones.addr": "湖北宜昌"
              }
            }
          ]
        }
      }
    }
  }
}
## 结果
{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 3.465736,
    "hits" : [
      {
        "_index" : "user",
        "_type" : "user_info",
        "_id" : "2",
        "_score" : 3.465736,
        "_source" : {
          "userName" : "李四",
          "sex" : "女",
          "age" : 20,
          "phones" : [
            {
              "phone" : "19975262233",
              "addr" : "湖北宜昌"
            },
            {
              "phone" : "17762556931",
              "addr" : "深圳南山"
            }
          ]
        }
      }
    ]
  }
}

7、Java方式实现检索文档

@Test
public void t25() throws IOException {
    //创建SearchRequest对象
    SearchRequest request = new SearchRequest(index).types(type);
    //创建复合查询对象
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    //封装查询条件
    boolQuery.must(QueryBuilders.matchQuery("phones.phone","19975262233"));
    boolQuery.must(QueryBuilders.matchQuery("phones.addr","湖北宜昌"));
    //创建嵌套查询对象
    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("phones",boolQuery,ScoreMode.None);
    //创建并设置SearchSourceBuilder对象
    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(nestedQueryBuilder);
    // 将条件放入request中
    request.source(builder);
    //执行查询
    SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
    SearchHit[] hits = response.getHits().getHits();
    for (SearchHit hit : hits) {
        System.out.println(hit);
    }
}

运行结果:

{
  "_index" : "user",
  "_type" : "user_info",
  "_id" : "2",
  "_score" : 0.0,
  "_source" : {
    "userName" : "李四",
    "sex" : "女",
    "age" : 20,
    "phones" : [
      {
        "phone" : "19975262233",
        "addr" : "湖北宜昌"
      },
      {
        "phone" : "17762556931",
        "addr" : "深圳南山"
      }
    ]
  }
}

8、注意事项

嵌套索引中的最大字段数默认为50个,索引具有100个嵌套字段的1个文档实际上索引101个文档,因为每个嵌套文档都被索引为一个单独的隐藏文档。在索引中定义太多字段的情况可能导致映射爆炸
  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值