科普文:微服务之全文检索SpringBoot整合ElasticSearch说明

一、RestHighLevelClient介绍

JavaREST客户端有两种模式:

  • Java Low Level REST Client:ES官方的低级客户端。低级别的客户端通过http与Elasticsearch集群通信。
  • Java High Level REST Client:ES官方的高级客户端。基于上面的低级客户端,也是通过HTTP与ES集群进行通信。它提供了更多的接口。

下面介绍下SpringBoot如何通过elasticsearch-rest-high-level-client工具操作ElasticSearch。当然也可以通过spring-data-elasticsearch来操作ElasticSearch,而本文仅是elasticsearch-rest-high-level-client的案例介绍,所以本文中我并未使用spring-data-elasticsearch,后期我会补上。

注意事项:客户端(Client)Jar包的版本尽量不要大于Elasticsearch本体的版本,否则可能出现客户端中使用的某些API在Elasticsearch中不支持。

这里需要说一下,能使用RestHighLevelClient尽量使用它,为什么不推荐使用Spring家族封装的spring-data-elasticsearch。主要原因是灵活性和更新速度,Spring将ElasticSearch过度封装,让开发者很难跟ES的DSL查询语句进行关联。再者就是更新速度,ES的更新速度是非常快,但是spring-data-elasticsearch更新速度比较缓慢。并且spring-data-elasticsearch在Elasticsearch6.x和7.x版本上的Java API差距很大,如果升级版本需要花点时间来了解。

TIPS:spring-data-elasticsearch的底层其实也是否则了elasticsearch-rest-high-level-client的api。

二、引入依赖

特别注意:引入的依赖最好与SpringBoot中的版本一样,免得出现版本冲突。

<!--引入es-high-level-client的坐标-->
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.6.2</version>
</dependency>
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.6.2</version>
</dependency>
<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.6.2</version>
</dependency><!--mybatis-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>

完整的Maven依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.thr.elasticsearch</groupId><artifactId>elasticsearch-rest-high-level-client-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>elasticsearch-rest-high-level-client-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><!--引入es-high-level-client的坐标--><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.6.2</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.6.2</version></dependency><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.6.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.72</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.thr.elasticsearch.ESRestHighLevelClientApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

三、ES的配置

(1)、创建索引

PUT /goods
{"mappings": {"properties": {"brandName": {"type": "keyword"},"categoryName": {"type": "keyword"},"createTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"},"id": {"type": "keyword"},"price": {"type": "double"},"saleNum": {"type": "integer"},"status": {"type": "integer"},"stock": {"type": "integer"},"title": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"}}}
}

(2)、application.yml 配置文件

elasticsearch:host: 116.205.230.143port: 9200
spring:# 应用名称application:name: elasticsearch-spring-datadatasource:username: rootpassword: 123456url: jdbc:mysql://116.205.230.143:3306/es?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&allowMultiQueries=truedriver-class-name: com.mysql.cj.jdbc.Driverelasticsearch:rest:# 定位ES的位置uris: http://116.205.230.143:9200
mybatis:type-aliases-package: com.thr.elastisearch.domainmapper-locations: classpath:mapper/*.xml

(3)、java 连接配置类

写一个Java配置类读取application中的配置信息:

/*** ES的配置类* ElasticSearchConfig** @author tanghaorong*/
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {private String host;private Integer port;/*** 如果@Bean没有指定bean的名称,那么这个bean的名称就是方法名*/@Beanpublic RestHighLevelClient restHighLevelClient() {return new RestHighLevelClient(RestClient.builder(new HttpHost(host, port, "http")));}
}

(4)、mybatis配置

/*** Mapper接口** @author tanghaorong*/
@Repository
@Mapper
public interface GoodsMapper {/*** 查询所有*/List<Goods> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.thr.elasticsearch.dao.GoodsMapper"><select id="findAll" resultType="com.thr.elasticsearch.domain.Goods">select `id`,`title`,`price`,`stock`,`saleNum`,`createTime`,`categoryName`,`brandName`,`status`from goods</select>
</mapper>

(5)、实体对象

@Data
@Accessors(chain = true)   // 链式赋值(连续set方法)
@AllArgsConstructor        // 全参构造
@NoArgsConstructor         // 无参构造
public class Goods {/*** 商品编号*/private Long id;/*** 商品标题*/private String title;/*** 商品价格*/private BigDecimal price;/*** 商品库存*/private Integer stock;/*** 商品销售数量*/private Integer saleNum;/*** 商品分类*/private String categoryName;/*** 商品品牌*/private String brandName;/*** 上下架状态*/private Integer status;/*** 商品创建时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date createTime;
}

(6)、测试类

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESRestHighLevelClientApplicationTests {@Testpublic void test1() throws IOException {}
}

需要注意的是,测试启动类要和项目的启动类位于同一个包中,否则启动可能会报错。

(7)、项目整体结构

四、索引操作

/*** 创建索引库和映射表结构* 注意:索引一般不会怎么创建*/
@Test
public void indexCreate() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建索引CreateIndexRequest indexRequest = new CreateIndexRequest("goods111");// 创建表 结构String mapping = "{\n" +"    \"properties\": {\n" +"      \"brandName\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"categoryName\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"createTime\": {\n" +"        \"type\": \"date\",\n" +"        \"format\": \"yyyy-MM-dd HH:mm:ss\"\n" +"      },\n" +"      \"id\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"price\": {\n" +"        \"type\": \"double\"\n" +"      },\n" +"      \"saleNum\": {\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"status\": {\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"stock\": {\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"title\": {\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_max_word\",\n" +"        \"search_analyzer\": \"ik_smart\"\n" +"      }\n" +"    }\n" +"  }";// 把映射信息添加到request请求里面// 第一个参数:表示数据源// 第二个参数:表示请求的数据类型indexRequest.mapping(mapping, XContentType.JSON);// 请求服务器CreateIndexResponse response = indicesClient.create(indexRequest, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());
}/*** 获取表结构* GET goods/_mapping*/
@Test
public void getMapping() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建get请求GetIndexRequest request = new GetIndexRequest("goods");// 发送get请求GetIndexResponse response = indicesClient.get(request, RequestOptions.DEFAULT);// 获取表结构Map<String, MappingMetaData> mappings = response.getMappings();for (String key : mappings.keySet()) {System.out.println("key--" + mappings.get(key).getSourceAsMap());}
}/*** 删除索引库*/
@Test
public void indexDelete() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建delete请求方式DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("goods");// 发送delete请求AcknowledgedResponse response = indicesClient.delete(deleteIndexRequest, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());
}/*** 判断索引库是否存在*/
@Test
public void indexExists() throws Exception {IndicesClient indicesClient = restHighLevelClient.indices();// 创建get请求GetIndexRequest request = new GetIndexRequest("goods");// 判断索引库是否存在boolean result = indicesClient.exists(request, RequestOptions.DEFAULT);System.out.println(result);
}

五、文档操作

/*** 增加文档信息*/
@Test
public void addDocument() throws IOException {// 创建商品信息Goods goods = new Goods();goods.setId(1L);goods.setTitle("Apple iPhone 13 Pro (A2639) 256GB 远峰蓝色 支持移动联通电信5G 双卡双待手机");goods.setPrice(new BigDecimal("8799.00"));goods.setStock(1000);goods.setSaleNum(599);goods.setCategoryName("手机");goods.setBrandName("Apple");goods.setStatus(0);goods.setCreateTime(new Date());// 将对象转为jsonString data = JSON.toJSONString(goods);// 创建索引请求对象IndexRequest indexRequest = new IndexRequest("goods").id(goods.getId() + "").source(data, XContentType.JSON);// 执行增加文档IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);log.info("创建状态:{}", response.status());
}/*** 获取文档信息*/
@Test
public void getDocument() throws IOException {// 创建获取请求对象GetRequest getRequest = new GetRequest("goods", "1");GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);System.out.println(response.getSourceAsString());
}/*** 更新文档信息*/
@Test
public void updateDocument() throws IOException {// 设置商品更新信息Goods goods = new Goods();goods.setTitle("Apple iPhone 13 Pro Max (A2644) 256GB 远峰蓝色 支持移动联通电信5G 双卡双待手机");goods.setPrice(new BigDecimal("9999"));// 将对象转为jsonString data = JSON.toJSONString(goods);// 创建索引请求对象UpdateRequest updateRequest = new UpdateRequest("goods", "1");// 设置更新文档内容updateRequest.doc(data, XContentType.JSON);// 执行更新文档UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);log.info("创建状态:{}", response.status());
}/*** 删除文档信息*/
@Test
public void deleteDocument() throws IOException {// 创建删除请求对象DeleteRequest deleteRequest = new DeleteRequest("goods", "1");// 执行删除文档DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);log.info("删除状态:{}", response.status());
}

六、导入测试数据

下载测试数据

下载链接:https://files.cnblogs.com/files/tanghaorong/goods.zip?t=1654416464

下载后导入数据库中,大概有900多条。

导入测试数据至ES中:

/*** 批量导入测试数据*/
@Test
public void importData() throws IOException {//1.查询所有数据,mysqlList<Goods> goodsList = goodsMapper.findAll();//2.bulk导入BulkRequest bulkRequest = new BulkRequest();//2.1 循环goodsList,创建IndexRequest添加数据for (Goods goods : goodsList) {//将goods对象转换为json字符串String data = JSON.toJSONString(goods);//map --> {}IndexRequest indexRequest = new IndexRequest("goods");indexRequest.id(goods.getId() + "").source(data, XContentType.JSON);bulkRequest.add(indexRequest);}BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);System.out.println(response.status());
}

导入成功。

七、DSL高级查询操作

7.1 精确查询(term)

term查询:不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配,也就是精确查找,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型)

terms查询:terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去 做匹配:

/*** 精确查询(termQuery)*/
@Test
public void termQuery() {try {// 构建查询条件// (注意:termQuery支持多种格式查询,如boolean、int、double、string等,这里使用的是string的查询)SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.termQuery("title", "华为"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info("=======" + userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** terms:多个查询内容在一个字段中进行查询*/
@Test
public void termsQuery() {try {// 构建查询条件// (注意:termsQuery支持多种格式查询,如boolean、int、double、string等,这里使用的是string的查询)SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.termsQuery("title", "华为", "OPPO", "TCL"));// 展示100条,默认只展示10条记录searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.2 全文查询(match)

全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集。

term和match的区别是:match是经过analyer的,也就是说,文档首先被分析器给处理了。根据不同的分析器,分析的结果也稍显不同,然后再根据分词结果进行匹配。term则不经过分词,它是直接去倒排索引中查找了精确的值了。

match 查询语法汇总:

  1. match_all:查询全部。
  2. match:返回所有匹配的分词。
  3. match_phrase:短语查询,在match的基础上进一步查询词组,可以指定slop分词间隔。
  4. match_phrase_prefix:前缀查询,根据短语中最后一个词组做前缀匹配,可以应用于搜索提示,但注意和max_expanions搭配。其实默认是50.......
  5. multi_match:多字段查询,使用相当的灵活,可以完成match_phrase和match_phrase_prefix的工作。
/*** 匹配查询符合条件的所有数据,并设置分页*/
@Test
public void matchAllQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from(0);searchSourceBuilder.size(3);// 设置排序searchSourceBuilder.sort("price", SortOrder.ASC);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 匹配查询数据*/
@Test
public void matchQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.matchQuery("title", "华为"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 词语匹配查询*/
@Test
public void matchPhraseQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("title", "三星"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 内容在多字段中进行查询*/
@Test
public void matchMultiQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.multiMatchQuery("手机", "title", "categoryName"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.3 通配符查询(wildcard)

wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)

/*** 查询所有以 “三” 结尾的商品信息* <p>* *:表示多个字符(0个或多个字符)* ?:表示单个字符*/
@Test
public void wildcardQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.wildcardQuery("title", "*三"));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.4 模糊查询(fuzzy)

/*** 模糊查询所有以 “三” 结尾的商品信息*/
@Test
public void fuzzyQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.fuzzyQuery("title", "三").fuzziness(Fuzziness.AUTO));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.5 排序查询(sort)

注意:需要分词的字段不可以直接排序,比如:text类型,如果想要对这类字段进行排序,需要特别设置:对字段索引两次,一次索引分词(用于搜索)一次索引不分词(用于排序),es默认生成的text类型字段就是通过这样的方法实现可排序的。

/*** 排序查询(sort) 代码同matchAllQuery* 匹配查询符合条件的所有数据,并设置分页*/
@Test
public void matchAllQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from(0);searchSourceBuilder.size(3);// 设置排序searchSourceBuilder.sort("price", SortOrder.ASC);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.6 分页查询(page)

Elasticsearch的分页查询和 SQL 使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch 接受 from 和 size 参数:

  • size : 结果数,默认10
  • from : 跳过开始的结果数,即从哪一行开始获取数据,默认0

这种方式分页查询如果需要深度分页,那么这种方式性能不太好。

/*** 分页查询(page) 代码同matchAllQuery* 匹配查询符合条件的所有数据,并设置分页*/
@Test
public void matchAllQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 设置分页searchSourceBuilder.from(0);searchSourceBuilder.size(3);// 设置排序searchSourceBuilder.sort("price", SortOrder.ASC);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.7 滚动查询(scroll)

滚动查询可以优化ES的深度分页,但是需要维护scrollId

/*** 根据查询条件滚动查询* 可以用来解决深度分页查询问题*/
@Test
public void scrollQuery() {// 假设用户想获取第70页数据,其中每页10条int pageNo = 70;int pageSize = 10;// 定义请求对象SearchRequest searchRequest = new SearchRequest("goods");// 构建查询条件SearchSourceBuilder builder = new SearchSourceBuilder();searchRequest.source(builder.query(QueryBuilders.matchAllQuery()).size(pageSize));String scrollId = null;// 3、发送请求到ESSearchResponse scrollResponse = null;// 设置游标id存活时间Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2));// 记录所有游标idList<String> scrollIds = new ArrayList<>();for (int i = 0; i < pageNo; i++) {try {// 首次检索if (i == 0) {//记录游标idsearchRequest.scroll(scroll);// 首次查询需要指定索引名称和查询条件SearchResponse response = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 下一次搜索要用到该游标idscrollId = response.getScrollId();// 记录所有游标id}// 非首次检索else {// 不需要在使用其他条件,也不需要指定索引名称,// 只需要使用执行游标id存活时间和上次游标id即可,毕竟信息都在上次游标id里面呢SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);searchScrollRequest.scroll(scroll);scrollResponse = restHighLevelClient.scroll(searchScrollRequest,RequestOptions.DEFAULT);// 下一次搜索要用到该游标idscrollId = scrollResponse.getScrollId();// 记录所有游标id}scrollIds.add(scrollId);} catch (Exception e) {e.printStackTrace();}}//清除游标idClearScrollRequest clearScrollRequest = new ClearScrollRequest();clearScrollRequest.scrollIds(scrollIds);try {restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);} catch (IOException e) {System.out.println("清除滚动查询游标id失败");e.printStackTrace();}// 4、处理响应结果System.out.println("滚动查询返回数据:");assert scrollResponse != null;SearchHits hits = scrollResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}
}

7.8 范围查询(range)

/*** 查询价格大于等于10000的商品信息*/
@Test
public void rangeQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.rangeQuery("price").gte(10000));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();log.info(hits.getTotalHits().value + "");for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}/*** 查询距离现在 10 年间的商品信息* [年(y)、月(M)、星期(w)、天(d)、小时(h)、分钟(m)、秒(s)]* 例如:* now-1h 查询一小时内范围* now-1d 查询一天内时间范围* now-1y 查询最近一年内的时间范围*/
@Test
public void dateRangeQuery() {try {// 构建查询条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// includeLower(是否包含下边界)、includeUpper(是否包含上边界)searchSourceBuilder.query(QueryBuilders.rangeQuery("createTime").gte("now-10y").includeLower(true).includeUpper(true));// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods userInfo = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(userInfo.toString());}}} catch (IOException e) {log.error("", e);}
}

7.9 布尔查询(bool)

bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:

  • must:多个查询条件必须完全匹配,相当于关系型数据库中的 and。
  • should:至少有一个查询条件匹配,相当于关系型数据库中的 or。
  • must_not: 多个查询条件的相反匹配,相当于关系型数据库中的 not。
  • filter:过滤满足条件的数据。
    • range:条件筛选范围。
      • gt:大于,相当于关系型数据库中的 >。
      • gte:大于等于,相当于关系型数据库中的 >=。
      • lt:小于,相当于关系型数据库中的 <。
      • lte:小于等于,相当于关系型数据库中的 <=。
/*** boolQuery 查询* 案例:查询从2018-2022年间标题含 三星 的商品信息*/
@Test
public void boolQuery() {try {// 创建 Bool 查询构建器BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 构建查询条件boolQueryBuilder.must(QueryBuilders.matchQuery("title", "三星")).filter().add(QueryBuilders.rangeQuery("createTime").format("yyyy").gte("2018").lte("2022"));// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(boolQueryBuilder);searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

7.10 queryString查询

会对查询条件进行分词, 然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR),可以指定单个字段也可多个查询字段

/*** queryStringQuery查询* 案例:查询出必须包含 华为手机 词语的商品信息*/
@Test
public void queryStringQuery() {try {// 创建 queryString 查询构建器QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery("华为手机").defaultOperator(Operator.AND);// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(queryStringQueryBuilder);searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status())&& searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

7.11 查询结果过滤

我们在查询数据的时候,返回的结果中,所有字段都给我们返回了,但是有时候我们并不需要那么多,所以可以对结果进行过滤处理。

/*** 过滤source获取部分字段内容* 案例:只获取 title、categoryName和price的数据*/
@Test
public void sourceFilter() {try {//查询条件(词条查询:对应ES query里的match)BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title", "金立")).must(QueryBuilders.matchQuery("categoryName", "手机")).filter(QueryBuilders.rangeQuery("price").gt(1000).lt(2000));// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(boolQueryBuilder);// 如果查询的属性很少,那就使用includes,而excludes设置为空数组// 如果排序的属性很少,那就使用excludes,而includes设置为空数组String[] includes = {"title", "categoryName", "price"};String[] excludes = {};searchSourceBuilder.fetchSource(includes, excludes);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

7.12 高亮查询

/*** 高亮查询* 案例:把标题中为 三星手机 的词语高亮显示*/
@Test
public void highlightBuilder() {try {//查询条件(词条查询:对应ES query里的match)MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "三星手机");//设置高亮三要素                                     // field: 你的高亮字段// preTags: 前缀// postTags: 后缀HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");// 构建查询源构建器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchQueryBuilder);searchSourceBuilder.highlighter(highlightBuilder);searchSourceBuilder.size(100);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);// 根据状态和数据条数验证是否返回了数据if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {// 将 JSON 转换成对象Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);// 获取高亮的数据HighlightField highlightField = hit.getHighlightFields().get("title");System.out.println("高亮名称:" + highlightField.getFragments()[0].string());// 替换掉原来的数据Text[] fragments = highlightField.getFragments();if (fragments != null && fragments.length > 0) {StringBuilder title = new StringBuilder();for (Text fragment : fragments) {//System.out.println(fragment);title.append(fragment);}goods.setTitle(title.toString());}// 输出查询信息log.info(goods.toString());}}} catch (IOException e) {log.error("", e);}
}

7.13 聚合查询

我们平时在使用Elasticsearch时,更多会用到聚合操作,它类似SQL中的group by操作。ES的聚合查询一定是先查出结果,然后对结果使用聚合函数做处理,常用的操作有:avg:求平均、max:最大值、min:最小值、sum:求和等。

在ES中聚合分为指标聚合和分桶聚合:

  • Metric 指标聚合:指标聚合对一个数据集求最大、最小、和、平均值
  • Bucket 分桶聚合:除了有上面的聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行游标聚合。

7.13.1 Metric指标聚合分析

/*** 聚合查询* Metric 指标聚合分析* 案例:分别获取最贵的商品和获取最便宜的商品*/
@Test
public void metricQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 获取最贵的商品AggregationBuilder maxPrice = AggregationBuilders.max("maxPrice").field("price");searchSourceBuilder.aggregation(maxPrice);// 获取最便宜的商品AggregationBuilder minPrice = AggregationBuilders.min("minPrice").field("price");searchSourceBuilder.aggregation(minPrice);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedMax max = aggregations.get("maxPrice");log.info("最贵的价格:" + max.getValue());ParsedMin min = aggregations.get("minPrice");log.info("最便宜的价格:" + min.getValue());} catch (IOException e) {log.error("", e);}
}/*** 聚合查询* Bucket 分桶聚合分析* 案例:根据品牌进行聚合查询*/
@Test
public void bucketQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 根据商品分类进行分组查询TermsAggregationBuilder aggBrandName = AggregationBuilders.terms("brandNameName").field("brandName");searchSourceBuilder.aggregation(aggBrandName);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {System.out.println(bucket.getKeyAsString() + "====" + bucket.getDocCount());}} catch (IOException e) {log.error("", e);}
}

7.13.2 Bucket分桶聚合分析

/*** 聚合查询* Bucket 分桶聚合分析* 案例:根据品牌进行聚合查询*/
@Test
public void bucketQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 根据商品分类进行分组查询TermsAggregationBuilder aggBrandName = AggregationBuilders.terms("brandNameName").field("brandName");searchSourceBuilder.aggregation(aggBrandName);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {System.out.println(bucket.getKeyAsString() + "====" + bucket.getDocCount());}} catch (IOException e) {log.error("", e);}
}/*** 子聚合聚合查询* Bucket 分桶聚合分析* 案例:根据商品分类进行分组查询,并且获取分类商品中的平均价格*/
@Test
public void subBucketQuery() {try {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 根据商品分类进行分组查询,并且获取分类商品中的平均价格TermsAggregationBuilder subAggregation = AggregationBuilders.terms("brandNameName").field("brandName").subAggregation(AggregationBuilders.avg("avgPrice").field("price"));searchSourceBuilder.aggregation(subAggregation);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms aggBrandName1 = aggregations.get("brandNameName");for (Terms.Bucket bucket : aggBrandName1.getBuckets()) {// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象ParsedAvg avgPrice = bucket.getAggregations().get("avgPrice");System.out.println(bucket.getKeyAsString() + "====" + avgPrice.getValueAsString());}} catch (IOException e) {log.error("", e);}
}

7.13.3 综合聚合查询

/*** 综合聚合查询* 根据商品分类聚合,获取每个商品类的平均价格,并且在商品分类聚合之上子聚合每个品牌的平均价格*/
@Test
public void subSubAgg() throws IOException {// 构建查询条件MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// 创建查询源构造器SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);// 注意这里聚合写的位置不要写错,很容易搞混,错一个括号就不对了TermsAggregationBuilder subAggregation = AggregationBuilders.terms("categoryNameAgg").field("categoryName").subAggregation(AggregationBuilders.avg("categoryNameAvgPrice").field("price")).subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName").subAggregation(AggregationBuilders.avg("brandNameAvgPrice").field("price")));searchSourceBuilder.aggregation(subAggregation);// 创建查询请求对象,将查询对象配置到其中SearchRequest searchRequest = new SearchRequest("goods");searchRequest.source(searchSourceBuilder);// 执行查询,然后处理响应结果SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//获取总记录数System.out.println("totalHits = " + searchResponse.getHits().getTotalHits().value);// 获取聚合信息Aggregations aggregations = searchResponse.getAggregations();ParsedStringTerms categoryNameAgg = aggregations.get("categoryNameAgg");//获取值返回for (Terms.Bucket bucket : categoryNameAgg.getBuckets()) {// 获取聚合后的分类名称String categoryName = bucket.getKeyAsString();// 获取聚合命中的文档数量long docCount = bucket.getDocCount();// 获取聚合后的分类的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象ParsedAvg avgPrice = bucket.getAggregations().get("categoryNameAvgPrice");System.out.println(categoryName + "======平均价:" + avgPrice.getValue()+ "======数量:" + docCount);ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");for (Terms.Bucket brandeNameAggBucket : brandNameAgg.getBuckets()) {// 获取聚合后的品牌名称String brandName = brandeNameAggBucket.getKeyAsString();// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象ParsedAvg brandNameAvgPrice = brandeNameAggBucket.getAggregations().get("brandNameAvgPrice");System.out.println("     " + brandName + "======" + brandNameAvgPrice.getValue());}}
}

JAVA 折叠 复制 全屏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/393376.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Io 35

FIleinputStream字节输入 package File.io;import java.io.*;public class io1 {public static void main(String[] args) throws IOException {// InputStream is new FileInputStream(new File("C:\\Users\\SUI\\Desktop\\Java1\\one\\src\\kaishi"));//简化Input…

C++ 几何算法 - 求两条直线交点

一&#xff1a;算法介绍 1. 首先定义两条直线方程&#xff1a; 2. 解方程&#xff0c;求出x, y坐标 3. 如果x分母的行列式等于0&#xff0c; 说明两条直线平行或方向相反 4. 如果x&#xff0c;y分母的行列式都等于0&#xff0c;说明两条线重叠 二&#xff1a;代码实现: #inclu…

求职Leetcode题目(5)

1.分割回文串 每一个结点表示剩余没有扫描到的字符串&#xff0c;产生分支是截取了剩余字符串的前缀&#xff1b;产生前缀字符串的时候&#xff0c;判断前缀字符串是否是回文。如果前缀字符串是回文&#xff0c;则可以产生分支和结点&#xff1b;如果前缀字符串不是回文&#…

Vue常见问题(一)组件的使用

Failed to resolve component. 报错原因&#xff1a; 组件注册错误&#xff1a;我们在组件中使用了未注册的组件。在Vue中&#xff0c;组件必须先注册才能使用。 解决方法&#xff1a; 引用组件 &#xff1a; import ItemPage from "/components/itemPage.vue";…

【踩坑】pytorch中的索引与copy_结合不会复制数据及其解决方案

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 背景知识 实验验证 结论分析 错误案例 处理方法 注意事项 附加说明 基本索引返回视图 高级索引返回副本 赋值操作都是原地操作 以下内容…

重生之我 学习【数据结构之顺序表(SeqList)】

⭐⭐⭐ 新老博友们&#xff0c;感谢各位的阅读观看 期末考试&假期调整暂时的停更了两个多月 没有写博客为大家分享优质内容 还容各位博友多多的理解 美丽的八月重生之我归来 继续为大家分享内容 你我共同加油 一起努力 ⭐⭐⭐ 数据结构将以顺序表、链表、栈区、队列、二叉树…

索尼相机SD卡找不到视频怎么办?提供全面解决方案

在使用索尼相机拍摄美好瞬间时&#xff0c;SD卡作为存储介质&#xff0c;承载着珍贵的视频和照片。然而&#xff0c;有时我们可能会遇到SD卡中视频文件无法找到的问题&#xff0c;这无疑让人倍感焦虑。本文旨在为大家提供一套全面的解决方案&#xff0c;希望帮助大家快速找回丢…

探索Linux世界之Linux环境开发工具的使用

一、yum -- Linux软件包管理器 1、什么是yum yum(Yellow dog Updater, Modified)&#xff1a; 是Linux下非常常用的一种包管理器. 主要应用在Fedora, RedHat, Centos等发行版上。 在Linux上安装软件的方式&#xff1a; 源代码直接安装&#xff1a;在Linux下安装软件, 一个通…

The Llama 3 Herd of Models 第8部分语音实验部分全文

第1,2,3部分,介绍、概览、预训练 第4部分,后训练 第5部分,结果 第6部分,推理 第7部分,视觉实验 8 Speech Experiments 我们进行了实验来研究将语音功能集成到Llama 3中的组合方法,类似于我们用于视觉识别的方法。在输入端,一个编码器,连同一个适配器,被并入处理语…

uniapp vue3 转换华为鸿蒙(以及问题一些解决方案)

主要是从 Windows系统配置 、配置离线SDK和DevEco-Studio、HBuilderX、三方面进行配置。 因为我也是之前写小程序的用uniapp vue3 写的看官网&#xff08;uni-app 开发鸿蒙应用 | uni-app官网&#xff09;的时候看到vue3 uniapp 写法可以转换华为鸿蒙开发&#xff0c;我就自己来…

为什么要用分布式锁

单应用中,如果要确保多线程修改同一个资源的安全性 加synchronized就可以了 但是性能不高 而mybatis-plus的乐观锁就可以很好的解决这类问题 但是这样的锁机制,只在单应用中有效 试想,在分布式下,有没有可能出现多个应用中的线程同时去修改同一个数据资源的并发问题 例如A …

Rstudio Server常见问题处理手册

一.开头 上面这个界面是不是非常熟悉&#xff1f;Rstudio 死亡圈圈一般发生在输入账号密码后进入Rstudio的时候&#xff0c;如果之前运行过大任务&#xff0c;有可能会出现这种情况。Rstudio常见问题我们如何排查和处理,本文章将给你一些思路和处理方式。 【ads】如果您不想被…

【开源】嵌入式Linux(IMX6U)应用层综合项目(4)--音乐播放器APP

1.简介 此文章并不是教程&#xff0c;只能当作笔者的学习分享&#xff0c;只会做一些简单的介绍&#xff0c;其他的各位结合着代码和运行现象自己分析吧&#xff0c;相信通过函数名和注释&#xff0c;基本上是不难看懂代码的&#xff0c;其中涉及到的一些技术栈&#xff0c;也…

图论(强联通分量)

在图论中&#xff0c;特别是在讨论有向图&#xff08;Directed Graph&#xff09;时&#xff0c;我们常常需要了解图的结构特性&#xff0c;比如强联通分量&#xff08;Strongly Connected Components, SCC&#xff09;。了解强联通分量中的各种边对于理解图的整体结构以及某些…

Redisson可重入锁原理(基于黑马视频总结,保姆级)

上一篇文章我们基于redis的set nx ex 命令以及Lua脚本实现了基本的分布式锁&#xff0c;但是还存在一下几点问题。于是又引出了redisson。 为什么基于SETNX的分布式锁无法实现可重入 先在method1中获取锁&#xff0c;获取成功后又调用method2&#xff0c;而method2内部也会获取…

spring+SSM+Mybatis面试题(上)(30道)

目录 1. 何为Spring Bean容器?Spring Bean容器与Spring IOC 容器有什么不同吗?2. Spring IOC 如何理解?3. Spring DI 如何理解?4. Spring 中基于注解如何配置对象作用域?以及如何配置延迟加载机制?1.配置作用域需要注解Scope(“Singleton”)2.开启延迟加载&#xff1a;La…

脚本:自动生成精准的Oracle AWR报告

很多朋友把AWR报告发过来让我帮忙分析Oracle数据库的性能&#xff0c;但很多报告都有一个共同的缺陷&#xff1a;就是这些报告覆盖的时间范围太广&#xff0c;导致性能问题的数据被严重稀释。 英文原文&#xff1a;Script: Generating Focused AWR Reports 为了解决这个问题&a…

完美解决pip命令版本冲突导致对应版本模块包无法安装的问题

解决步骤 使用pip更新/降低指定模块包命令格式降低pip自身至指定版本的命令再次换源安装指定模块包 在对 FasterNet 这篇论文源码复现过程中&#xff0c;我们首先需要安装相关依赖文件&#xff08; path/to/your/requirements.txt&#xff09; -extra-index-url https://down…

临床数据科学中如何用R来进行缺失值的处理(上)

在临床科研中&#xff0c;由于失访、无应答或记录不清等各种原因&#xff0c;经常会遇到数据缺失的问题。本文将深入探讨医学科研中数据缺失的成因、分类、影响以及应对方法&#xff0c;结合R语言的实际应用&#xff0c;为医学研究人员提供全面的解决方案。 一、认识缺失数据 …

【生成式人工智能-四-chatgpt的训练过程-pretrain预训练自督导式学习督导式学习】

大模型是怎么被训练出来的具有人类智慧的 阶段一训练-自我学习-具备知识训练资料self-supervised learning&#xff08;自督导式学习&#xff09; 阶段二-怎么让模型具备人的智慧supervised learning 督导式学习预训练pretrain为什么要用预训练的模型&#xff1f;Adapter逆向工…