当前位置: 首页 > article >正文

Spring Boot - 数据库集成06 - 集成ElasticSearch

Spring boot 集成 ElasticSearch

文章目录

  • Spring boot 集成 ElasticSearch
    • 一:前置工作
      • 1:项目搭建和依赖导入
      • 2:客户端连接相关构建
      • 3:实体类相关注解配置说明
    • 二:客户端client相关操作说明
      • 1:检索流程
        • 1.1:构建请求request和源source
        • 1.2:查询建造器queryBuilder构建
        • 1.3:请求建造器加入到请求源,请求源加入到请求中
        • 1.4:结果hits和highlight分析
      • 2:各种查询构造器说明
        • 2.1:MatchQueryBuilder
        • 2.2:RegexQueryBuilder
        • 2.3:IdsQueryBuilder
        • 2.4:MatchPhraseQueryBuilder
        • 2.5:MatchPhrasePrefixQueryBuilder
        • 2.6:MultiMatchQueryBuilder
        • 2.7:TermQueryBuilder
        • 2.8:FuzzyQueryBuilder
        • 2.9:RangeQueryBuilder
        • 2.10:WildcardQueryBuilder
        • 2.11:BoolQueryBuilder
        • 2.12:其他的QueryBuilder
      • 3:聚合建造器说明
        • 3.1:构造聚合条件
        • 3.2:将聚合条件交给builder,builder交给源,封装request
        • 3.3:结果解析和处理
    • 三:repo & template
      • 1:ElasticsearchRepository
        • 1.1:repo介绍
        • 1.2:自定义方法命名规范
      • 2:ElasticsearchRestTemplate
        • 2.1:template介绍
        • 2.2:template查询操作
          • 2.2.1:入参特殊情况处理【包括异常入参】
          • 2.2.2:构建查询条件Criteria
          • 2.2.3:构建高亮条件
          • 2.2.4:查询(也可能是其他的方法,不一定是search)
          • 2.2.5:结果封装
        • 2.3:创建索引和删除索引
      • 3:使用实例

在这里插入图片描述
Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用 JSON作为文档序列化的格式

{"name" :     "John","sex" :      "Male","age" :      25,"birthDate": "1990/05/01","about" :    "I love to go rock climbing","interests": [ "sports", "music" ]
}

ES和传统的关系型数据库相关术语对比如下:

关系型数据库ES
数据库(Database)索引(index)
表(table)类型(Type)[es6.0.0废弃]
行(row)文档(document)
列(column)字段(field)
表结构(schema)映射(mapping)
索引反向索引
SQL查询DSL
Select * from tableGet http://…
update table set…Put http://…
deleteDelete http://…

一:前置工作

1:项目搭建和依赖导入

  • spring data -> spring-boot-starter-data-elasticsearch -> repo & template
  • transport & elasticsearch-rest-high-level-client -> client
<dependencies><!-- spring boot启动器 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- spring boot web启动器 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- spring boot 测试启动器 --><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- commons-lang3工具类 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- spring data elasticsearch --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!-- es 高阶客户端 --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId></dependency><!-- es 低阶客户端 --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>transport</artifactId></dependency></dependencies>
spring:elasticsearch:rest:uris: http://localhost:9200,http://localhost:9201,http://localhost:9202 # 集群地址connection-timeout: 5s # 连接超时时间read-timeout: 30s # 读取超时时间max-connections: 100 # 最大连接数max-connections-per-route: 20 # 每个路由的最大连接数password: es_password

2:客户端连接相关构建

package com.cui.es_demo.config;import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;import java.util.Arrays;/*** @author cui haida* 2025/1/29*/@Configuration
@ConfigurationProperties(prefix = "spring.elasticsearch.rest")
public class ElasticSearchClientConfig {/*** 集群地址*/private String uris;/*** 用户名和密码*/private String username;private String password;@Beanpublic RestHighLevelClient restHighLevelClient() {// 创建连接, 指定url和用户名密码ClientConfiguration build = ClientConfiguration.builder().connectedTo(uris).withBasicAuth(username, password).build();return RestClients.create(build).rest();}
}

3:实体类相关注解配置说明

package com.cui.es_demo.entity;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.time.LocalDateTime;/*** 实体类相关注解介绍* - @Document:指定索引库的名称,以及分片数、副本数、刷新间隔、是否允许创建索引* - @Id:指定主键* - @Field:指定字段的名称,以及字段的分词器,以及字段的存储类型,以及字段的分词器* - @JsonFormat:指定日期格式(注意日期类型字段不要用java.util.Date类型,要用java.time.LocalDate或java.time.LocalDateTime类型)* @author cui haida* 2025/1/30*/
@NoArgsConstructor
@AllArgsConstructor
@Data
// 指定对应的索引名称,, 主分片数 = 3, 副本数 = 1, 刷新间隔 = 1s, 允许创建索引
@Document(indexName = "person", shards = 3, replicas = 1, refreshInterval = "1s", createIndex = true)
public class Person {@Idprivate String id;@Field(type = FieldType.Keyword)private String name;@Field(type = FieldType.Keyword)private String age;// text类型,并使用IK最粗粒度的分词器// 检索时的分词器采用的是最细粒度的IK分词器@Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_max_word")private String address;// 注意日期格式的特殊处理// 指定格式化和时区@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime createTime;
}

二:客户端client相关操作说明

对索引相关的操作都不推荐在这里执行,建议直接使用Kibana

文档的更新和插入操作建议使用repo & template方式

这里只介绍查询相关操作

1:检索流程

1.1:构建请求request和源source
// 声明查询请求对象
SearchRequest client = new SearchRequest();
// 指定使用的索引(数据库)
client.indices(index);// 声明searchSourceBuilder源对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// ======================== 源对象其他常见设置 ===========================
// 1. 排序和浅显分页
// from  & size 默认都是-1,也就是有多少显示多少
// from-size浅分页适合数据量不大的情况(官网推荐是数据少于10000条),可以跳码进行查询
searchSourceBuilder.from(0);
searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序
searchSourceBuilder.sort("age", SortOrder.DESC).sort("name", SortOrder.ASC);
1.2:查询建造器queryBuilder构建
// =============== 以boolean为例 ==================
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);
// 加入对应的builder到searchSourceBuilder中
searchSourceBuilder.query(boolQueryBuilder);// ============ 如果有高亮设置 ===============
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font style='color: red'>");
highlightBuilder.postTags("</font>");
highlightBuilder.field("name").field("age");
//同一字段中存在多个高亮值 设置都高亮
highlightBuilder.requireFieldMatch(true);// 高亮属性中加入高亮配置器
searchSourceBuilder.highlighter(highlightBuilder);
1.3:请求建造器加入到请求源,请求源加入到请求中
searchSourceBuilder.query(xxxQueryBuilder);
// 封装request,指定要request的索引index,指定source -> searchSourceBuilder
SearchRequest request = new SearchRequest(index).source(searchSourceBuilder);
// 通过client.search获取响应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
1.4:结果hits和highlight分析
List<Map<String, Object>> list = new ArrayList<>();
SearchResponse searchResponse = this.client.search(client, RequestOptions.DEFAULT);
if (searchResponse == null) {log.warn("没有返回值");return;
}// todo: 然后从这个searchResponse解析各种的值 -> 根据返回结构
int failedShards = searchResponse.getFailedShards();
System.out.println("失败的分片数:" + failedShards);
int successfulShards = searchResponse.getSuccessfulShards();
System.out.println("成功的分片数:" + successfulShards);
RestStatus status = searchResponse.status();
System.out.println("状态:" + status);//解析高亮数据
SearchHits hits = searchResponse.getHits();
System.out.println(hits.getMaxScore());
for (SearchHit hit : hits) {System.out.println("fields: " + hit.getFields());System.out.println("index is:" + hit.getIndex());System.out.println("document field is: " + hit.getDocumentFields());System.out.println("metadata field is: " + hit.getMetadataFields());System.out.println("score is: " + hit.getScore());System.out.println("-----------------");//原始数据,不包含高亮的数据Map<String, Object> sourceMap = hit.getSourceAsMap();//高亮数据,拿到高亮字段nameMap<String, HighlightField> highlightFields = hit.getHighlightFields();HighlightField highlightTitle = highlightFields.get("name");//将原始数据的name替换if (highlightTitle != null) {Text[] fragments = highlightTitle.getFragments();if (fragments != null && fragments.length > 0) {sourceMap.replace("name", fragments[0].toString());}}list.add(sourceMap);//循环将数据添加入列表
}
list.forEach(System.out::println);
package com.cui.es_demo.client;import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 客户端相关操作** @author cui haida* 2025/1/30*/
@Service
@Slf4j
public class ClientDemo {// private final RestHighLevelClient client;public ClientDemo(RestHighLevelClient restHighLevelClient) {this.client = restHighLevelClient;}/*** 主要介绍客户端查询操作*/public void searchTest(String[] args) {// 声明查询请求,同时声明作用的索引(数据库)SearchRequest request = new SearchRequest();request.indices("person");// 声明searchSourceBuilder源对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// ======================== 源对象其他常见设置 ===========================// 1. 排序和浅显分页// from  & size 默认都是-1,也就是有多少显示多少// from-size浅分页适合数据量不大的情况(官网推荐是数据少于10000条),可以跳码进行查询searchSourceBuilder.from(0);searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序searchSourceBuilder.sort("age", SortOrder.DESC).sort("name", SortOrder.ASC);// 构造查询建造器,根据不同的业务需求,进行不同的查询// =============== 以boolean为例 ==================BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);// 请求构造器加入源中searchSourceBuilder.query(boolQueryBuilder);// ============ 如果有高亮设置 ===============HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.preTags("<font style='color: red'>");highlightBuilder.postTags("</font>");highlightBuilder.field("name").field("age");//同一字段中存在多个高亮值 设置都高亮highlightBuilder.requireFieldMatch(true);// 高亮属性中加入源中searchSourceBuilder.highlighter(highlightBuilder);// 进行查询// 1. 设置查询源(source -> request)request.source(searchSourceBuilder);// 2. 通过search方法进行查询List<Map<String, Object>> list = new ArrayList<>();try {SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 结果处理System.out.println("response.getHits().getTotalHits() = " + response.getHits().getTotalHits());// 然后从这个searchResponse解析各种的值 -> 根据返回结构int failedShards = response.getFailedShards();System.out.println("失败的分片数:" + failedShards);int successfulShards = response.getSuccessfulShards();System.out.println("成功的分片数:" + successfulShards);RestStatus status = response.status();System.out.println("状态:" + status);// 解析高亮数据SearchHits hits = response.getHits();System.out.println(hits.getMaxScore());for (SearchHit hit : hits) {System.out.println("fields: " + hit.getFields());System.out.println("index is:" + hit.getIndex());System.out.println("document field is: " + hit.getDocumentFields());System.out.println("metadata field is: " + hit.getMetadataFields());System.out.println("score is: " + hit.getScore());System.out.println("-----------------");// 原始数据,不包含高亮的数据Map<String, Object> sourceMap = hit.getSourceAsMap();// 高亮数据,拿到高亮字段nameMap<String, org.elasticsearch.search.fetch.subphase.highlight.HighlightField> highlightFields = hit.getHighlightFields();HighlightField highlightTitle = highlightFields.get("name");// 将原始数据的name替换if (highlightTitle != null) {Text[] fragments = highlightTitle.getFragments();if (fragments != null && fragments.length > 0) {sourceMap.replace("name", fragments[0].toString());}}// 循环将数据添加入列表list.add(sourceMap);}list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();}}
}

2:各种查询构造器说明

2.1:MatchQueryBuilder
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "李四")// 李和四必须都出现在name字段中才可以, operator可以是OR.operator(Operator.AND)// 匹配度必须是 >= 75%才行.minimumShouldMatch("75%")// 是否忽略数据类型转换异常.lenient(true);
2.2:RegexQueryBuilder
// 张姓开头的doc
RegexpQueryBuilder regexpQueryBuilder = QueryBuilders.regexpQuery("name", "张*").caseInsensitive(true);
2.3:IdsQueryBuilder
String[] ids = new String[]{"1", "2", "3"};
IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds(ids);
2.4:MatchPhraseQueryBuilder
MatchPhraseQueryBuilder match = QueryBuilders.matchPhraseQuery("name", "李四").slop(2);
2.5:MatchPhrasePrefixQueryBuilder
MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = QueryBuilders.matchPhrasePrefixQuery("name", "张").maxExpansions(1);
2.6:MultiMatchQueryBuilder
String[] fieldNames = new String[]{"name", "age"};
String searchText = "老坛";
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(searchText, fieldNames)// 最多数量匹配.type(MultiMatchQueryBuilder.Type.MOST_FIELDS)// 使用and操作符和minimum_should_match参数来减少相关度低的文档数量.operator(Operator.AND);
2.7:TermQueryBuilder
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name.keyword", "李四");
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("name.keyword", "李四光", "李四");
2.8:FuzzyQueryBuilder
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name", "李四");
2.9:RangeQueryBuilder
RangeQueryBuilder ageRangeFilter = QueryBuilders.rangeQuery("age")// greater than 12.gt(12)// less than 17.lt(17);// 默认是true包含头尾,设置false去掉头尾
RangeQueryBuilder ageRangeFilter2 = QueryBuilders.rangeQuery("age").from(12).to(17)// 不包含最后一个元素.includeLower(false)// 包含第一个元素.includeUpper(true);
2.10:WildcardQueryBuilder
// wildcard 通配符查询, 支持*,匹配任何字符序列, 包括空,避免*
String queryString = "Lc*dd";
WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("name", queryString);
2.11:BoolQueryBuilder
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);
searchSourceBuilder.query(boolQueryBuilder);
2.12:其他的QueryBuilder
// 16: 其他查询
// moreLikeThisQuery: 实现基于内容推荐, 支持实现一句话相似文章查询
// percent_terms_to_match:匹配项(term)的百分比,默认是0.3
// min_term_freq:一篇文档中一个词语至少出现次数,小于这个值的词将被忽略,默认是2
// max_query_terms:一条查询语句中允许最多查询词语的个数,默认是25
// stop_words:设置停止词,匹配时会忽略停止词
// min_doc_freq:一个词语最少在多少篇文档中出现,小于这个值的词会将被忽略,默认是无限制
// max_doc_freq:一个词语最多在多少篇文档中出现,大于这个值的词会将被忽略,默认是无限制
// min_word_len:最小的词语长度,默认是0
// max_word_len:最多的词语长度,默认无限制
// boost_terms:设置词语权重,默认是1
// boost:设置查询权重,默认是1
// analyzer:设置使用的分词器,默认是使用该字段指定的分词器
QueryBuilder queryBuilder = QueryBuilders.moreLikeThisQuery(new String[]{"王"})// 一篇文档中一个词语至少出现次数,小于这个值的词将被忽略,默认是2.minTermFreq(1)// 一条查询语句中允许最多查询词语的个数,默认是25.maxQueryTerms(3);// 查询条件
searchSourceBuilder.query(matchQueryBuilder)// 后置过滤器,id, exists, term, range.postFilter(QueryBuilders.existsQuery("tag"));

3:聚合建造器说明

3.1:构造聚合条件
TermsAggregationBuilder aggregation = AggregationBuilders.terms(等于的值).field(字段名称) // 进行分桶操作.subAggregation(AggregationBuilders.avg(桶的名字,随便起的).field(根据xx字段分桶)) // 对每一个分桶,进行子聚合
3.2:将聚合条件交给builder,builder交给源,封装request
// 声明源
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 将查询建造器放入源,将高亮信息放入源,将聚合信息放入源
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.highlighter(highlightBuilder);
searchSourceBuilder.aggregation(aggregation);// 将源放入请求request
SearchRequest request = new SearchRequest(index).source(searchSourceBuilder);// 进行查询
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
3.3:结果解析和处理
// 结果解析
List<Object> ans = handleResult(response);
// 结果处理
for (Object o : ans) {System.out.println(o.toString());
}
// todo: 结果解析封装
/*** 聚合查询*/
public void aggTest() {// 声明查询请求,同时声明作用的索引(数据库)SearchRequest request = new SearchRequest();request.indices("person");// 声明searchSourceBuilder源对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// ======================== 源对象其他常见设置 ===========================// 1. 排序和浅显分页// from  & size 默认都是-1,也就是有多少显示多少// from-size浅分页适合数据量不大的情况(官网推荐是数据少于10000条),可以跳码进行查询searchSourceBuilder.from(0);searchSourceBuilder.size(9999);// 2. 排序, date、float 等类型添加排序, text类型的字段不允许排序searchSourceBuilder.sort("age", SortOrder.DESC).sort("name", SortOrder.ASC);// 构造查询建造器,根据不同的业务需求,进行不同的查询// =============== 以boolean为例 ==================BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("name.keyword", "王五")).mustNot(QueryBuilders.idsQuery().addIds("1", "2", "3"))// 可以进行bool嵌套.must(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "张三"))).boost(2.0f);// 请求构造器加入源中searchSourceBuilder.query(boolQueryBuilder);// ============ 如果有高亮设置 ===============HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.preTags("<font style='color: red'>");highlightBuilder.postTags("</font>");highlightBuilder.field("name").field("age");//同一字段中存在多个高亮值 设置都高亮highlightBuilder.requireFieldMatch(true);// 高亮属性中加入源中searchSourceBuilder.highlighter(highlightBuilder);TermsAggregationBuilder aggregation =// 进行分桶操作AggregationBuilders.terms("Jone").field("name")// 对每一个分桶,进行子聚合.subAggregation(AggregationBuilders.avg("age_avg").field("age"));// 将聚合结果放入源中searchSourceBuilder.aggregation(aggregation);// 进行查询// 1. 设置查询源(source -> request)request.source(searchSourceBuilder);// 2. 通过search方法进行查询List<Map<String, Object>> list = new ArrayList<>();try {SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 结果处理System.out.println("response.getHits().getTotalHits() = " + response.getHits().getTotalHits());// 然后从这个searchResponse解析各种的值 -> 根据返回结构int failedShards = response.getFailedShards();System.out.println("失败的分片数:" + failedShards);int successfulShards = response.getSuccessfulShards();System.out.println("成功的分片数:" + successfulShards);RestStatus status = response.status();System.out.println("状态:" + status);// 解析高亮数据SearchHits hits = response.getHits();System.out.println(hits.getMaxScore());for (SearchHit hit : hits) {System.out.println("fields: " + hit.getFields());System.out.println("index is:" + hit.getIndex());System.out.println("document field is: " + hit.getDocumentFields());System.out.println("metadata field is: " + hit.getMetadataFields());System.out.println("score is: " + hit.getScore());System.out.println("-----------------");// 原始数据,不包含高亮的数据Map<String, Object> sourceMap = hit.getSourceAsMap();// 高亮数据,拿到高亮字段nameMap<String, org.elasticsearch.search.fetch.subphase.highlight.HighlightField> highlightFields = hit.getHighlightFields();HighlightField highlightTitle = highlightFields.get("name");// 将原始数据的name替换if (highlightTitle != null) {Text[] fragments = highlightTitle.getFragments();if (fragments != null && fragments.length > 0) {sourceMap.replace("name", fragments[0].toString());}}// 循环将数据添加入列表list.add(sourceMap);}list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();}
}

三:repo & template

1:ElasticsearchRepository

1.1:repo介绍

ElasticsearchRepository接口封装了Document的CRUD操作,我们直接定义接口继承它即可。
在这里插入图片描述

ElasticsearchRepository接口的源码

package org.springframework.data.elasticsearch.repository;import java.io.Serializable;import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.repository.NoRepositoryBean;@NoRepositoryBean
public interface ElasticsearchRepository<T, ID extends Serializable> extends ElasticsearchCrudRepository<T, ID> {<S extends T> S index(S entity);Iterable<T> search(QueryBuilder query);Page<T> search(QueryBuilder query, Pageable pageable);Page<T> search(SearchQuery searchQuery);Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);void refresh();Class<T> getEntityClass();
}

CrudRepository源码

package org.springframework.data.repository;import java.util.Optional;/*** Interface for generic CRUD operations on a repository for a specific type.** @author Oliver Gierke* @author Eberhard Wolff*/
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {// 保存相关<S extends T> S save(S entity);<S extends T> Iterable<S> saveAll(Iterable<S> entities);// 查找相关Optional<T> findById(ID id);boolean existsById(ID id);Iterable<T> findAll();Iterable<T> findAllById(Iterable<ID> ids);// 计数long count();// delete相关void deleteById(ID id);void delete(T entity);void deleteAll(Iterable<? extends T> entities);void deleteAll();
}

PagingAndSortingRepository源码

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {Iterable<T> findAll(Sort sort);Page<T> findAll(Pageable pageable);
}

可见ElasticsearchRepository为我们封装好了很多常用的方法,我们可以在使用的时候直接调用这些方法进行操作

1.2:自定义方法命名规范

和所有的Spring Data一样,都支持指定格式的函数声明,对于这些函数,不用写具体的实现,而是可以直接调用

SpringData会通过动态代理的方式,帮我们生成基础的CRUD方法

自定义方法命名规范

  • findBy[fieldName]:根据指定的单个条件进行等值查询;
  • findBy[fieldName]And[fieldName]And[...]:根据指定的多条件进行and查询;
  • findBy[fieldName]Or[fieldName]Or[...]:根据指定的多条件进行or查询;
  • findBy[fieldName]Equals:根据指定的单个条件进行等值查询;
  • findBy[fieldName]In:对指定的单个字段进行in查询,入参为一个列表;
  • findBy[fieldName]Like:对指定的单个字段进行like模糊查询;
  • findBy[fieldName]NotNull:查询指定字段不为空的数据;
  • findBy[fieldName]GreaterThan:对指定的单个字段进行]范围查询;
  • findBy[fieldName]GreaterThanEqual:对指定的单个字段进行]=范围查询;
  • findBy[fieldName]LessThan:对指定的单个字段进行[范围查询;
  • findBy[fieldName]LessThanEqual:对指定的单个字段进行[=范围查询;
  • Page[...] findBy[...]:根据指定的条件进行分页查询;
  • countBy[fieldName]:根据指定的条件字段进行计数统计;
  • findTop[n]By[fieldName]:根据指定字段做等值查询,并返回前n条数据;
  • findBy[fieldName]Between:根据指定字段进行between范围查询;
  • findDistinctBy[fieldName]:根据指定的单个条件进行去重查询;
  • findFirstBy[fieldName]:根据指定的单个条件进行等值查询(只返回满足条件的第一个数据);
  • findBy[fieldName1]OrderBy[fieldName2]:根据第一个字段做等值查询,并根据第二个字段做排序;

各种方法名开头含义

  • get、find、read、query、stream开头,代表是查询数据的方法;
  • count开头,代表是计数统计的方法;
  • delete、remove开头,代表是删除数据的方法;
  • exists开头,代表是判断是否存在的方法;
  • search开头,代表是全文搜索的方法;
  • update开头,代表是修改数据的方法;

方法名开头后面跟的关键字含义,以find开头的方法为例:

  • By:表示当前方法生成的查询语句,会根据By后面的逻辑来组成;
  • FirstBy:表示当前方法生成的语句,只会返回符合条件的第一条数据;
  • DistinctBy:表示当前方法生成的语句,会对符合条件的数据去重;
  • TopBy:表示当前方法生成的语句,只会返回符合条件的前N条数据;
  • [实体类名称]By:表示当前方法生成的语句,只会返回一条数据;
  • [实体类名称]sBy:表示当前方法生成的语句,会返回多条数据;
  • AllBy:表示当前方法生成的语句,会返回多条或所有数据;
  • DistinctFirstBy:表示当前方法生成的语句,只会返回去重后的第一条数据;
  • DistinctTopBy:表示当前方法生成的语句,只会返回去重后的前N条数据;

方法名开头跟的关键字之后跟的是字段名[fieldName], 字段名称后面可以接的关键字如下

在这些关键字之后,都是跟具体的字段名(实体类的属性名),字段名称后面可以接的关键字如下(同样以find为例):

  • Or:表示当前查询方法有多个条件,多个条件之间为“或者”关系;
  • And:表示当前查询方法有多个条件,多个条件之间为“并且”关系;
  • OrderBy:表示当前查询会涉及到排序,后面需要跟一个排序字段;
  • Between:表示当前方法为between范围查询;
  • GreaterThan:表示当前方法为>查询;
  • GreaterThanEqual:表示当前方法为>=查询;
  • LessThan:表示当前方法为 < 查询;
  • LessThanEqual:表示当前方法为<=查询;
  • After:和GreaterThan差不多,相当于查询指定数值之后的数据;
  • Before:和LessThan差不多,查询指定条件之前的数据;
  • Containing:查询某字段中包含指定字符的数据;
  • Empty:表示当前方法会查询指定字段为空的数据,与之含义类似的还有Null、Exists;
  • Equals:表示当前方法会根据指定字段做等值查询;
  • Is:和Equals差不多;
  • In:表示当前方法为in多值匹配查询;
  • Like:表示当前方法为like模糊查询;
  • Not:可以和上述大多数关键字组合,带有Not的则含义相反,如NotEmpty表示不为空;

在这里插入图片描述
在这里插入图片描述

2:ElasticsearchRestTemplate

2.1:template介绍

template提供了众多模板方法,只要我们编写好对应的条件,然后调用模板方法即可

在这里插入图片描述
在这里插入图片描述

2.2:template查询操作

下面是操作流程【查询为例】

2.2.1:入参特殊情况处理【包括异常入参】
2.2.2:构建查询条件Criteria
// 1:构建查询条件
Criteria criteria = new Criteria()// 条件一:xxxx.and(new Criteria("字段名称").contains(条件中的内容))// 条件二:xxx.and(new Criteria("字段名称").is(条件中的内容));
// 2:封装进入到CriteriaQuery
CriteriaQuery filter = new CriteriaQuery(criteria);
// 3:设置分页信息【可能没有,看业务】
CriteriaQuery criteriaQuery = filter.setPageable(PageRequest.of(pageIndex, pageSize));
2.2.3:构建高亮条件
// 1:声明高亮构造器对象
HighlightBuilder highlightBuilder = new HighlightBuilder(); 
// todo: 设置高亮领域
// todo: 前置后置标签// 2:构建高亮query
HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
// 3:将高亮query封装进criteriaQuery中
criteriaQuery.setHighlightQuery(highlightQuery);
2.2.4:查询(也可能是其他的方法,不一定是search)
elasticsearchRestTemplate.search(查询条件,返回信息的实体.class);
2.2.5:结果封装
// 对结果进行封装
2.3:创建索引和删除索引

对于索引的创建和删除,建议还是直接使用Kibana进行相关的操作,如果要使用template,可以输入对应的索引名称进行创建

template会到实体中找到@Document(indexName = "xxx")对应的信息,根据这个注解的配置拿到settings对应的信息配置

然后根据实体类标注的字段类型和分析器类型创建对应的mappings对应的信息配置

package com.example.es_demo.pojo;import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.time.LocalDateTime;/*** <p>* 功能描述:实体类* </p>** @author cui haida* @date 2024/01/28/8:20*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Document(indexName = "mytest") // 指定对应的索引名称
public class MyTest {@Id@JSONField(serialize = false)private String id;@Field(type = FieldType.Keyword)private String name;@Field(type = FieldType.Keyword)private String age;// text类型,并使用IK最粗粒度的分词器,检索时的分词器采用的是最细粒度的IK分词器@Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_max_word")private String address;// 注意日期格式的特殊处理@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime createTime;
}
public String create(String indexName) {IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName));if (indexOperations.exists()) {return "索引已存在";}indexOperations.create();return "索引创建成功";
}
public String delete(String indexName) {IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName));indexOperations.delete();return "索引删除成功";
}

3:使用实例

package com.cui.es_demo.client;import com.cui.es_demo.client.repo.PersonRepository;
import com.cui.es_demo.entity.PageResponse;
import com.cui.es_demo.entity.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** @author cui haida* 2025/1/30*/
@Service
@Slf4j
public class TemplateDemo {// es repo -> 这里提供了Spring Data的基本方法,可以直接使用@AutowiredPersonRepository personRepository;// template@AutowiredElasticsearchRestTemplate elasticsearchRestTemplate;// =========== repo测试 =============// 就是直接调用对应的方法,只要语法满足上面说的Spring Data语法就可以public void saveAll(List<Person> orders) {personRepository.saveAll(orders);}public void deleteById(Integer id) {personRepository.deleteById(id);}public void updateById(Person person) {personRepository.save(person);}public Person findById(Integer id) {return (Person) personRepository.findById(id).orElse(null);}public PageResponse<Person> findAll(Integer pageIndex, Integer pageSize) {Page<Person> page = personRepository.findAll(PageRequest.of(pageIndex, pageSize));PageResponse<Person> pageResponse = new PageResponse<Person>();pageResponse.setTotal(page.getTotalElements());pageResponse.setResult(page.getContent());return pageResponse;}// =============== template测试 ==============// 1:构造查询条件// 2:调用template.xxx(条件)// 3:结果处理(封装)public PageResponse<Person> findList(Person person, Integer pageIndex, Integer pageSize) {// 1:构建查询条件Criteria criteria = new Criteria()// 条件一:文档中的orderDesc字段要包含查询条件的orderDesc字段.and(new Criteria("orderDesc").contains(person.getAddress()))// 条件二:文档中的orderNo字段要等于查询条件的orderNo字段.and(new Criteria("orderNo").is(person.getId()));// 封装进入到CriteriaQueryCriteriaQuery filter = new CriteriaQuery(criteria);// 设置分页信息CriteriaQuery criteriaQuery = filter.setPageable(PageRequest.of(pageIndex, pageSize));// 2:查询 -> elasticsearchRestTemplate.search(查询条件,返回信息的实体.class)SearchHits<Person> searchHits = elasticsearchRestTemplate.search(criteriaQuery, Person.class);// 3:结果封装List<Person> result = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());PageResponse<Person> pageResponse = new PageResponse<>();pageResponse.setTotal(searchHits.getTotalHits());pageResponse.setResult(result);return pageResponse;}public PageResponse<Person> findHighlight(Person person, Integer pageIndex, Integer pageSize) {// 0:特殊情况的处理if (person == null) {PageResponse<Person> pageResponse = new PageResponse<Person>();pageResponse.setTotal(0L);pageResponse.setResult(new ArrayList<>());return pageResponse;}// 1:构建查询条件Criteria criteria = new Criteria()// 条件一:文档中的orderDesc字段要包含查询条件的orderDesc字段.and(new Criteria("id").contains(person.getId()))// 条件二:文档中的orderNo字段要等于查询条件的orderNo字段.and(new Criteria("address").is(person.getAddress()));// 封装进入到CriteriaQueryCriteriaQuery filter = new CriteriaQuery(criteria);// 设置分页信息CriteriaQuery criteriaQuery = filter.setPageable(PageRequest.of(pageIndex, pageSize));// 3:查询 -> elasticsearchRestTemplate.search(查询条件,返回信息的实体.class)SearchHits<Person> searchHits = elasticsearchRestTemplate.search(criteriaQuery, Person.class);// 4:结果封装// 对每一个都设置对应的高亮字段信息List<Person> result = searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());// 封装页码信息PageResponse<Person> pageResponse = new PageResponse<>();pageResponse.setTotal(searchHits.getTotalHits());pageResponse.setResult(result);return pageResponse;}
}

相关文章:

Spring Boot - 数据库集成06 - 集成ElasticSearch

Spring boot 集成 ElasticSearch 文章目录 Spring boot 集成 ElasticSearch一&#xff1a;前置工作1&#xff1a;项目搭建和依赖导入2&#xff1a;客户端连接相关构建3&#xff1a;实体类相关注解配置说明 二&#xff1a;客户端client相关操作说明1&#xff1a;检索流程1.1&…...

Java篇之继承

目录 一. 继承 1. 为什么需要继承 2. 继承的概念 3. 继承的语法 4. 访问父类成员 4.1 子类中访问父类的成员变量 4.2 子类中访问父类的成员方法 5. super关键字 6. super和this关键字 7. 子类构造方法 8. 代码块的执行顺序 9. protected访问修饰限定符 10. 继承方式…...

32. C 语言 安全函数( _s 尾缀)

本章目录 前言什么是安全函数&#xff1f;安全函数的特点主要的安全函数1. 字符串操作安全函数2. 格式化输出安全函数3. 内存操作安全函数4. 其他常用安全函数 安全函数实例示例 1&#xff1a;strcpy_s 和 strcat_s示例 2&#xff1a;memcpy_s示例 3&#xff1a;strtok_s 总结 …...

ArkTS编程规范

文章目录 目标和适用范围规则来源章节概览代码风格编程实践 术语和定义总体原则命名类名、枚举名、命名空间名采用UpperCamelCase风格变量名、方法名、参数名采用lowerCamelCase风格常量名、枚举值名采用全部大写&#xff0c;单词间使用下划线隔开避免使用否定的布尔变量名&…...

SQL进阶实战技巧:断点去重技术详解

目录 一、核心概念 二、典型应用场景 三、实现步骤与SQL示例 场景 目标 步骤 分析 结果 四、核心原理解释 1. 核心原理&#xff1a;相邻比较 2. 去重的本质 3. 与传统方法的对比 4 类别理解 五、如何应对复杂场景&#xff1f; 1. 多字段断点检测 2. 时间窗口断点 …...

深度学习之“向量范数和距离度量”

在深度学习中&#xff0c;范数和向量距离是两个不同的概念。向量范数是一种函数&#xff0c;用于将一个实数或复数向量映射为一个值。虽然范数通常用于度量向量之间的距离&#xff0c;但是同样也有其它的一些表示距离的方式。 范数距离 范数是具有“长度”概念的函数。在向量…...

基于Python的简单企业维修管理系统的设计与实现

以下是一个基于Python的简单企业维修管理系统的设计与实现&#xff0c;这里我们会使用Flask作为Web框架&#xff0c;SQLite作为数据库来存储相关信息。 1. 需求分析 企业维修管理系统主要功能包括&#xff1a; 维修工单的创建、查询、更新和删除。设备信息的管理。维修人员…...

javascript常用函数大全

javascript函数一共可分为五类&#xff1a; •常规函数 •数组函数 •日期函数 •数学函数 •字符串函数 1.常规函数 javascript常规函数包括以下9个函数&#xff1a; (1)alert函数&#xff1a;显示一个警告对话框&#xff0c;包括一个OK按钮。 (2)confirm函数&#xff1a;显…...

【Leetcode 每日一题】81. 搜索旋转排序数组 II

问题背景 已知存在一个按非降序排列的整数数组 n u m s nums nums&#xff0c;数组中的值不必互不相同。 在传递给函数之前&#xff0c; n u m s nums nums 在预先未知的某个下标 k ( 0 < k < n u m s . l e n g t h ) k\ (0 < k < nums.length) k (0<k<…...

< OS 有关 > Android 手机 SSH 客户端 app: connectBot

connectBot 开源且功能齐全的SSH客户端,界面简洁,支持证书密钥。 下载量超 500万 方便在 Android 手机上&#xff0c;连接 SSH 服务器&#xff0c;去运行命令。 Fail2ban 12小时内抓获的 IP ~ ~ ~ ~ rootjpn:~# sudo fail2ban-client status sshd Status for the jail: sshd …...

【算法设计与分析】实验7:复杂装载及0/1背包问题的回溯法设计与求解

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 针对复杂装载问题、及0/1背包问题开展分析、建模、评价&#xff0c;算法设计与优化&#xff0c;并进行编码实践。 理解复杂装载…...

仿真设计|基于51单片机的温湿度、一氧化碳、甲醛检测报警系统

目录 具体实现功能 设计介绍 51单片机简介 资料内容 仿真实现&#xff08;protues8.7&#xff09; 程序&#xff08;Keil5&#xff09; 全部内容 资料获取 具体实现功能 &#xff08;1&#xff09;温湿度传感器、CO传感器、甲醛传感器实时检测温湿度值、CO值和甲醛值进…...

使用vhd虚拟磁盘安装两个win10系统

使用vhd虚拟磁盘安装两个win10系统 前言vhd虚拟磁盘技术简介准备工具开始动手实践1.winX选择磁盘管理2.选择“操作”--“创建VHD”3.自定义一个位置&#xff0c;输入虚拟磁盘大小4.右键初始化磁盘5.选择GPT分区表格式6.右键新建简单卷7.给卷起个名字&#xff0c;用于区分8.打开…...

Python学习——函数参数详解

Python中的函数参数传递机制允许多种灵活的参数类型&#xff0c;可以根据需求灵活配置参数&#xff0c;这使得函数具有更强大的扩展性和适应性。以下是对各类参数类型的详细说明&#xff1a; 1. 定义函数的不同参数类型 1.1 位置参数 定义方式&#xff1a;def func(a, b2) 特…...

深入理解Spring事务管理

一、事务基础概念 1.1 什么是事务&#xff1f; 事务&#xff08;Transaction&#xff09;是数据库操作的最小工作单元&#xff0c;具有ACID四大特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务中的操作要么全部成功&#xff0c;要么全部失败 一致…...

自制虚拟机(C/C++)(二、分析引导扇区,虚拟机读二进制文件img软盘)

先修复上一次的bug&#xff0c;添加新指令&#xff0c;并增加图形界面 #include <graphics.h> #include <conio.h> #include <windows.h> #include <commdlg.h> #include <iostream> #include <fstream> #include <sstream> #inclu…...

基于最近邻数据进行分类

人工智能例子汇总:AI常见的算法和例子-CSDN博客 完整代码: import torch import numpy as np from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score import matplotlib.pyplot as plt# 生成一个简单的数据集 (2个特征和2个分类…...

ASP.NET Core 启动并提供静态文件

ASP.NET Core 启动并提供静态文件 即是单个可执行文件&#xff0c;它既运行 API 项目&#xff0c;也托管 前端项目&#xff08;通常是前端的发布文件&#xff09;。 这种方式一般是通过将 前端项目 的发布文件&#xff08;例如 HTML、CSS、JavaScript&#xff09;放入 Web AP…...

【异步编程】CompletableFuture:异步任务的选择(执行最快的)执行

文章目录 一. applyToEither : 拿到第一个任务结束的结果二. runAfterEither &#xff1a;第一个任务完成后执行副作用三. acceptEither&#xff1a;消费第一个任务的结果四. 三种接口总结 对于两个异步任务&#xff0c;我们有时希望在其中一个任务完成时立即执行某些操作&…...

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起&#xff0c; GitHub 上不断出现“幽灵仓库”&#xff0c;仓库中没有任何代码&#xff0c;只有诱导性的病毒文件。当天&#xff0c;他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒&#xff0c;等待不…...

变量和常量

一.变量 1.标准声明 var 变量名 变量类型 变量声明行末不需要分号 2..批量声明 package main import "fmt" func main(){var(a string b int c boold float32)}3.变量的初始化 var a int 10 var b float321.1 4.类型推导 var name"tom" var age18 fmt.Pr…...

大模型概述(方便不懂技术的人入门)

1 大模型的价值 LLM模型对人类的作用&#xff0c;就是一个百科全书级的助手。有多么地百科全书&#xff0c;则用参数的量来描述&#xff0c; 一般地&#xff0c;大模型的参数越多&#xff0c;则该模型越好。例如&#xff0c;GPT-3有1750亿个参数&#xff0c;GPT-4可能有超过1万…...

流浪 Linux: 外置 USB SSD 安装 ArchLinux

注: ArchLinux 系统为滚动更新, 变化很快, 所以本文中的安装方法可能很快就过时了, 仅供参考. 实际安装时建议去阅读官方文档. 最近, 突然 (也没有那么突然) 有了一大堆 PC: 4 个笔记本, 2 个台式主机 (M-ATX 主板), 1 个小主机 (迷你主机). 嗯, 多到用不过来. 但是, 窝又不能…...

Hot100之子串

560和为K的子数组 题目 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列 思路解析 ps&#xff1a;我们的presum【0】就是0&#xff0c;如果没有这个0的话我们的第一个元素就无法减去上…...

网络工程师 (11)软件生命周期与开发模型

一、软件生命周期 前言 软件生命周期&#xff0c;也称为软件开发周期或软件开发生命周期&#xff0c;是指从软件项目的启动到软件不再被使用为止的整个期间。这个过程可以细分为多个阶段&#xff0c;每个阶段都有其特定的目标、任务和产出物。 1. 问题定义与需求分析 问题定义…...

(三)QT——信号与槽机制——计数器程序

目录 前言 信号&#xff08;Signal&#xff09;与槽&#xff08;Slot&#xff09;的定义 一、系统自带的信号和槽 二、自定义信号和槽 三、信号和槽的扩展 四、Lambda 表达式 总结 前言 信号与槽机制是 Qt 中的一种重要的通信机制&#xff0c;用于不同对象之间的事件响…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础图形库实现)

目录 基础图形库的抽象 抽象图形 抽象点 设计我们的抽象 实现我们的抽象 测试 抽象线 设计我们的抽象 实现我们的抽象 绘制垂直的和水平的线 使用Bresenham算法完成任意斜率的绘制 绘制三角形和矩形 矩形 三角形 实现 绘制圆&#xff0c;圆弧和椭圆 继续我们的…...

hot100_21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[…...

安全防护前置

就业概述 网络安全工程师/安全运维工程师/安全工程师 安全架构师/安全专员/研究院&#xff08;数学要好&#xff09; 厂商工程师&#xff08;售前/售后&#xff09; 系统集成工程师&#xff08;所有计算机知识都要会一点&#xff09; 学习目标 前言 网络安全事件 蠕虫病毒--&…...

01-六自由度串联机械臂(ABB)位置分析

ABB工业机器人&#xff08;IRB2600&#xff09;如下图所示&#xff08;d1444.8mm&#xff0c;a1150mm&#xff0c;a2700mm&#xff0c;a3115mm&#xff0c;d4795mm&#xff0c;d685mm&#xff09;&#xff0c;利用改进DH法建模&#xff0c;坐标系如下所示&#xff1a; 利用改进…...