网站整体策划与设计,南昌网站免费制作,做免费网站安全吗,株洲seo优化ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Quer…ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Query DSLhttps://blog.csdn.net/zhenghuishengq/article/details/134159587【四】ElasticSearch的聚合查询操作https://blog.csdn.net/zhenghuishengq/article/details/134159587【五】SpringBoot整合elasticSearchhttps://blog.csdn.net/zhenghuishengq/article/details/134212200【六】Es集群架构的搭建以及集群的核心概念https://blog.csdn.net/zhenghuishengq/article/details/134258577【七】ES的开发场景和索引分片的设置及优化https://blog.csdn.net/zhenghuishengq/article/details/134302130【八】ElasticSearch处理对象间的关联关系https://blog.csdn.net/zhenghuishengq/article/details/134327295 Es处理对象间的关联关系 一Es处理对象间的关联关系1对象类型1.1对象类型的kibana操作1.2对象类型的java操作 2嵌套类型2.1嵌套类型的kibana操作2.2嵌套类型的java操作 3父子类型类型3.1父子类型的kibana操作3.2父子类型的java代码 一Es处理对象间的关联关系
es属于是nosql类型的非关系型数据库而在处理关联关系时往往是不擅长处理这种关联关系的不像mysql通过范式化来处理这种关联关系。
范式化有利于减少数据的冗余减少数据库的空间让整体维护更加简单但是在查询时需要多步查询join联表查询也会让整个查询时间增加反范式需要将数据冗余不需要考虑关联关系也无需进行这些join的操作在读取数据时性能更高但是缺点也很明显在修改数据时是比较麻烦的可能就是因为一个字段的修改就可能会引起多条数据的修改。
在ElasticSearch中主要也是考虑这种非关系型数据库的走向内部主要有四种方法处理这种关联数据的场景分别是对象类型、嵌套类型、父子关系类型、应用端关联
1对象类型
1.1对象类型的kibana操作
如在文档中包含对象的数据类型举例如下在article文章的索引中里面有一个属性为一个对象属性user就是每一篇文章中都包含着一个user用户的信息创建索引的语句如下
PUT /article
{mappings: {properties: {title:{type:text},createTime:{type: date},user:{properties: {username:{type:keyword},age:{type:long},sex:{type:text}}}}}
}随后往这个索引中插入一条数据并且设置用户的信息
PUT /article/_doc/1
{title:ElasticSearch学习,createTime:2023-11-09T00:00:00,user:{username:zhenghuisheng,age:18,sex:男}
}用户的查询如下通过用户名进行查询这里可以直接通过 对象.属性 的方式进行数据查询
GET /article/_search
{query: {match: {user.username: zhenghuisheng}}
}1.2对象类型的java操作
在创建索引之前需要通过配置获取es的连接其配置类如下
Bean
public RestHighLevelClient esRestClient(){RestHighLevelClient client new RestHighLevelClient(RestClient.builder(new HttpHost(xxx.33.xxx.xxx, 9200, http)));return client;
}创建articl索引和插入数据的java代码如下里面的client参数为springboot整合篇的数据
//插入数据
IndexRequest userIndex new IndexRequest(article);
User user new User();
user.setUsername(zhenghuisheng);
user.setAge(18);
user.setSex(男);
//添加数据
userIndex.source(JSON.toJSONString(user), XContentType.JSON);
//client为前面springBoot整合的客户端,通过resource导入
client.index(userIndex, ElasticSearchConfig.COMMON_OPTIONS);查询数据的方式如下在设置这个字段时即使是子字段也可以直接通过拼接的方式设置即可user.username
SearchRequest request new SearchRequest(article);
SearchSourceBuilder builder new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery(user.username,zhenghuisheng));
request.source(builder);
SearchResponse search client.search(request, RequestOptions.DEFAULT);
System.out.println(search);2嵌套类型
2.1嵌套类型的kibana操作
嵌套对象指的是对象数组的对象可以被独立索引。就是说在es内部会将数据进行分词操作但是在查询时可能就是会因这个操作导致将不正确的数据查询出来如英文名字有firstName和lastName但是因为组合问题将不必要的字段查询出来,如 zhan san、li si 但是在查询zhan si的时候是会将这两条数据查询出来的按理是不存在的.
为了解决这个嵌套类型的问题可以通过关键字 nested 类型来解决这个底层就是会将文档保存在两个索引库中在做查询时就会有一个join的连接查询
如下面的案例先创建一个索引数据依旧是创建一个文章的索引然后内部有一个author作者的信息
PUT /article
{mappings: {properties: {title:{type: text},author:{type: nested,properties: {first_name:{type:keyword},last_name:{type:keyword}}}}}
}往这个文档中插入一条数据如下里面的作者有两个以数组的形式存储
POST /article/_doc/1
{title:ElasticSearch教学,author:[{first_name:zheng,last_name:huisheng},{first_name:li,last_name:si}]
}那么在查询的时候只需要用nested 进行数据查询即可后面的path路径就是对应的需要查询的对象这样在查询时就不会将不需要的数据给查询出来
GET /article/_search
{query: {nested: { //固定搭配可以直接进紧跟在query后面path: author,query: {bool: {must: [{match: {author.first_name: zheng}},{match: {author.last_name: si}}]}}}}
}在聚合查询时也需要指定这个nested这个属性值设置路径为查询的对象
GET /article/_search
{aggs: {author: {nested: {path:author}}}
}2.2嵌套类型的java操作
嵌套类型对应的java代码如下首先先创建一个索引对参数进行设置 Testpublic void createIndex() throws Exception{XContentBuilder mapping XContentFactory.jsonBuilder().startObject().startObject(properties).startObject(title).field(type,text).endObject().startObject(author).field(type,nested).startObject(properties).startObject(first_name).field(type,keyword).endObject().startObject(last_name).field(type,keyword).endObject().endObject().endObject().endObject().endObject();CreateIndexRequest request new CreateIndexRequest(article).settings(Settings.builder().put(number_of_shards, 3) //设置分片数.put(number_of_replicas, 1) //设置副本数.build()).mapping(mapping);//执行创建CreateIndexResponse response client.indices().create(request, RequestOptions.DEFAULT);System.out.println(执行结果为 response);}插入数据的方式直接如下和上面用kibana操作的一样插入两条数据
//插入数据
IndexRequest userIndex new IndexRequest(article);
ListAuthor list new ArrayList();
Author author1 new Author();
author1.setFirstName(zheng);
author1.setLastName(huisheng);
Author author2 new Author();
author2.setFirstName(li);
author2.setLastName(si);
list.add(author1);
list.add(author2);
Article article new Article();
article.setTitle(ElasticSearch教学);
article.setAuthor(list);
//添加数据
userIndex.source(JSON.toJSONString(article), XContentType.JSON);
client.index(userIndex, ElasticSearchConfig.COMMON_OPTIONS);接下来就是查询数据直接通过构建这个NestedQueryBuilder即可
//查询数据
SearchRequest request new SearchRequest(article);
String path author;
QueryBuilder builder new NestedQueryBuilder(path, QueryBuilders.boolQuery().must(QueryBuilders.matchQuery(author.first_name, zheng)).must(QueryBuilders.matchQuery(author.last_name,si)), ScoreMode.None);
SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();
searchSourceBuilder.query(builder);
request.source(searchSourceBuilder);
SearchResponse search client.search(request, RequestOptions.DEFAULT);
System.out.println(search);3父子类型类型
3.1父子类型的kibana操作
在ElasticSearch中也存在着父子关系的文档其内部通过join的方式连接父子文档的关系并且在es中实现了父文档和子文档之间的独立也就是说当某一个父文档要更新时可以不需要修改子文档的数据而使用最上面的对象类型就是只要某一些数据更新就可能引起大范围数据的更新。通过文档独立的方式单个文档间的操作不会互相影响。
但是也有一个缺点就是大数据的联表其效率肯定是不高的但是优点是在更新时效率会更高。
在使用这个父子关系文档时需要在建立索引的时候确定父索引和子索引之间的关系。如下需要通过关键字join来表明是连接关系在relations中设置key为父索引的名称value为子索引名称
teacher_student_relation: {type: join, //指明类型relations: { //确认关系teacher: student //teacher为父文档、student为子文档}
}如创建一个用户索引分别对应的是student学生和teacher老师的信息设置分片数为3teacher为父文档student为子文档
PUT /user
{settings: {number_of_shards: 3},mappings: {properties: {relation: {type: join,relations: {teacher: student}},username: {type: keyword},sex: {type: text}}}
}接下来往父文档中插入一条数据依旧需要 relation 这个属性并且表名为teacher父文档
PUT /user/_doc/1
{username:Tom,sex:男,relation:{name:teacher //表明为父文档}
}接下来指定子文档为了解决这个join查询的性能需要通过routing路由功能让子文档和父文档路由到相同的分片上面其次就是也需要指定这个父子文档的属性除了设置子文档的名称之外还需要指定父文档的名称
PUT /user/_doc/student1?routing1
{username:zhenghuisheng,sex:男,relation:{name:student,parent:teacher}
}那么一下就是一些查询的方式如通过id的方式查询如下
GET /user/_doc/1 //根据父文档id查询
GET /user/_doc/student1?routing1 //通过子文档查询还可以查询子文档中是否包含某些数据里面需要注意使用的是 has_child 并且为类型为type
//查询子文档中是否包含某些数据
GET /user/_search
{query: {has_child: {type: student,query: {match: {username: zhenghusiheng}}}}
}同时也存在查询父文档中是否包含某些数据这里需要使用 has_parent 类型为parent_type
GET /user/_search
{query: {has_parent: {parent_type: teacher,query: {match: {username: Tom}}}}
}3.2父子类型的java代码
首先也是创建索引设置副本信息这些
XContentBuilder mapping XContentFactory.jsonBuilder().startObject().startObject(properties).startObject(teacher_student_relation).field(type,join).startObject(relations).field(teacher,student).endObject().endObject().startObject(username).field(type,keyword).endObject().startObject(sex).field(type,text).endObject().endObject().endObject();CreateIndexRequest request new CreateIndexRequest(user).settings(Settings.builder().put(number_of_shards, 3).put(number_of_replicas, 1).build()).mapping(mapping);CreateIndexResponse response client.indices().create(request, RequestOptions.DEFAULT);System.out.println(执行结果为 response);随后也是插入数据首先是父文档插入数据需要设置索引名并且最好设置文档id后面子文档查询时也是需要通过routing路由指定和父文档一样的id所以最好自己指定
//指定索引和路由
IndexRequest request new IndexRequest(user);
request.id(teacher1);
User user new User();
user.setUsername(Tom);
user.setSex(男);
Relation relation new Relation();
relation.setName(teacher);
user.setRelation(relation);
request.source(JSON.toJSONString(user), XContentType.JSON);
IndexResponse response client.index(request, ElasticSearchConfig.COMMON_OPTIONS);
System.out.println(response);随后是插入子文档的数据需要指定路由可以通过该路由使得子文档数据和父文档数据再一个分片上这样有利于提升join的关联查询。除此之外还需要设置这个parent的值
//指定索引和路由
IndexRequest request new IndexRequest(user).routing(teacher1);
User user new User();
user.setUsername(zhenghuisheng);
user.setSex(男);
Relation relation new Relation();
relation.setName(student);
relation.setParent(teacher);
user.setRelation(relation);
request.source(JSON.toJSONString(user), XContentType.JSON);
IndexResponse response client.index(request, ElasticSearchConfig.COMMON_OPTIONS);
System.out.println(response);嵌套文档和父子文档的主要区别如下嵌套文档通过nested文档实现其文档都是以冗余的方式存储在一起其读取数据的性能相对较高但是更新性能低父子文档通过join的方式实现父子文档数据独立但是需要额外维护父子关系读取数据的性能相对来说比较差
嵌套文档的场景主要适用于以查询为主的数据更新的数据比较少父子文档的场景主要在于子文档可能会出现频繁更新。