ElasticSearch实战篇 - Spring Boot 整合 ElasticSearch 旧城等待, 2021-08-30 01:09 349阅读 0赞 点击上方 [Java后端][Java],选择 设为星标 优质文章,及时送达 -------------------- 作者:冯文议 链接:segmentfault.com/a/1190000018625101 当前Spring Boot很是流行,包括我自己,也是在用Spring Boot集成其他框架进行项目开发,所以这一节,我们一起来探讨Spring Boot整合ElasticSearch的问题。 本文主要讲以下内容: 第一部分,通读文档 第二部分,Spring Boot整合ElasticSearch 第三部分,基本的CRUD操作 第四部分,搜索 第五部分,例子 还没有学过Elasticsearch的朋友,可以先学这个系列的第一节(这个系列共三节),如果你有不明白或者不正确的地方,可以给我评论、留言或者私信。 ## **第一步,通读文档** ## Spring Data Elasticsearch 官方文档,这是当前最新的文档。 #### **关于repository** #### 文档一开始就介绍 `CrudRepository` ,比如,继承 `Repository`,其他比如 `JpaRepository`、`MongoRepository`是继承`CrudRepository`。也对其中的方法做了简单说明,我们一起来看一下: public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { // Saves the given entity. <S extends T> S save(S entity); // Returns the entity identified by the given ID. Optional<T> findById(ID primaryKey); // Returns all entities. Iterable<T> findAll(); // Returns the number of entities. long count(); // Deletes the given entity. void delete(T entity); // Indicates whether an entity with the given ID exists. boolean existsById(ID primaryKey); // … more functionality omitted. } 好了,下面我们看一下今天的主角 `ElasticsearchRepository` 他是怎样的吧。 ![ElasticsearchRepository继承图][ElasticsearchRepository] 这说明什么? * 用法和JPA一样; * 再这他除了有CRUD的基本功能之外,还有分页和排序。 清楚了这之后,是不是应该考虑该如何使用了呢? #### **如何****用?** #### 没错,接下来,开始说如何用,也写了很多示例代码。相对来说,还是比较简单,这里就贴一下代码就行了吧。 interface PersonRepository extends Repository<User, Long> { List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname); // Enables the distinct flag for the query List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname); List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname); // Enabling ignoring case for an individual property List<Person> findByLastnameIgnoreCase(String lastname); // Enabling ignoring case for all suitable properties List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname); // Enabling static ORDER BY for a query List<Person> findByLastnameOrderByFirstnameAsc(String lastname); List<Person> findByLastnameOrderByFirstnameDesc(String lastname); } 是不是这样,就可以正常使用了呢? #### **问题** #### 当然可以,但是如果错了问题怎么办呢,官网写了一个常见的问题,比如包扫描问题,没有你要的方法。 interface HumanRepository { void someHumanMethod(User user); } class HumanRepositoryImpl implements HumanRepository { public void someHumanMethod(User user) { // Your custom implementation } } interface ContactRepository { void someContactMethod(User user); User anotherContactMethod(User user); } class ContactRepositoryImpl implements ContactRepository { public void someContactMethod(User user) { // Your custom implementation } public User anotherContactMethod(User user) { // Your custom implementation } } 你也可以自己写接口,并且去实现它。 说完理论,作为我,应该在实际的代码中如何运用呢? #### **示例** #### 官方也提供了很多示例代码,我们一起来看看。 @Controller class PersonController { @Autowired PersonRepository repository; @RequestMapping(value = "/persons", method = RequestMethod.GET) HttpEntity<PagedResources<Person>> persons(Pageable pageable, PagedResourcesAssembler assembler) { Page<Person> persons = repository.findAll(pageable); return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK); } } 这段代码相对来说还是十分经典的,我相信很多人都看到别人的代码,可能都会问,它为什么会这么用呢,答案或许就在这里吧。 当然,这是以前的代码,或许现在用不一定合适。 #### **高级搜索** #### 终于到高潮了! 学完我的第一节,你应该已经发现了,Elasticsearch搜索是一件十分复杂的事,为了用好它,我们不得不学好它。一起加油。 到这里,官方文档我们算是过了一遍了,大致明白了,他要告诉我们什么。其实,文档还有很多内容,可能你遇到的问题都能在里面找到答案。 最后,我们继续看一下官网写的一段处理得十分优秀的一段代码吧: SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(matchAllQuery()) .withIndices(INDEX_NAME) .withTypes(TYPE_NAME) .withFields("message") .withPageable(PageRequest.of(0, 10)) .build(); CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class); List<SampleEntity> sampleEntities = new ArrayList<>(); while (stream.hasNext()) { sampleEntities.add(stream.next()); } ## **第二部分,Spring Boot整合ElasticSearch** ## #### **添加依赖** #### implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch' #### **添加配置** #### spring: data: elasticsearch: cluster-nodes: localhost:9300 cluster-name: es-wyf 这样就完成了整合,接下来我们用两种方式操作。 #### **Model** #### 我们先写一个的实体类,借助这个实体类呢来完成基础的CRUD功能。 @Data @Accessors(chain = true) @Document(indexName = "blog", type = "java") public class BlogModel implements Serializable { private static final long serialVersionUID = 6320548148250372657L; @Id private String id; private String title; //@Field(type = FieldType.Date, format = DateFormat.basic_date) @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private Date time; } 注意id字段是必须的,可以不写注解@Id。 public interface BlogRepository extends ElasticsearchRepository<BlogModel, String> { } ## **第三部分,CRUD** ## 基础操作的代码,都是在 `BlogController` 里面写。 @RestController @RequestMapping("/blog") public class BlogController { @Autowired private BlogRepository blogRepository; } #### **添加** #### @PostMapping("/add") public Result add(@RequestBody BlogModel blogModel) { blogRepository.save(blogModel); return Result.success(); } #### 我们添加一条数据,标题是:Elasticsearch实战篇:Spring Boot整合ElasticSearch,时间是:2019-03-06。我们来测试,看一下成不成功。 #### POST http://localhost:8080/blog/add { "title":"Elasticsearch实战篇:Spring Boot整合ElasticSearch", "time":"2019-05-06" } 得到响应: { "code": 0, "msg": "Success" } 嘿,成功了。那接下来,我们一下查询方法测试一下。 #### **查询** #### * 根据ID查询 @GetMapping("/get/{id}") public Result getById(@PathVariable String id) { if (StringUtils.isEmpty(id)) return Result.error(); Optional<BlogModel> blogModelOptional = blogRepository.findById(id); if (blogModelOptional.isPresent()) { BlogModel blogModel = blogModelOptional.get(); return Result.success(blogModel); } return Result.error(); } 测试一下: ![测试根据ID查询][ID] ok,没问题。 * 查询所有 @GetMapping("/get") public Result getAll() { Iterable<BlogModel> iterable = blogRepository.findAll(); List<BlogModel> list = new ArrayList<>(); iterable.forEach(list::add); return Result.success(list); } 测试一下: GET http://localhost:8080/blog/get 结果: { "code": 0, "msg": "Success", "data": [ { "id": "fFXTTmkBTzBv3AXCweFS", "title": "Elasticsearch实战篇:Spring Boot整合ElasticSearch", "time": "2019-05-06" } ] } #### 根据ID修改 #### @PostMapping("/update") public Result updateById(@RequestBody BlogModel blogModel) { String id = blogModel.getId(); if (StringUtils.isEmpty(id)) return Result.error(); blogRepository.save(blogModel); return Result.success(); } 测试: POST http://localhost:8080/blog/update { "id":"fFXTTmkBTzBv3AXCweFS", "title":"Elasticsearch入门篇", "time":"2019-05-01" } 响应: { "code": 0, "msg": "Success" } ``查询一下: ![修改数据成功][format_png] ok,成功! #### 删除 #### * 根据ID删除 @DeleteMapping("/delete/{id}") public Result deleteById(@PathVariable String id) { if (StringUtils.isEmpty(id)) return Result.error(); blogRepository.deleteById(id); return Result.success(); } 测试: DELETE http://localhost:8080/blog/delete/fFXTTmkBTzBv3AXCweFS 响应: { "code": 0, "msg": "Success" } 我们再查一下: ![删除数据成功][format_png 1] * 删除所有数据 @DeleteMapping("/delete") public Result deleteById() { blogRepository.deleteAll(); return Result.success(); } ## **第四部分,搜索** ## #### **构造数据** #### 为了方便测试,我们先构造数据 ![构造查询数据][format_png 2] #### Repository查询操作 #### **搜索标题中的关键字** `BlogRepositor` List<BlogModel> findByTitleLike(String keyword); `BlogController` @GetMapping("/rep/search/title") public Result repSearchTitle(String keyword) { if (StringUtils.isEmpty(keyword)) return Result.error(); return Result.success(blogRepository.findByTitleLike(keyword)); } 我们来测试一下。 POST http://localhost:8080/blog/rep/search/title?keyword=java 结果: { "code": 0, "msg": "Success", "data": [ { "id": "f1XrTmkBTzBv3AXCeeFA", "title": "java实战", "time": "2018-03-01" }, { "id": "fVXrTmkBTzBv3AXCHuGH", "title": "java入门", "time": "2018-01-01" }, { "id": "flXrTmkBTzBv3AXCUOHj", "title": "java基础", "time": "2018-02-01" }, { "id": "gFXrTmkBTzBv3AXCn-Eb", "title": "java web", "time": "2018-04-01" }, { "id": "gVXrTmkBTzBv3AXCzuGh", "title": "java ee", "time": "2018-04-10" } ] } 继续搜索: GET http://localhost:8080/blog/rep/search/title?keyword=入门 结果: { "code": 0, "msg": "Success", "data": [ { "id": "hFXsTmkBTzBv3AXCtOE6", "title": "Elasticsearch入门", "time": "2019-01-20" }, { "id": "fVXrTmkBTzBv3AXCHuGH", "title": "java入门", "time": "2018-01-01" }, { "id": "glXsTmkBTzBv3AXCBeH_", "title": "php入门", "time": "2018-05-10" } ] } 为了验证,我们再换一个关键字搜索: GET http://localhost:8080/blog/rep/search/title?keyword=java入门 { "code": 0, "msg": "Success", "data": [ { "id": "fVXrTmkBTzBv3AXCHuGH", "title": "java入门", "time": "2018-01-01" }, { "id": "hFXsTmkBTzBv3AXCtOE6", "title": "Elasticsearch入门", "time": "2019-01-20" }, { "id": "glXsTmkBTzBv3AXCBeH_", "title": "php入门", "time": "2018-05-10" }, { "id": "gFXrTmkBTzBv3AXCn-Eb", "title": "java web", "time": "2018-04-01" }, { "id": "gVXrTmkBTzBv3AXCzuGh", "title": "java ee", "time": "2018-04-10" }, { "id": "f1XrTmkBTzBv3AXCeeFA", "title": "java实战", "time": "2018-03-01" }, { "id": "flXrTmkBTzBv3AXCUOHj", "title": "java基础", "time": "2018-02-01" } ] } 哈哈,有没有觉得很眼熟。 那根据上次的经验,我们正好换一种方式解决这个问题。 @Query("{\"match_phrase\":{\"title\":\"?0\"}}") List<BlogModel> findByTitleCustom(String keyword); 值得一提的是,官方文档示例代码可能是为了好看,出现问题。 官网文档给的错误示例: ![官网文档错误][format_png 3] 官网示例代码: ![官方示例代码][format_png 4] 官方示例代码 另外,`?0` 代指变量的意思。 @GetMapping("/rep/search/title/custom") public Result repSearchTitleCustom(String keyword) { if (StringUtils.isEmpty(keyword)) return Result.error(); return Result.success(blogRepository.findByTitleCustom(keyword)); } 测试一下: ![测试成功示例][format_png 5] ok,没有问题。 #### ElasticsearchTemplate #### @Autowired private ElasticsearchTemplate elasticsearchTemplate; @GetMapping("/search/title") public Result searchTitle(String keyword) { if (StringUtils.isEmpty(keyword)) return Result.error(); SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(queryStringQuery(keyword)) .build(); List<BlogModel> list = elasticsearchTemplate.queryForList(searchQuery, BlogModel.class); return Result.success(list); } 测试: POST http://localhost:8080/blog/search/title?keyword=java入门 结果: { "code": 0, "msg": "Success", "data": [ { "id": "fVXrTmkBTzBv3AXCHuGH", "title": "java入门", "time": "2018-01-01" }, { "id": "hFXsTmkBTzBv3AXCtOE6", "title": "Elasticsearch入门", "time": "2019-01-20" }, { "id": "glXsTmkBTzBv3AXCBeH_", "title": "php入门", "time": "2018-05-10" }, { "id": "gFXrTmkBTzBv3AXCn-Eb", "title": "java web", "time": "2018-04-01" }, { "id": "gVXrTmkBTzBv3AXCzuGh", "title": "java ee", "time": "2018-04-10" }, { "id": "f1XrTmkBTzBv3AXCeeFA", "title": "java实战", "time": "2018-03-01" }, { "id": "flXrTmkBTzBv3AXCUOHj", "title": "java基础", "time": "2018-02-01" } ] } OK,暂时先到这里,关于搜索,我们后面会专门开一个专题,学习搜索。 ## **第五部分,例子** ## 我们写个什么例子,想了很久,那就写一个搜索手机的例子吧! #### 界面截图 #### 我们先看下最后实现的效果吧 主页效果: ![主页效果][format_png 6] 分页效果: ![分页效果][format_png 7] 我们搜索 “小米”: ![format_png 8][] 我们搜索 “1999”: ![format_png 9][] 我们搜索 “黑色”: ![format_png 10][] 高级搜索页面: ![format_png 11][] 我们使用高级搜索,搜索:“小米”、“1999”: ![format_png 12][] 高级搜索 “小米”、“1999” 结果: ![format_png 13][] 上面的并且关系生效了吗?我们试一下搜索 “华为”,“1999”: ![format_png 14][] 最后,我们尝试搜索时间段: ![format_png 15][] 看一下,搜索结果吧: ![format_png 16][] 说实话,这个时间搜索结果,我不是很满意,ES 的时间问题,我打算在后面花一些时间去研究下。 #### 搭建项目 #### 基于Gradle搭建Spring Boot项目,把我折腾的受不了(如果哪位这方面有经验,可以给我指点指点),这个demo写了很久,那天都跑的好好的,今早上起来,就跑步起来了,一气之下,就改成Maven了。 下面看一下我的依赖和配置 **pom.xml** 片段 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> </parent> <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.github.fengwenyi</groupId> <artifactId>JavaLib</artifactId> <version>1.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.56</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.10.2</version> </dependency> </dependencies> **application.yml** server: port: 9090 spring: data: elasticsearch: cluster-nodes: localhost:9300 cluster-name: es-wyf repositories: enabled: true **PhoneModel** @Data @Accessors(chain = true) @Document(indexName = "springboot_elasticsearch_example_phone", type = "com.fengwenyi.springbootelasticsearchexamplephone.model.PhoneModel") public class PhoneModel implements Serializable { private static final long serialVersionUID = -5087658155687251393L; /* ID */ @Id private String id; /* 名称 */ private String name; /* 颜色,用英文分号(;)分隔 */ private String colors; /* 卖点,用英文分号(;)分隔 */ private String sellingPoints; /* 价格 */ private String price; /* 产量 */ private Long yield; /* 销售量 */ private Long sale; /* 上市时间 */ //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date marketTime; /* 数据抓取时间 */ //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; } **PhoneRepository** public interface PhoneRepository extends ElasticsearchRepository<PhoneModel, String> { } **PhoneController** @RestController @RequestMapping(value = "/phone") @CrossOrigin public class PhoneController { @Autowired private ElasticsearchTemplate elasticsearchTemplate; } 后面接口,都会在这里写。 #### 构造数据 #### 我的数据是抓的 “华为” 和 “小米” 官网 首先使用 `httpclient` 下载html,然后使用 `jsoup` 进行解析。 以 **华为** 为例: private void huawei() throws IOException { CloseableHttpClient httpclient = HttpClients.createDefault(); // 创建httpclient实例 HttpGet httpget = new HttpGet("https://consumer.huawei.com/cn/phones/?ic_medium=hwdc&ic_source=corp_header_consumer"); // 创建httpget实例 CloseableHttpResponse response = httpclient.execute(httpget); // 执行get请求 HttpEntity entity=response.getEntity(); // 获取返回实体 //System.out.println("网页内容:"+ EntityUtils.toString(entity, "utf-8")); // 指定编码打印网页内容 String content = EntityUtils.toString(entity, "utf-8"); response.close(); // 关闭流和释放系统资源 // System.out.println(content); Document document = Jsoup.parse(content); Elements elements = document.select("#content-v3-plp #pagehidedata .plphidedata"); for (Element element : elements) { // System.out.println(element.text()); String jsonStr = element.text(); List<HuaWeiPhoneBean> list = JSON.parseArray(jsonStr, HuaWeiPhoneBean.class); for (HuaWeiPhoneBean bean : list) { String productName = bean.getProductName(); List<ColorModeBean> colorsItemModeList = bean.getColorsItemMode(); StringBuilder colors = new StringBuilder(); for (ColorModeBean colorModeBean : colorsItemModeList) { String colorName = colorModeBean.getColorName(); colors.append(colorName).append(";"); } List<String> sellingPointList = bean.getSellingPoints(); StringBuilder sellingPoints = new StringBuilder(); for (String sellingPoint : sellingPointList) { sellingPoints.append(sellingPoint).append(";"); } // System.out.println("产品名:" + productName); // System.out.println("颜 色:" + color); // System.out.println("买 点:" + sellingPoint); // System.out.println("-----------------------------------"); PhoneModel phoneModel = new PhoneModel() .setName(productName) .setColors(colors.substring(0, colors.length() - 1)) .setSellingPoints(sellingPoints.substring(0, sellingPoints.length() - 1)) .setCreateTime(new Date()); phoneRepository.save(phoneModel); } } } #### 全文搜索 #### 全文搜索来说,还是相对来说,比较简单,直接贴代码吧: /** * 全文搜索 * @param keyword 关键字 * @param page 当前页,从0开始 * @param size 每页大小 * @return {@link Result} 接收到的数据格式为json */ @GetMapping("/full") public Mono<Result> full(String keyword, int page, int size) { // System.out.println(new Date() + " => " + keyword); // 校验参数 if (StringUtils.isEmpty(page)) page = 0; // if page is null, page = 0 if (StringUtils.isEmpty(size)) size = 10; // if size is null, size default 10 // 构造分页类 Pageable pageable = PageRequest.of(page, size); // 构造查询 NativeSearchQueryBuilder NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder() .withPageable(pageable) ; if (!StringUtils.isEmpty(keyword)) { // keyword must not null searchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keyword)); } /* SearchQuery 这个很关键,这是搜索条件的入口, elasticsearchTemplate 会 使用它 进行搜索 */ SearchQuery searchQuery = searchQueryBuilder.build(); // page search Page<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class); // return return Mono.just(Result.success(phoneModelPage)); } 官网文档也是这么用的,所以相对来说,这还是很简单的,不过拆词 和 搜索策略 搜索速度 可能在实际使用中要考虑。 #### 高级搜索 #### 先看代码,后面我们再来分析: /** * 高级搜索,根据字段进行搜索 * @param name 名称 * @param color 颜色 * @param sellingPoint 卖点 * @param price 价格 * @param start 开始时间(格式:yyyy-MM-dd HH:mm:ss) * @param end 结束时间(格式:yyyy-MM-dd HH:mm:ss) * @param page 当前页,从0开始 * @param size 每页大小 * @return {@link Result} */ @GetMapping("/_search") public Mono<Result> search(String name, String color, String sellingPoint, String price, String start, String end, int page, int size) { // 校验参数 if (StringUtils.isEmpty(page) || page < 0) page = 0; // if page is null, page = 0 if (StringUtils.isEmpty(size) || size < 0) size = 10; // if size is null, size default 10 // 构造分页对象 Pageable pageable = PageRequest.of(page, size); // BoolQueryBuilder (Elasticsearch Query) BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); if (!StringUtils.isEmpty(name)) { boolQueryBuilder.must(QueryBuilders.matchQuery("name", name)); } if (!StringUtils.isEmpty(color)) { boolQueryBuilder.must(QueryBuilders.matchQuery("colors", color)); } if (!StringUtils.isEmpty(color)) { boolQueryBuilder.must(QueryBuilders.matchQuery("sellingPoints", sellingPoint)); } if (!StringUtils.isEmpty(price)) { boolQueryBuilder.must(QueryBuilders.matchQuery("price", price)); } if (!StringUtils.isEmpty(start)) { Date startTime = null; try { startTime = DateTimeUtil.stringToDate(start, DateTimeFormat.yyyy_MM_dd_HH_mm_ss); } catch (ParseException e) { e.printStackTrace(); } boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").gt(startTime.getTime())); } if (!StringUtils.isEmpty(end)) { Date endTime = null; try { endTime = DateTimeUtil.stringToDate(end, DateTimeFormat.yyyy_MM_dd_HH_mm_ss); } catch (ParseException e) { e.printStackTrace(); } boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").lt(endTime.getTime())); } // BoolQueryBuilder (Spring Query) SearchQuery searchQuery = new NativeSearchQueryBuilder() .withPageable(pageable) .withQuery(boolQueryBuilder) .build() ; // page search Page<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class); // return return Mono.just(Result.success(phoneModelPage)); } 不管spring如何封装,查询方式都一样,如下图: ![es 搜索 语句][es _] 好吧,我们怀着这样的心态去看下源码。 org.springframework.data.elasticsearch.core.query.SearchQuery 这个是我们搜索需要用到对象 public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) { this.queryBuilder = queryBuilder; return this; } OK,根据源码,我们需要构造这个 QueryBuilder,那么问题来了,这个是个什么东西,我们要如何构造,继续看: org.elasticsearch.index.query.QueryBuilder 注意包名。 啥,怎么又跑到 elasticsearch。 你想啊,你写的东西,会让别人直接操作吗? 答案是不会的,我们只会提供API,所有,不管Spring如何封装,也只会通过API去调用。 ![query 包下得类][query] 好吧,今天先到这里,下一个专题,我们再讨论关于搜索问题。 ## **链接** ## * ElasticSearch入门:https://www.imooc.com/learn/889 * Elastic官网:https://www.elastic.co/ * ElasticSearch:https://www.elastic.co/cn/products/elasticsearch * ElasticSearch Docs:https://github.com/mobz/elasticsearch-head * ElasticSearch Head:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html * 搜索软件Elastic上市:市值近50亿美元 是开源项目商业化范本 * http://www.sohu.com/a/257956489\_430392 ## **ElasticSearch 学习系列** ## * Elasticsearch入门篇——基础知识 * https://www.jianshu.com/p/7ea5f6fa5d66 * Elasticsearch实战篇——Spring Boot整合ElasticSearch * https://www.jianshu.com/p/bd2da1cde6f5 * Elasticsearch专题篇——搜索 * https://www.jianshu.com/p/69dc8ff24ecc ## **代码** ## Spring Boot整合Elasticsearch https://github.com/fengwenyi/learn-springboot/tree/master/springboot-elasticsearch Spring Boot结合Elasticsearch,实现手机信息搜索小例子 https://github.com/fengwenyi/learn-springboot/tree/master/springboot-elasticsearch-example-phone -------------------- \-END- 如果看到这里,说明你喜欢这篇文章,请 转发、点赞。同时 标星(置顶)本公众号可以第一时间接受到博文推送。 **推****荐****阅****读** ***1. ***[让你纵横 GitHub 的五大神器][GitHub] ***2. ***[再见,Eclipse!][Eclipse] ***3.*** [干掉 Navicat:这个 IDEA 的兄弟真香!][Navicat_ IDEA] ***4. ***[你必须掌握的 21 个 Java 核心技术!][21 _ Java] ![format_png 17][] 最近整理一份面试资料**《Java技术栈学习手册》**,覆盖了Java技术、面试题精选、Spring全家桶、Nginx、SSM、微服务、数据库、数据结构、架构等等。 获取方式:点“ **在看**,关注公众号 **Java后端** 并回复 **777** 领取,更多内容陆续奉上。 喜欢文章,点个在看 ![format_png 18][] [Java]: https://blog.csdn.net/qq_37217713/article/details/101445094 [ElasticsearchRepository]: /images/20210730/34bf7e3ce7544c668e74edd781cbfed7.png [ID]: /images/20210730/0ad431f07c9b48b482824a6a154286fa.png [format_png]: /images/20210730/deb4b5a33e1f46d0813ef9664014b27e.png [format_png 1]: /images/20210730/f4790d9c51f5412b813d01940fe7eb68.png [format_png 2]: /images/20210730/47736d6190834e58add9119235368a59.png [format_png 3]: /images/20210730/e02085dca0a94b3a870f043fcafbb1a5.png [format_png 4]: /images/20210730/8fed9bf4c6a24509930e38a78dcb3734.png [format_png 5]: /images/20210730/483e39b9ed0343f2a74084824dff7349.png [format_png 6]: /images/20210730/7a34a23a91a24795a104248e762d1bda.png [format_png 7]: /images/20210730/3c250699ee74417a93a6a2e347f0115f.png [format_png 8]: /images/20210730/a4120a9e56b74044bc52d9a389deafe9.png [format_png 9]: /images/20210730/b5ced3d804c2424e9bc5d5ff24f649ff.png [format_png 10]: /images/20210730/045cc832d44b440eb218ff87f781745d.png [format_png 11]: /images/20210730/c4f8b4ad45a0471f877912849da11d39.png [format_png 12]: /images/20210730/7959e272772f4bbd98995594e8f30fce.png [format_png 13]: /images/20210730/66491aff89714da19a2a9c13c390b818.png [format_png 14]: /images/20210730/6e70f8d0c5f946ad86b1e35a90e3ef31.png [format_png 15]: /images/20210730/fdfb7c2b670b4fbdb17d499b8b8bd4ed.png [format_png 16]: /images/20210730/6094a9eb7642484f886abebf0d36de15.png [es _]: /images/20210730/93824ccd03da45cd9783d3977eb97860.png [query]: /images/20210730/76aa81974602424098b299bd36c65999.png [GitHub]: https://blog.csdn.net/qq_37217713/article/details/106346002 [Eclipse]: https://blog.csdn.net/qq_37217713/article/details/106324487 [Navicat_ IDEA]: https://blog.csdn.net/qq_37217713/article/details/106324488 [21 _ Java]: https://blog.csdn.net/qq_37217713/article/details/106309035 [format_png 17]: /images/20210730/07d40b76e8504419a94103eced303e4e.png [format_png 18]: /images/20210730/c40389e6410e4d4ab6e38b6dc28abb31.png
还没有评论,来说两句吧...