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.first
和userName.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个文档,因为每个嵌套文档都被索引为一个单独的隐藏文档。在索引中定义太多字段的情况可能导致映射爆炸