作者:冯文议

当前Spring Boot很是流行,包括我自己,也是在用Spring Boot集成其他框架进行项目开发,所以这一节,我们一起来探讨Spring Boot整合ElasticSearch的问题。

本文主要讲以下内容:

  • 第一部分,通读文档

  • 第二部分,Spring Boot整合ElasticSearch

  • 第三部分,基本的CRUD操作

  • 第四部分,搜索

  • 第五部分,例子

还没有学过Elasticsearch的朋友,可以先学这个系列的第一节(这个系列共三节),如果你有不明白或者不正确的地方,可以给我评论、留言或者私信。

第一步,通读文档

关于repository

文档一开始就介绍 CrudRepository ,比如,继承 Repository,其他比如 JpaRepository、 MongoRepository是继承 CrudRepository。也对其中的方法做了简单说明,我们一起来看一下:

  1. public interface CrudRepository<T, ID extends Serializable>

  2. extends Repository<T, ID> {

  3.  

  4. // Saves the given entity.

  5. <S extends T> S save(S entity);

  6.  

  7. // Returns the entity identified by the given ID.

  8. Optional<T> findById(ID primaryKey);

  9.  

  10. // Returns all entities.

  11. Iterable<T> findAll();

  12.  

  13. // Returns the number of entities.

  14. long count();

  15.  

  16. // Deletes the given entity.

  17. void delete(T entity);

  18.  

  19. // Indicates whether an entity with the given ID exists.

  20. boolean existsById(ID primaryKey);

  21.  

  22. // ... more functionality omitted.

  23. }

好了,下面我们看一下今天的主角 ElasticsearchRepository 他是怎样的吧。

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_elasticsearch

这说明什么?

  • 用法和JPA一样;

  • 再这他除了有CRUD的基本功能之外,还有分页和排序。

清楚了这之后,是不是应该考虑该如何使用了呢?

如何用?

没错,接下来,开始说如何用,也写了很多示例代码。相对来说,还是比较简单,这里就贴一下代码就行了吧。

  1. interface PersonRepository extends Repository<User, Long> {

  2.  

  3. List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  4.  

  5. // Enables the distinct flag for the query

  6. List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);

  7. List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  8.  

  9. // Enabling ignoring case for an individual property

  10. List<Person> findByLastnameIgnoreCase(String lastname);

  11. // Enabling ignoring case for all suitable properties

  12. List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  13.  

  14. // Enabling static ORDER BY for a query

  15. List<Person> findByLastnameOrderByFirstnameAsc(String lastname);

  16. List<Person> findByLastnameOrderByFirstnameDesc(String lastname);

  17. }

是不是这样,就可以正常使用了呢?

问题

当然可以,但是如果错了问题怎么办呢,官网写了一个常见的问题,比如包扫描问题,没有你要的方法。

  1. interface HumanRepository {

  2. void someHumanMethod(User user);

  3. }

  4.  

  5. class HumanRepositoryImpl implements HumanRepository {

  6.  

  7. public void someHumanMethod(User user) {

  8. // Your custom implementation

  9. }

  10. }

  11.  

  12. interface ContactRepository {

  13.  

  14. void someContactMethod(User user);

  15.  

  16. User anotherContactMethod(User user);

  17. }

  18.  

  19. class ContactRepositoryImpl implements ContactRepository {

  20.  

  21. public void someContactMethod(User user) {

  22. // Your custom implementation

  23. }

  24.  

  25. public User anotherContactMethod(User user) {

  26. // Your custom implementation

  27. }

  28. }

你也可以自己写接口,并且去实现它。

说完理论,作为我,应该在实际的代码中如何运用呢?

示例

官方也提供了很多示例代码,我们一起来看看。

  1. @Controller

  2. class PersonController {

  3.  

  4. @Autowired PersonRepository repository;

  5.  

  6. @RequestMapping(value = "/persons", method = RequestMethod.GET)

  7. HttpEntity<PagedResources<Person>> persons(Pageable pageable,

  8. PagedResourcesAssembler assembler) {

  9.  

  10. Page<Person> persons = repository.findAll(pageable);

  11. return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);

  12. }

  13. }

这段代码相对来说还是十分经典的,我相信很多人都看到别人的代码,可能都会问,它为什么会这么用呢,答案或许就在这里吧。

当然,这是以前的代码,或许现在用不一定合适。

高级搜索

终于到高潮了!

学完我的第一节,你应该已经发现了,Elasticsearch搜索是一件十分复杂的事,为了用好它,我们不得不学好它。一起加油。

到这里,官方文档我们算是过了一遍了,大致明白了,他要告诉我们什么。其实,文档还有很多内容,可能你遇到的问题都能在里面找到答案。

最后,我们继续看一下官网写的一段处理得十分优秀的一段代码吧:

  1. SearchQuery searchQuery = new NativeSearchQueryBuilder()

  2. .withQuery(matchAllQuery())

  3. .withIndices(INDEX_NAME)

  4. .withTypes(TYPE_NAME)

  5. .withFields("message")

  6. .withPageable(PageRequest.of(0, 10))

  7. .build();

  8.  

  9. CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class);

  10.  

  11. List<SampleEntity> sampleEntities = new ArrayList<>();

  12. while (stream.hasNext()) {

  13. sampleEntities.add(stream.next());

  14. }

第二部分,Spring Boot整合ElasticSearch

添加依赖

  1. implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

添加配置

  1. spring:

  2. data:

  3. elasticsearch:

  4. cluster-nodes: localhost:9300

  5. cluster-name: es-wyf

这样就完成了整合,接下来我们用两种方式操作。

Model

我们先写一个的实体类,借助这个实体类呢来完成基础的CRUD功能。

  1. @Data

  2. @Accessors(chain = true)

  3. @Document(indexName = "blog", type = "java")

  4. public class BlogModel implements Serializable {

  5.  

  6. private static final long serialVersionUID = 6320548148250372657L;

  7.  

  8. @Id

  9. private String id;

  10.  

  11. private String title;

  12.  

  13. //@Field(type = FieldType.Date, format = DateFormat.basic_date)

  14. @DateTimeFormat(pattern = "yyyy-MM-dd")

  15. @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")

  16. private Date time;

  17. }

注意id字段是必须的,可以不写注解@Id。

BlogRepository

  1. public interface BlogRepository extends ElasticsearchRepository<BlogModel, String> {

  2. }

第三部分,CRUD

基础操作的代码,都是在 BlogController 里面写。

  1. @RestController

  2. @RequestMapping("/blog")

  3. public class BlogController {

  4. @Autowired

  5. private BlogRepository blogRepository;

  6. }

添加

  1. @PostMapping("/add")

  2. public Result add(@RequestBody BlogModel blogModel) {

  3. blogRepository.save(blogModel);

  4. return Result.success();

  5. }

我们添加一条数据,标题是:Elasticsearch实战篇:Spring Boot整合ElasticSearch,时间是:2019-03-06。我们来测试,看一下成不成功。

POST http://localhost:8080/blog/add

  1. {

  2. "title":"Elasticsearch实战篇:Spring Boot整合ElasticSearch",

  3. "time":"2019-05-06"

  4. }

得到响应:

  1. {

  2. "code": 0,

  3. "msg": "Success"

  4. }

嘿,成功了。那接下来,我们一下查询方法测试一下。

查询

  • 根据ID查询

  1. @GetMapping("/get/{id}")

  2. public Result getById(@PathVariable String id) {

  3. if (StringUtils.isEmpty(id))

  4. return Result.error();

  5. Optional<BlogModel> blogModelOptional = blogRepository.findById(id);

  6. if (blogModelOptional.isPresent()) {

  7. BlogModel blogModel = blogModelOptional.get();

  8. return Result.success(blogModel);

  9. }

  10. return Result.error();

  11. }

测试一下:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_搜索_02

ok,没问题。

  • 查询所有

  1. @GetMapping("/get")

  2. public Result getAll() {

  3. Iterable<BlogModel> iterable = blogRepository.findAll();

  4. List<BlogModel> list = new ArrayList<>();

  5. iterable.forEach(list::add);

  6. return Result.success(list);

  7. }

测试一下:

GET http://localhost:8080/blog/get

结果:

  1. {

  2. "code": 0,

  3. "msg": "Success",

  4. "data": [

  5. {

  6. "id": "fFXTTmkBTzBv3AXCweFS",

  7. "title": "Elasticsearch实战篇:Spring Boot整合ElasticSearch",

  8. "time": "2019-05-06"

  9. }

  10. ]

  11. }

根据ID修改

  1. @PostMapping("/update")

  2. public Result updateById(@RequestBody BlogModel blogModel) {

  3. String id = blogModel.getId();

  4. if (StringUtils.isEmpty(id))

  5. return Result.error();

  6. blogRepository.save(blogModel);

  7. return Result.success();

  8. }

测试:

POST http://localhost:8080/blog/update

  1. {

  2. "id":"fFXTTmkBTzBv3AXCweFS",

  3. "title":"Elasticsearch入门篇",

  4. "time":"2019-05-01"

  5. }

响应:

  1. {

  2. "code": 0,

  3. "msg": "Success"

  4. }

查询一下:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_elasticsearch_03

ok,成功!

删除

  • 根据ID删除

  1. @DeleteMapping("/delete/{id}")

  2. public Result deleteById(@PathVariable String id) {

  3. if (StringUtils.isEmpty(id))

  4. return Result.error();

  5. blogRepository.deleteById(id);

  6. return Result.success();

  7. }

测试:

DELETE http://localhost:8080/blog/delete/fFXTTmkBTzBv3AXCweFS

响应:

  1. {

  2. "code": 0,

  3. "msg": "Success"

  4. }

我们再查一下:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_spring_04

  • 删除所有数据

  1. @DeleteMapping("/delete")

  2. public Result deleteById() {

  3. blogRepository.deleteAll();

  4. return Result.success();

  5. }

分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!

第四部分,搜索

构造数据

为了方便测试,我们先构造数据

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_java_05

Repository查询操作

搜索标题中的关键字

BlogRepository

  1. List<BlogModel> findByTitleLike(String keyword);

BlogController

  1. @GetMapping("/rep/search/title")

  2. public Result repSearchTitle(String keyword) {

  3. if (StringUtils.isEmpty(keyword))

  4. return Result.error();

  5. return Result.success(blogRepository.findByTitleLike(keyword));

  6. }

我们来测试一下。

POST http://localhost:8080/blog/rep/search/title?keyword=java

结果:

  1. {

  2. "code": 0,

  3. "msg": "Success",

  4. "data": [

  5. {

  6. "id": "f1XrTmkBTzBv3AXCeeFA",

  7. "title": "java实战",

  8. "time": "2018-03-01"

  9. },

  10. {

  11. "id": "fVXrTmkBTzBv3AXCHuGH",

  12. "title": "java入门",

  13. "time": "2018-01-01"

  14. },

  15. {

  16. "id": "flXrTmkBTzBv3AXCUOHj",

  17. "title": "java基础",

  18. "time": "2018-02-01"

  19. },

  20. {

  21. "id": "gFXrTmkBTzBv3AXCn-Eb",

  22. "title": "java web",

  23. "time": "2018-04-01"

  24. },

  25. {

  26. "id": "gVXrTmkBTzBv3AXCzuGh",

  27. "title": "java ee",

  28. "time": "2018-04-10"

  29. }

  30. ]

  31. }

继续搜索:

GET http://localhost:8080/blog/rep/search/title?keyword=入门

结果:

  1. {

  2. "code": 0,

  3. "msg": "Success",

  4. "data": [

  5. {

  6. "id": "hFXsTmkBTzBv3AXCtOE6",

  7. "title": "Elasticsearch入门",

  8. "time": "2019-01-20"

  9. },

  10. {

  11. "id": "fVXrTmkBTzBv3AXCHuGH",

  12. "title": "java入门",

  13. "time": "2018-01-01"

  14. },

  15. {

  16. "id": "glXsTmkBTzBv3AXCBeH_",

  17. "title": "php入门",

  18. "time": "2018-05-10"

  19. }

  20. ]

  21. }

为了验证,我们再换一个关键字搜索:

GET http://localhost:8080/blog/rep/search/title?keyword=java入门

  1. {

  2. "code": 0,

  3. "msg": "Success",

  4. "data": [

  5. {

  6. "id": "fVXrTmkBTzBv3AXCHuGH",

  7. "title": "java入门",

  8. "time": "2018-01-01"

  9. },

  10. {

  11. "id": "hFXsTmkBTzBv3AXCtOE6",

  12. "title": "Elasticsearch入门",

  13. "time": "2019-01-20"

  14. },

  15. {

  16. "id": "glXsTmkBTzBv3AXCBeH_",

  17. "title": "php入门",

  18. "time": "2018-05-10"

  19. },

  20. {

  21. "id": "gFXrTmkBTzBv3AXCn-Eb",

  22. "title": "java web",

  23. "time": "2018-04-01"

  24. },

  25. {

  26. "id": "gVXrTmkBTzBv3AXCzuGh",

  27. "title": "java ee",

  28. "time": "2018-04-10"

  29. },

  30. {

  31. "id": "f1XrTmkBTzBv3AXCeeFA",

  32. "title": "java实战",

  33. "time": "2018-03-01"

  34. },

  35. {

  36. "id": "flXrTmkBTzBv3AXCUOHj",

  37. "title": "java基础",

  38. "time": "2018-02-01"

  39. }

  40. ]

  41. }

哈哈,有没有觉得很眼熟。

那根据上次的经验,我们正好换一种方式解决这个问题。

  1. @Query("{\"match_phrase\":{\"title\":\"?0\"}}")

  2. List<BlogModel> findByTitleCustom(String keyword);

值得一提的是,官方文档示例代码可能是为了好看,出现问题。

官网文档给的错误示例:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_spring_06

官网示例代码:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_spring_07

官方示例代码

另外, ?0 代指变量的意思。

  1. @GetMapping("/rep/search/title/custom")

  2. public Result repSearchTitleCustom(String keyword) {

  3. if (StringUtils.isEmpty(keyword))

  4. return Result.error();

  5. return Result.success(blogRepository.findByTitleCustom(keyword));

  6. }

测试一下:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_elasticsearch_08

ok,没有问题。

ElasticsearchTemplate

  1. @Autowired

  2. private ElasticsearchTemplate elasticsearchTemplate;

  3.  

  4. @GetMapping("/search/title")

  5. public Result searchTitle(String keyword) {

  6. if (StringUtils.isEmpty(keyword))

  7. return Result.error();

  8. SearchQuery searchQuery = new NativeSearchQueryBuilder()

  9. .withQuery(queryStringQuery(keyword))

  10. .build();

  11. List<BlogModel> list = elasticsearchTemplate.queryForList(searchQuery, BlogModel.class);

  12. return Result.success(list);

  13. }

测试:

POST http://localhost:8080/blog/search/title?keyword=java入门

结果:

  1. {

  2. "code": 0,

  3. "msg": "Success",

  4. "data": [

  5. {

  6. "id": "fVXrTmkBTzBv3AXCHuGH",

  7. "title": "java入门",

  8. "time": "2018-01-01"

  9. },

  10. {

  11. "id": "hFXsTmkBTzBv3AXCtOE6",

  12. "title": "Elasticsearch入门",

  13. "time": "2019-01-20"

  14. },

  15. {

  16. "id": "glXsTmkBTzBv3AXCBeH_",

  17. "title": "php入门",

  18. "time": "2018-05-10"

  19. },

  20. {

  21. "id": "gFXrTmkBTzBv3AXCn-Eb",

  22. "title": "java web",

  23. "time": "2018-04-01"

  24. },

  25. {

  26. "id": "gVXrTmkBTzBv3AXCzuGh",

  27. "title": "java ee",

  28. "time": "2018-04-10"

  29. },

  30. {

  31. "id": "f1XrTmkBTzBv3AXCeeFA",

  32. "title": "java实战",

  33. "time": "2018-03-01"

  34. },

  35. {

  36. "id": "flXrTmkBTzBv3AXCUOHj",

  37. "title": "java基础",

  38. "time": "2018-02-01"

  39. }

  40. ]

  41. }

OK,暂时先到这里,关于搜索,我们后面会专门开一个专题,学习搜索。

分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!

第五部分,例子

我们写个什么例子,想了很久,那就写一个搜索手机的例子吧!

界面截图

我们先看下最后实现的效果吧

主页效果:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_spring_09

分页效果:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_json_10

我们搜索 "小米":

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_java_11

我们使用高级搜索,搜索:"小米"、"1999":

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_json_12

高级搜索 "小米"、"1999" 结果:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_elasticsearch_13

上面的并且关系生效了吗?我们试一下搜索 "华为","1999":

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_java_14

最后,我们尝试搜索时间段:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_json_15

看一下,搜索结果吧:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_spring_16

说实话,这个时间搜索结果,我不是很满意,ES 的时间问题,我打算在后面花一些时间去研究下。

搭建项目

基于Gradle搭建Spring Boot项目,把我折腾的受不了(如果哪位这方面有经验,可以给我指点指点),这个demo写了很久,那天都跑的好好的,今早上起来,就跑步起来了,一气之下,就改成Maven了。

下面看一下我的依赖和配置

pom.xml 片段

  1. <parent>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-parent</artifactId>

  4. <version>2.1.3.RELEASE</version>

  5. <relativePath/> <!-- lookup parent from repository -->

  6. </parent>

  7.  

  8. <repositories>

  9. <repository>

  10. <id>jitpack.io</id>

  11. <url>https://jitpack.io</url>

  12. </repository>

  13. </repositories>

  14.  

  15. <dependencies>

  16. <dependency>

  17. <groupId>org.springframework.boot</groupId>

  18. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>

  19. </dependency>

  20. <dependency>

  21. <groupId>org.springframework.boot</groupId>

  22. <artifactId>spring-boot-starter-web</artifactId>

  23. </dependency>

  24.  

  25. <dependency>

  26. <groupId>org.projectlombok</groupId>

  27. <artifactId>lombok</artifactId>

  28. <optional>true</optional>

  29. </dependency>

  30. <dependency>

  31. <groupId>org.springframework.boot</groupId>

  32. <artifactId>spring-boot-starter-test</artifactId>

  33. <scope>test</scope>

  34. </dependency>

  35.  

  36. <!--

  37. 添加 JavaLib 支持

  38. 用于接口返回

  39. -->

  40. <dependency>

  41. <groupId>com.github.fengwenyi</groupId>

  42. <artifactId>JavaLib</artifactId>

  43. <version>1.0.7.RELEASE</version>

  44. </dependency>

  45.  

  46. <!--

  47. 添加 webflux 支持

  48. 用于编写非阻塞接口

  49. -->

  50. <dependency>

  51. <groupId>org.springframework.boot</groupId>

  52. <artifactId>spring-boot-starter-webflux</artifactId>

  53. </dependency>

  54.  

  55. <!--

  56. 添加 fastjson 的支持

  57. 用于处理JSON格式数据

  58. -->

  59. <dependency>

  60. <groupId>com.alibaba</groupId>

  61. <artifactId>fastjson</artifactId>

  62. <version>1.2.56</version>

  63. </dependency>

  64.  

  65. <!--

  66. 添加 Httpclient 的支持

  67. 用于网络请求

  68. -->

  69. <dependency>

  70. <groupId>org.apache.httpcomponents</groupId>

  71. <artifactId>httpclient</artifactId>

  72. <version>4.5.7</version>

  73. </dependency>

  74.  

  75. <!--

  76. 添加 jsoup 的支持

  77. 用于解析网页内容

  78. -->

  79. <dependency>

  80. <groupId>org.jsoup</groupId>

  81. <artifactId>jsoup</artifactId>

  82. <version>1.10.2</version>

  83. </dependency>

  84. </dependencies>

application.yml

  1. server:

  2. port: 9090

  3.  

  4. spring:

  5. data:

  6. elasticsearch:

  7. cluster-nodes: localhost:9300

  8. cluster-name: es-wyf

  9. repositories:

  10. enabled: true

PhoneModel

  1. @Data

  2. @Accessors(chain = true)

  3. @Document(indexName = "springboot_elasticsearch_example_phone", type = "com.fengwenyi.springbootelasticsearchexamplephone.model.PhoneModel")

  4. public class PhoneModel implements Serializable {

  5. private static final long serialVersionUID = -5087658155687251393L;

  6.  

  7. /* ID */

  8. @Id

  9. private String id;

  10.  

  11. /* 名称 */

  12. private String name;

  13.  

  14. /* 颜色,用英文分号(;)分隔 */

  15. private String colors;

  16.  

  17. /* 卖点,用英文分号(;)分隔 */

  18. private String sellingPoints;

  19.  

  20. /* 价格 */

  21. private String price;

  22.  

  23. /* 产量 */

  24. private Long yield;

  25.  

  26. /* 销售量 */

  27. private Long sale;

  28.  

  29. /* 上市时间 */

  30. //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

  31. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

  32. private Date marketTime;

  33.  

  34. /* 数据抓取时间 */

  35. //@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

  36. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

  37. private Date createTime;

  38.  

  39. }

PhoneRepository

  1. public interface PhoneRepository extends ElasticsearchRepository<PhoneModel, String> {

  2. }

PhoneController

  1. @RestController

  2. @RequestMapping(value = "/phone")

  3. @CrossOrigin

  4. public class PhoneController {

  5.  

  6. @Autowired

  7. private ElasticsearchTemplate elasticsearchTemplate;

  8.  

  9. }

后面接口,都会在这里写。

构造数据

我的数据是抓的 "华为" 和 "小米" 官网

首先使用 httpclient 下载html,然后使用 jsoup 进行解析。

以 华为 为例:

  1. private void huawei() throws IOException {

  2. CloseableHttpClient httpclient = HttpClients.createDefault(); // 创建httpclient实例

  3. HttpGet httpget = new HttpGet("https://consumer.huawei.com/cn/phones/?ic_medium=hwdc&ic_source=corp_header_consumer"); // 创建httpget实例

  4.  

  5. CloseableHttpResponse response = httpclient.execute(httpget); // 执行get请求

  6. HttpEntity entity=response.getEntity(); // 获取返回实体

  7. //System.out.println("网页内容:"+ EntityUtils.toString(entity, "utf-8")); // 指定编码打印网页内容

  8. String content = EntityUtils.toString(entity, "utf-8");

  9. response.close(); // 关闭流和释放系统资源

  10.  

  11. // System.out.println(content);

  12.  

  13. Document document = Jsoup.parse(content);

  14. Elements elements = document.select("#content-v3-plp #pagehidedata .plphidedata");

  15. for (Element element : elements) {

  16. // System.out.println(element.text());

  17. String jsonStr = element.text();

  18. List<HuaWeiPhoneBean> list = JSON.parseArray(jsonStr, HuaWeiPhoneBean.class);

  19. for (HuaWeiPhoneBean bean : list) {

  20. String productName = bean.getProductName();

  21. List<ColorModeBean> colorsItemModeList = bean.getColorsItemMode();

  22.  

  23. StringBuilder colors = new StringBuilder();

  24. for (ColorModeBean colorModeBean : colorsItemModeList) {

  25. String colorName = colorModeBean.getColorName();

  26. colors.append(colorName).append(";");

  27. }

  28.  

  29. List<String> sellingPointList = bean.getSellingPoints();

  30. StringBuilder sellingPoints = new StringBuilder();

  31. for (String sellingPoint : sellingPointList) {

  32. sellingPoints.append(sellingPoint).append(";");

  33. }

  34.  

  35. // System.out.println("产品名:" + productName);

  36. // System.out.println("颜 色:" + color);

  37. // System.out.println("买 点:" + sellingPoint);

  38. // System.out.println("-----------------------------------");

  39. PhoneModel phoneModel = new PhoneModel()

  40. .setName(productName)

  41. .setColors(colors.substring(0, colors.length() - 1))

  42. .setSellingPoints(sellingPoints.substring(0, sellingPoints.length() - 1))

  43. .setCreateTime(new Date());

  44. phoneRepository.save(phoneModel);

  45. }

  46. }

  47. }

全文搜索

全文搜索来说,还是相对来说,比较简单,直接贴代码吧:

  1. /**

  2. * 全文搜索

  3. * @param keyword 关键字

  4. * @param page 当前页,从0开始

  5. * @param size 每页大小

  6. * @return {@link Result} 接收到的数据格式为json

  7. */

  8. @GetMapping("/full")

  9. public Mono<Result> full(String keyword, int page, int size) {

  10. // System.out.println(new Date() + " => " + keyword);

  11.  

  12. // 校验参数

  13. if (StringUtils.isEmpty(page))

  14. page = 0; // if page is null, page = 0

  15.  

  16. if (StringUtils.isEmpty(size))

  17. size = 10; // if size is null, size default 10

  18.  

  19. // 构造分页类

  20. Pageable pageable = PageRequest.of(page, size);

  21.  

  22. // 构造查询 NativeSearchQueryBuilder

  23. NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder()

  24. .withPageable(pageable)

  25. ;

  26. if (!StringUtils.isEmpty(keyword)) {

  27. // keyword must not null

  28. searchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keyword));

  29. }

  30.  

  31. /*

  32. SearchQuery

  33. 这个很关键,这是搜索条件的入口,

  34. elasticsearchTemplate 会 使用它 进行搜索

  35. */

  36. SearchQuery searchQuery = searchQueryBuilder.build();

  37.  

  38. // page search

  39. Page<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);

  40.  

  41. // return

  42. return Mono.just(Result.success(phoneModelPage));

  43. }

官网文档也是这么用的,所以相对来说,这还是很简单的,不过拆词 和 搜索策略 搜索速度 可能在实际使用中要考虑。

2020年最新的常问企业面试题大全以及答案

高级搜索

先看代码,后面我们再来分析:

  1. /**

  2. * 高级搜索,根据字段进行搜索

  3. * @param name 名称

  4. * @param color 颜色

  5. * @param sellingPoint 卖点

  6. * @param price 价格

  7. * @param start 开始时间(格式:yyyy-MM-dd HH:mm:ss)

  8. * @param end 结束时间(格式:yyyy-MM-dd HH:mm:ss)

  9. * @param page 当前页,从0开始

  10. * @param size 每页大小

  11. * @return {@link Result}

  12. */

  13. @GetMapping("/_search")

  14. public Mono<Result> search(String name, String color, String sellingPoint, String price, String start, String end, int page, int size) {

  15.  

  16. // 校验参数

  17. if (StringUtils.isEmpty(page) || page < 0)

  18. page = 0; // if page is null, page = 0

  19.  

  20. if (StringUtils.isEmpty(size) || size < 0)

  21. size = 10; // if size is null, size default 10

  22.  

  23. // 构造分页对象

  24. Pageable pageable = PageRequest.of(page, size);

  25.  

  26. // BoolQueryBuilder (Elasticsearch Query)

  27. BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

  28. if (!StringUtils.isEmpty(name)) {

  29. boolQueryBuilder.must(QueryBuilders.matchQuery("name", name));

  30. }

  31.  

  32. if (!StringUtils.isEmpty(color)) {

  33. boolQueryBuilder.must(QueryBuilders.matchQuery("colors", color));

  34. }

  35.  

  36. if (!StringUtils.isEmpty(color)) {

  37. boolQueryBuilder.must(QueryBuilders.matchQuery("sellingPoints", sellingPoint));

  38. }

  39.  

  40. if (!StringUtils.isEmpty(price)) {

  41. boolQueryBuilder.must(QueryBuilders.matchQuery("price", price));

  42. }

  43.  

  44. if (!StringUtils.isEmpty(start)) {

  45. Date startTime = null;

  46. try {

  47. startTime = DateTimeUtil.stringToDate(start, DateTimeFormat.yyyy_MM_dd_HH_mm_ss);

  48. } catch (ParseException e) {

  49. e.printStackTrace();

  50. }

  51. boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").gt(startTime.getTime()));

  52. }

  53.  

  54. if (!StringUtils.isEmpty(end)) {

  55. Date endTime = null;

  56. try {

  57. endTime = DateTimeUtil.stringToDate(end, DateTimeFormat.yyyy_MM_dd_HH_mm_ss);

  58. } catch (ParseException e) {

  59. e.printStackTrace();

  60. }

  61. boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").lt(endTime.getTime()));

  62. }

  63.  

  64. // BoolQueryBuilder (Spring Query)

  65. SearchQuery searchQuery = new NativeSearchQueryBuilder()

  66. .withPageable(pageable)

  67. .withQuery(boolQueryBuilder)

  68. .build()

  69. ;

  70.  

  71. // page search

  72. Page<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);

  73.  

  74. // return

  75. return Mono.just(Result.success(phoneModelPage));

  76. }

不管spring如何封装,查询方式都一样,如下图:

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_java_17

好吧,我们怀着这样的心态去看下源码。

  1. org.springframework.data.elasticsearch.core.query.SearchQuery

这个是我们搜索需要用到对象

  1. public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {

  2. this.queryBuilder = queryBuilder;

  3. return this;

  4. }

OK,根据源码,我们需要构造这个 QueryBuilder,那么问题来了,这个是个什么东西,我们要如何构造,继续看:

  1. org.elasticsearch.index.query.QueryBuilder

注意包名。

啥,怎么又跑到 elasticsearch。

你想啊,你写的东西,会让别人直接操作吗?

答案是不会的,我们只会提供API,所有,不管Spring如何封装,也只会通过API去调用。

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_json_18

好吧,今天先到这里,下一个专题,我们再讨论关于搜索问题。

 

 

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_elasticsearch_19

 

很全很牛逼,看完这篇Elasticsearch实战,我觉得我可以写个百度~_搜索_20