Elasticsearch 学习(二).实战使用

参考:http://www.passjava.cn/#/01.PassJava/02.PassJava_Architecture/15.Elasticsearch%E5%AE%9E%E6%88%98

文章目录

  • Elasticsearch 学习(二).实战使用
    • 一、Elasticsearch 组件库介绍
    • 二、整合检索服务
      • 1.1 添加搜索服务模块
      • 1.2 配置 Maven 依赖
      • 1.3 注册搜索服务到注册中心
      • 1.4 添加 ES 配置类
      • 1.5 测试 ES Client 自动加载
      • 1.6 测试 ES 简单插入数据
      • 1.7 测试 ES 查询复杂语句
        • 1.7.1 构造检索条件
        • 17.2 获取命中记录的详情
        • 1.7.3 获取年龄分布聚合信息
        • 1.7.4 获取平均薪资聚合信息
    • 三、实战:同步 ES 数据
      • 3.1 定义检索模型
      • 3.2 在 ES 中创建索引
      • 3.3 定义 ES model
      • 3.4 触发保存的时机
      • 3.5 用 model 来组装数据
      • 3.6 保存数据到 ES
      • 3.7 检验 ES 中是否创建成功
        • 问题
    • 四、实战:查询 ES 数据
      • 4.1 定义请求参数
      • 4.2 定义返回参数
      • 4.3 组装 ES 查询参数
      • 4.4 返回结果封装
      • 4.5 测试 ES 查询
        • 4.5.1 实验一:测试 title 匹配
        • 4.5.2 实验二:测试 answer 匹配
        • 4.5.2 实验三:测试 id 匹配

一、Elasticsearch 组件库介绍

全文检索是什么:

全文检索: 指以全部文本信息作为检索对象的一种信息检索技术。而我们使用的数据库,如 Mysql,MongoDB 对文本信息检索能力特别是中文检索并没有 ES 强大。所以我们来看下 ES 在项目中是如何来代替 SQL 来工作的。

使用的 Elasticsearch 服务是 7.4.2 的版本,然后采用官方提供的 Elastiscsearch-Rest-Client 库来操作 ES,而且官方库的 API 上手简单。

该组件库的官方文档地址:

https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html

另外这个组件库是支持多种语言的:

注意:Elasticsearch Clients 就是指如何用 API 操作 ES 服务的组件库。

可能有同学会提问,Elasticsearch 的组件库中写着 JavaScript API,是不是可以直接在前端访问 ES 服务?可以是可以,但是会暴露 ES 服务的端口和 IP 地址,会非常不安全。所以我们还是用后端服务来访问 ES 服务。

我们这个项目是 Java 项目,自然就是用上面的两种:Java Rest Client 或者 Java API。我们先看下 Java API,但是会发现已经废弃了。如下图所示:

只能用 Java REST Client 了。而它又分成两种:高级和低级的。

高级包含更多的功能,如果把高级比作MyBatis的话,那么低级就相当于JDBC。所以我们用高级的 Client。

二、整合检索服务

把检索服务单独作为一个服务。就称作 passjava-search 模块吧。

1.1 添加搜索服务模块

  • 创建 passjava-search 模块。

首先我们在 PassJava-Platform 模块创建一个 搜索服务模块 passjava-search。然后勾选 spring web 服务。如下图所示。

第一步:新建一个模块 搜索服务模块

group: com.ung.passjava
artifact:passjava-search
name:passjava-search
description:搜索服务
package:com.ung.passjava.search

1.2 配置 Maven 依赖

选择 Web->Spring Web 依赖,然后点击 Next

进入到 ES 官方网站,可以看到有低级和高级的 Rest Client,我们选择高阶的(High Level Rest Client)。然后进入到高阶 Rest Client 的 Maven 仓库。官网地址如下所示:

https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.9/index.html

<?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.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.ung.passjava</groupId><artifactId>passjava-search</artifactId><version>0.0.1-SNAPSHOT</version><name>passjava-search</name><description>搜索服务</description><properties><java.version>1.8</java.version><elasticsearch.version>7.4.2</elasticsearch.version></properties><dependencies><dependency><groupId>com.ung.passjava</groupId><artifactId>passjava-common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!--入 ES 的高阶客户端--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.4.2</version></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></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

最后刷新maven依赖可以看到

1.3 注册搜索服务到注册中心

修改配置文件:src/main/resources/application.properties。配置应用程序名、注册中心地址、注册中心的命名中间。

#服务名
spring.application.name=passjava-search
#注册中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
#配置中心的命名空间
spring.cloud.nacos.config.namespace=passjava-search
#服务端口号
server.port=17000

启动类添加服务发现注解:@EnableDiscoveryClient。这样 passjava-search 服务就可以被注册中心发现了。

因 Common 模块依赖数据源,但 search 模块不依赖数据源,所以 search 模块需要移除数据源依赖:

exclude = DataSourceAutoConfiguration.class

以上的两个注解如下所示:

@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class PassjavaSearchApplication {public static void main(String[] args) {SpringApplication.run(PassjavaSearchApplication.class, args);}
}

接下来我们添加一个 ES 服务的专属配置类,主要目的是自动加载一个 ES Client 来供后续 ES API 使用,不用每次都 new 一个 ES Client。

1.4 添加 ES 配置类

配置类:PassJavaElasticsearchConfig.java

核心方法就是 RestClient.builder 方法,设置好 ES 服务的 IP 地址、端口号、传输协议就可以了。最后自动加载了 RestHighLevelClient。

package com.ung.passjava.search.config;import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class PassJavaElasticsearchConfig {@Bean// 给容器注册一个 RestHighLevelClient,用来操作 ES// 参考官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.9/java-rest-high-getting-started-initialization.htmlpublic RestHighLevelClient restHighLevelClient() {return new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.2.135", 9200, "http")));}
}

接下来我们测试下 ES Client 是否自动加载成功

1.5 测试 ES Client 自动加载

在测试类 PassjavaSearchApplicationTests 中编写测试方法,打印出自动加载的 ES Client。期望结果是一个 RestHighLevelClient 对象。

@SpringBootTest
class PassjavaSearchApplicationTests {@Qualifier("restHighLevelClient")@Autowiredprivate RestHighLevelClient client;@Testvoid contextLoads() {System.out.println(client);}}

结果打印成功

1.6 测试 ES 简单插入数据

测试方法 testIndexData, users 索引在我的 ES 中是没有记录的,所以期望结果是 ES 中新增了一条 users 数据。

   @Dataclass User {private String userName;private Integer age;private String gender;}/*** 测试存储数据到 ES。*/@Testpublic void testIndexData() throws IOException {IndexRequest request = new IndexRequest("users");request.id("1"); // 文档的 id//构造 User 对象User user = new User();user.setUserName("PassJava");user.setAge(18);user.setGender("Man");//User 对象转为 JSON 数据String jsonString = JSON.toJSONString(user);// JSON 数据放入 request 中request.source(jsonString, XContentType.JSON);// 执行插入操作IndexResponse response = client.index(request, RequestOptions.DEFAULT);System.out.println(response);}

执行 test 方法,我们可以看到控制台输出以下结果,说明数据插入到 ES 成功。结果中的 result 字段为 created

我们再来到 ES 中看下 users 索引中数据。查询 users 索引:

GET users/_search

可以看到结果成功查出

1.7 测试 ES 查询复杂语句

示例:搜索 bank 索引,address 字段中包含 road 的所有人的年龄分布 ( 前 10 条 ) 以及平均年龄,以及平均薪资。

1.7.1 构造检索条件

我们可以参照官方文档给出的示例来创建一个 SearchRequest 对象,指定要查询的索引为 bank,然后创建一个 SearchSourceBuilder 来组装查询条件。总共有三种条件需要组装:

  • address 中包含 road 的所有人。
  • 按照年龄分布进行聚合。
  • 计算平均薪资。

代码如下所示

@ToString
@Data
static class BankMember {private int account_number;private int balance;private String firstname;private String lastname;private int age;private String gender;private String address;private String employer;private String email;private String city;private String state;
}/*** 搜索 address 中包含 road 的所有人的年龄分布 ( 前 10 条 ) 以及平均年龄,平均薪资** @throws IOException*/
@Test
public void testSearchData() throws IOException {//新建一个查询请求对象SearchRequest request = new SearchRequest();//设置查询索引是bankrequest.indices("bank");SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 1.1)address 中包含 road 的所有人sourceBuilder.query(QueryBuilders.matchQuery("address", "road"));// 1.2)按照年龄分布进行聚合TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);sourceBuilder.aggregation(ageAgg);// 1.3)计算平均薪资AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");sourceBuilder.aggregation((balanceAvg));System.out.println("检索参数:" + sourceBuilder.toString());request.source(sourceBuilder);// 2、执行检索SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 3、分析结果System.out.println(response.toString());// 3.1)获取查到的数据。SearchHits hits = response.getHits();// 3.2)获取真正命中的结果SearchHit[] searchHits = hits.getHits();// 3.3)遍历命中结果for (SearchHit hit : searchHits) {String hitStr = hit.getSourceAsString();BankMember bankMember = JSON.parseObject(hitStr, BankMember.class);System.out.println(bankMember);}// 3.4)获取聚合信息// 参考文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-search.htmlAggregations aggregations = response.getAggregations();Terms ageAgg1 = aggregations.get("ageAgg");for (Terms.Bucket bucket : ageAgg1.getBuckets()) {String keyAsString = bucket.getKeyAsString();System.out.println("用户年龄: " + keyAsString + " 人数:" + bucket.getDocCount());}Avg balanceAvg1 = aggregations.get("balanceAvg");System.out.println("平均薪资:" + balanceAvg1.getValue());
}

打印结果:

检索简化参数:
GET bank/_search
{"query": {"match": {"address": "road"}},"aggs": {"ageAggr": {"terms": {"field": "age","size": 10}},"ageAvg": {"avg": {"field": "age"}},"balanceAvg": {"avg": {"field": "balance"}}}
}

17.2 获取命中记录的详情
// 3.1)获取查到的数据。
SearchHits hits = response.getHits();
// 3.2)获取真正命中的结果
SearchHit[] searchHits = hits.getHits();

我们可以通过遍历 searchHits 的方式打印出所有命中结果的详情。

// 3.3)、遍历命中结果
for (SearchHit hit: searchHits) {String hitStr = hit.getSourceAsString();BankMember bankMember = JSON.parseObject(hitStr, BankMember.class);
}

拿到每条记录的 hitStr 是个 JSON 数据,如下所示:

{"account_number": 431,"balance": 13136,"firstname": "Laurie","lastname": "Shaw","age": 26,"gender": "F","address": "263 Aviation Road","employer": "Zillanet","email": "laurieshaw@zillanet.com","city": "Harmon","state": "WV"
}

而 BankMember 是根据返回的结果详情定义的的 JavaBean。可以通过工具自动生成。在线生成 JavaBean 的网站如下:

https://www.bejson.com/json2javapojo/new/

把这个 JavaBean 加到 PassjavaSearchApplicationTests 类中:

@ToString
@Data
static class BankMember {private int account_number;private int balance;private String firstname;private String lastname;private int age;private String gender;private String address;private String employer;private String email;private String city;private String state;
}

然后将 bankMember 打印出来:

System.out.println(bankMember);

得到的结果确实是我们封装的 BankMember 对象,而且里面的属性值也都拿到了。

1.7.3 获取年龄分布聚合信息

ES 返回的 response 中,年龄分布的数据是按照 ES 的格式返回的,如果想按照我们自己的格式来返回,就需要将 response 进行处理。

如下图所示,这个是查询到的年龄分布结果,我们需要将其中某些字段取出来,比如 buckets,它代表了分布在 21 岁的有 4 个。

实现:

Aggregations aggregations = response.getAggregations();
Terms ageAgg1 = aggregations.get("ageAgg");
for (Terms.Bucket bucket : ageAgg1.getBuckets()) {String keyAsString = bucket.getKeyAsString();System.out.println("用户年龄: " + keyAsString + " 人数:" + bucket.getDocCount());
}

最后结果打印输出:

1.7.4 获取平均薪资聚合信息

现在来看看平均薪资如何按照所需的格式返回,ES 返回的结果如下图所示,我们需要获取 balanceAvg 字段的 value 值

代码实现:

Avg balanceAvg1 = aggregations.get("balanceAvg");
System.out.println("平均薪资:" + balanceAvg1.getValue());

打印结果如下,平均薪资 28578 元。

三、实战:同步 ES 数据

3.1 定义检索模型

PassJava 这个项目可以用来配置题库,如果我们想通过关键字来搜索题库,该怎么做呢?

类似于百度搜索,输入几个关键字就可以搜到关联的结果,我们这个功能也是类似,通过 Elasticsearch 做检索引擎,后台管理界面和小程序作为搜索入口,只需要在小程序上输入关键字,就可以检索相关的题目和答案。

首先我们需要把题目和答案保存到 ES 中,在存之前,第一步是定义索引的模型,如下所示,模型中有 titleanswer 字段,表示题目和答案。

"id": {"type": "long"
},
"title": {"type": "text","analyzer": "ik_smart"
},
"answer": {"type": "text","analyzer": "ik_smart"
},
"typeName": {"type": "keyword"
}

3.2 在 ES 中创建索引

上面我们已经定义了索引结构,接着就是在 ES 中创建索引。

在 Kibana 控制台中执行以下语句:

PUT question
{"mappings" : {"properties": {"id": {"type": "long"},"title": {"type": "text","analyzer": "ik_smart"},"answer": {"type": "text","analyzer": "ik_smart"},"typeName": {"type": "keyword"}}}
}

通过以下命令来查看 question 索引是否在 ES 中:

GET _cat/indices

结果:

3.3 定义 ES model

上面我们定义 ES 的索引,接着就是定义索引对应的模型,将数据存到这个模型中,然后再存到 ES 中。

ES 模型如下,共四个字段:id、title、answer、typeName。和 ES 索引是相互对应的。

在common模块创建 QuestionEsModel类

@Data
public class QuestionEsModel {private Long id;private String title;private String answer;private String typeName;
}

3.4 触发保存的时机

当我们在后台创建题目或保存题目时,先将数据保存到 mysql 数据库,然后再保存到 ES 中。

如下图所示,在管理后台创建题目时,触发保存数据到 ES 。

第一步,保存数据到 mysql 中,项目中已经包含此功能,就不再讲解了

直接进入第二步:保存数据到 ES 中。而保存数据到 ES 中,需要将数据组装成 ES 索引对应的数据,所以我用了一个 ES model,先将数据保存到 ES model 中。

3.5 用 model 来组装数据

这里的关键代码时 copyProperties,可以将 question 对象的数据取出,然后赋值到 ES model 中。不过 ES model 中还有些字段是 question 中没有的,所以需要单独拎出来赋值,比如 typeName 字段,question 对象中没有这个字段,它对应的字段是 question.type,所以我们把 type 取出来赋值到 ES model 的 typeName 字段上。

//创建ES model
QuestionEsModel esModel = new QuestionEsModel();//复制属性
BeanUtils.copyProperties(question, esModel);//获取题目类型的名称
TypeEntity typeEntity = typeService.getById(question.getType());
String typeName = typeEntity.getType();
//给model 的 typeName 赋值
esModel.setTypeName(typeName);
System.out.println("-----------------esModel:" + esModel);

3.6 保存数据到 ES

我在 passjava-search 微服务中写了一个保存题目的 api 用来保存数据到 ES 中。

使用fegin调用

创建search用于保存Question的service和实现类

public interface IQuestionService {boolean save(QuestionEsModel questionEsModel) throws IOException;
}

实现类

@Service("questionService")
public class QuestionServiceImpl implements IQuestionService {@AutowiredRestHighLevelClient restHighLevelClient;@Overridepublic boolean save(QuestionEsModel questionEsModel) throws IOException {// 创建数据到 ES 中IndexRequest indexRequest = new IndexRequest(EsConstant.QUESTION_INDEX);//设置idindexRequest.id(questionEsModel.getId().toString());String data = JSON.toJSONString(questionEsModel);indexRequest.source(data, XContentType.JSON);IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);System.out.println("save*****************questionEsModel:" + response);return true;}
}

EsConstant

public class EsConstant {public static final String QUESTION_INDEX = "question"; // 题目数据在 ES 中的索引public static final Integer PAGE_SIZE = 5; // 分页大小
}

passjava-question 使用feign调用

在 QuestionController中修改save方法

    /*** 保存*/@RequestMapping("/save")//@RequiresPermissions("question:question:save")public R save(@Valid @RequestBody QuestionEntity question) {//        questionService.save(question);questionService.saveQuestion(question);return R.ok();}

QuestionService

public interface QuestionService extends IService<QuestionEntity> {boolean saveQuestion(QuestionEntity question);
}

QuestionServiceImpl

@Service("questionService")
public class QuestionServiceImpl extends ServiceImpl<QuestionDao, QuestionEntity> implements QuestionService {@AutowiredTypeService typeService;@AutowiredSearchFeignService searchFeignService;@Overridepublic boolean saveQuestion(QuestionEntity question) {save(question);saveEs(question);return true;}/*** 同步到es** @param question* @return*/private boolean saveEs(QuestionEntity question) {//创建ES modelQuestionEsModel esModel = new QuestionEsModel();//复制属性BeanUtils.copyProperties(question, esModel);//获取题目类型的名称TypeEntity typeEntity = typeService.getById(question.getType());String typeName = typeEntity.getType();//给model 的 typeName 赋值esModel.setTypeName(typeName);System.out.println("-----------------esModel:" + esModel);// 调用 passjava-search 服务,将数据发送到 ES 中保存。R r = searchFeignService.saveQuestion(esModel);System.out.println("r:" + r);return true;}}

创建fegin的es的service

@FeignClient("passjava-search")
public interface SearchFeignService {@PostMapping("search/question/save")R saveQuestion(@RequestBody QuestionEsModel questionEsModel);
}

启动类开启feign

@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.ung.passjava.question.feign")
@MapperScan("com.ung.passjava.question.dao")
@SpringBootApplication
public class PassjavaQuestionApplication {public static void main(String[] args) {SpringApplication.run(PassjavaQuestionApplication.class, args);}
}

最后启动服务

发起请求

post  http://127.0.0.1:11000/question/v1/admin/question/save{"title": "ccc","answer": "aaa","level": 2,"displayOrder": 1,"subTitle": "ccc2","type": 1,"enable": 1}

3.7 检验 ES 中是否创建成功

通过 kibana 的控制台来查看 question 索引中的文档。通过以下命令来查看:

GET question/_search

结果:

{"took" : 1030,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "question","_type" : "_doc","_id" : "5","_score" : 1.0,"_source" : {"answer" : "aaa","id" : 5,"title" : "ccc","typeName" : "type名字"}}]}
}
问题

可以重复更新题目吗?

答案是可以的,保存到 ES 的数据是幂等的,因为保存的时候带了一个类似数据库主键的 id。

四、实战:查询 ES 数据

我们已经将数据同步到了 ES 中,现在就是前端怎么去查询 ES 数据中,这里我们还是使用 Postman 来模拟前端查询请求。

4.1 定义请求参数

请求参数我定义了三个:

  • keyword:用来匹配问题或者答案。
  • id:用来匹配题目 id。
  • pageNum:用来分页查询数据。

这里我将这三个参数定义为一个类:

@Data
public class SearchParam {private String keyword; // 全文匹配的关键字private String id; // 题目 idprivate Integer pageNum; // 查询第几页数据
}

4.2 定义返回参数

返回的 response 我也定义了四个字段:

  • questionList:查询到的题目列表。
  • pageNum:第几页数据。
  • total:查询到的总条数。
  • totalPages:总页数。

定义的类如下所示:

@Data
public class SearchQuestionResponse {private List<QuestionEsModel> questionList; // 题目列表private Integer pageNum; // 查询第几页数据private Long total; // 总条数private Integer totalPages; // 总页数
}

4.3 组装 ES 查询参数

调用 ES 的查询 API 时,需要构建查询参数。

组装查询参数的核心代码:

接口 IQuestionSearchService

public interface IQuestionSearchService {SearchQuestionResponse search(SearchParam param);
}

QuestionSearchServiceImpl

@Service
public class QuestionSearchServiceImpl implements IQuestionSearchService {@Qualifier("restHighLevelClient")@Autowiredprivate RestHighLevelClient client;@Overridepublic SearchQuestionResponse search(SearchParam param) {SearchQuestionResponse questionResponse = new SearchQuestionResponse();SearchResponse searchResponse = null;/** 1.动态构建出查询需要的 DSL 语句*/// 1.1) 创建检索请求SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); // 构建 DSL 语句BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();if (!StringUtils.isEmpty(param.getKeyword())) {// 1.2) title,answer,typeName 字段用来关键词检索boolQuery.must(QueryBuilders.multiMatchQuery(param.getKeyword(),"title", "answer", "typeName"));}if (param.getId() != null) {// 1.3)题目 id 用来精确匹配boolQuery.filter(QueryBuilders.termQuery("id", param.getId()));}sourceBuilder.query(boolQuery);// 1.4)分页sourceBuilder.from((param.getPageNum() - 1) * EsConstant.PAGE_SIZE);sourceBuilder.size(EsConstant.PAGE_SIZE);SearchRequest request = new SearchRequest(new String[] {EsConstant.QUESTION_INDEX}, sourceBuilder);try {// 2、执行检索searchResponse = client.search(request, RequestOptions.DEFAULT);// 3、分析结果System.out.println(searchResponse.toString());// 3.1)获取查到的数据。SearchHits hits = searchResponse.getHits();// 3.2)获取真正命中的结果SearchHit[] searchHits = hits.getHits();// 3.3)遍历命中结果List<QuestionEsModel> questionEsModelList = new ArrayList<>();if (hits.getHits() != null && hits.getHits().length > 0) {for (SearchHit hit : searchHits) {String hitStr = hit.getSourceAsString();QuestionEsModel questionEsModel = JSON.parseObject(hitStr, QuestionEsModel.class);System.out.println(questionEsModel);questionEsModelList.add(questionEsModel);}questionResponse.setQuestionList(questionEsModelList);// 分页long total = hits.getTotalHits().value;questionResponse.setTotal(total);questionResponse.setPageNum(param.getPageNum());int totalPages = (int) total % EsConstant.PAGE_SIZE == 0 ? (int) total / EsConstant.PAGE_SIZE : (int) (total / EsConstant.PAGE_SIZE + 1);questionResponse.setTotalPages(totalPages);}} catch (IOException e) {e.printStackTrace();}return questionResponse;}
}

请求参数封装:

  • 第一步:创建检索请求。
  • 第二步:设置哪些字段需要模糊匹配。这里有三个字段:title,answer,typeName。
  • 第三步:设置如何分页。这里分页大小是 5 个。
  • 第四步:调用查询 api。

4.4 返回结果封装

  • 第一步:获取查到的数据。
  • 第二步:获取真正命中的结果。
  • 第三步:格式化返回的数据。
  • 第四步:组装分页参数。

4.5 测试 ES 查询

4.5.1 实验一:测试 title 匹配

验证 title 字段是否能匹配到,传的请求参数 keyword = 111,匹配到了 title = 111 的数据,且只有一条。页码 pageNum 我传的 1,表示返回第一页数据。如下图所示:

4.5.2 实验二:测试 answer 匹配

验证 answer 字段是否能匹配到,传的请求参数 keyword = 测试答案,匹配到了 title = 测试答案的数据,且只有一条,说明查询成功。如下图所示:

4.5.2 实验三:测试 id 匹配

我们现在想要匹配题目 id 的话,需要传请求参数 id,而且 id 是精确匹配。另外 id 和 keyword 是取并集,所以不能传 keyword 字段。

请求参数 id = 5,返回结果也是 id =5 的数据,说明查询成功。如下图所示:

Elasticsearch 学习(二).实战使用相关推荐

  1. ElasticSearch学习(三)——Windows集群部署

    文章名称 地址 ElasticSearch学习(一)--概述 前往 ElasticSearch学习(二)--索引.文档简单操作 前往 ElasticSearch学习(三)--Windows 集群部署 ...

  2. ElasticSearch学习(四)——Linux 单节点部署

    文章名称 地址 ElasticSearch学习(一)--概述 前往 ElasticSearch学习(二)--索引.文档简单操作 前往 ElasticSearch学习(三)--Windows 集群部署 ...

  3. Elasticsearch核心技术与实战学习笔记 43 | 分页与遍历:From, Size, Search After Scroll API

    一 序 本文属于极客时间Elasticsearch核心技术与实战学习笔记系列. 二 分页 2.1 From / Size 默认情况下,查询按照相关度算分排序,返回前 10 条记录 容易理解的分页方案 ...

  4. 深度学习二(Pytorch物体检测实战)

    深度学习二(Pytorch物体检测实战) 文章目录 深度学习二(Pytorch物体检测实战) 1.PyTorch基础 1.1.基本数据结构:Tensor 1.1.1.Tensor数据类型 1.1.2. ...

  5. WiFi基础学习到实战(二:WiFi网络“数据链路层”)

    欢迎大家一起学习探讨通信之WLAN.上节我们对802.11标准基于OSI模型进行了分析,主要将OSI模型的"数据链路层"定义分为"LLC层"和"MAC ...

  6. ElasticSearch学习笔记之二十一 指标聚合

    ElasticSearch学习笔记之二十一 指标聚合 指标聚合 Avg Aggregation Script Value Script Missing value Weighted Avg Aggre ...

  7. pytorch卷积神经网络_资源|卷积神经网络迁移学习pytorch实战推荐

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 一.资源简介 这次给大家推荐一篇关于卷积神经网络迁移学习的实战资料,卷积神经网络迁移学 ...

  8. python3人工智能网盘_《Python3入门人工智能掌握机器学习+深度学习提升实战能力》百度云网盘资源分享下载[MP4/5.77GB]...

    内容简介 本资源为<Python3入门人工智能掌握机器学习+深度学习提升实战能力>百度云网盘资源分享下载,具体看下文目录,格式为MP4/5.77GB.本资源已做压缩包处理,请勿直接在百度网 ...

  9. TensorFlow 2.0深度学习案例实战

    向AI转型的程序员都关注了这个号???????????? 机器学习AI算法工程   公众号:datayx 基于TensorFlow 2.0正式版, 理论与实战结合,非常适合入门学习! 这是一本面向人工 ...

最新文章

  1. Active Directory还原工具之三Software Active Directory Recycle Bin PowerPack
  2. 李志飞:想在中国复制 Echo 的成功,肯定都不靠谱
  3. ActivityGroup是如何对嵌入的Activitys进行管理的
  4. oi程序提交注意:bool
  5. python中的zip()函数和map()函数
  6. Windows Server 2003 单网卡启用×××远程外网访问功能
  7. node --- 模块化连接MongoDB数据库的参数设置方案之一
  8. macOS卸载Java9及利用Homebrew搭建并配置Java开发环境
  9. Sentinel实现限流熔断及与Spring Cloud整合
  10. 17. 装箱、拆箱的最小化
  11. 英读廊——随时来串门吧!是真心的邀请,还是塑料的友情?
  12. 10麦客和300挖藕人
  13. Android直播软件搭建中实用的录制编辑方案有哪些
  14. 正则表达式匹配整行和注释
  15. 南京沁恒推出的国产 M3 架构芯片与意法半导体 M3 芯片对比
  16. windows防火墙配置(以windows server2008为例)
  17. java图片加气泡文字_图片加气泡文字
  18. 迁移学习篇之如何迁移经典CNN网络-附迁移学习Alexnet,VGG,Googlenet,Resnet详细代码注释和方法-pytorch
  19. sweet home3d_Sweet Home 3D的开源室内设计
  20. 视频教程-华为HCNP网络工程师【从入门到精通】自学视频[肖哥] ¥499-华为认证

热门文章

  1. 火狐linux 32位,火狐浏览器下载电脑版32位
  2. 简述相关与回归分析的关系_相关分析与回归分析的联系与区别
  3. Flir Blackfly S 工业相机:自动曝光配置及代码
  4. 小程序 _ 学习笔记
  5. Day3-T31项目 异常处理与日志——2021-11-02
  6. 一文理解 JWT、JWS、JWE、JWA、JWK、JOSE
  7. .Net 优秀的开源框架整理
  8. 前端小练习:纯css菜单栏
  9. [RK3288][Android6.0] 调试笔记 --- apk安装添加黑名单
  10. cmakeLists.txt中增加-g选项不生效/gcc -s参数