基础集成

引入项目依赖,下面只引入关键依赖。

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.4.1</version>
  5. <relativePath/>
  6. <!-- lookup parent from repository -->
  7. </parent>
  8. <dependencies>
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  12. </dependency>
  13. </dependencies>

不同的版本有不同的依赖,请选择自己需要的版本,下图是具体的信息。

Spring Data Release Train Spring Data Elasticsearch Elasticsearch Spring Boot
2020.0.01 4.1.x1 7.9.3 2.4.x1
Neumann 4.0.x 7.6.2 2.3.x
Moore 3.2.x 6.8.12 2.2.x
Lovelace 3.1.x 6.2.2 2.1.x
Kay2 3.0.x2 5.5.0 2.0.x2
Ingalls2 2.1.x2 2.4.0 1.5.x2

对应文档:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/

在当前的 SpringBoot 2.4 + 版本,已经不再使用 TransportClientElasticsearchTemplate ,而是推荐使用:ElasticsearchRestTemplate ,同时此时不需要手动引入关键类,只需要在 application.peroperties中配置如下信息即可。

  1. spring.elasticsearch.rest.uris=http://localhost:9200

使用的时候,通过 Autowired 注入ElasticsearchRestTemplate 即可。

假如有一个实体类,我们定义如下:

  1. @Setting(settingPath = "es/es-setting.json")
  2. @Document(indexName = "book")
  3. public class Book {
  4. @Id
  5. private String id;
  6. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ue-ngram")
  7. private String author;
  8. @Field(type = FieldType.Text, searchAnalyzer = "ik_smart", analyzer = "ik_max_word")
  9. private String name;
  10. @Field(type = FieldType.Date, format = DateFormat.date)
  11. private LocalDate publishDate;
  12. @Field(type = FieldType.Date, format = DateFormat.date_time)
  13. private LocalDateTime publishDateTime;
  14. //get set toString 省略
  15. }
  • @Setting表示的是ES的配置信息,包括自定义的索引器、分片等信息。不知道如何定义的可以参考文章:自定义分词器案例
  • @Documen中 indexName 是索引的名称
  • @Filed中的信息重点讲解searchAnalyzer和analyzer
    • searchAnalyzer 表示的是查询时候该单词如何分词
    • analyzer 表示的是索引(保存)的时候如何分词
  • @Filed中的type表示的是该字段类型,重点强调下时间类型

    • 使用注解type = FieldType.Date
    • 如果是 LocalDate 那么format=DateFormat.date
    • 如果是 LocalDateTime ,使用format = DateFormat.date_time

      JPA是什么

      其作用有主要有 2 个
  • 内置 通用的 CRUD 进行简单的基础操作

  • 通过方法命名的形式让框架自动生成操作代码:参考链接

当然我个人认为其存在几个缺点

  • 通过方法命名进行扩展的做法可能导致部分情况方法名太长了
  • 不太符合中文分词的搜索,比如你要查包含不能 Contains ,而应该使用Eqauls【后面会谈到】

    内置 API

    image.png
    关键接口是:CrudRepositoryPagingAndSortingRepository

  • CrudRepository集成了基本的 CRUD,如下图所示

image.png

  • PagingAndSortingRepositoryCrudRepository的基础上定义了 分页和排序 操作。

使用的时候,可以直接使用带分页的PagingAndSortingRepository

  1. public interface BookEsMapper extends PagingAndSortingRepository<Book, String> {
  2. }

比如定义了上述接口,并且在配置类获取启动类上添加了注解@EnableElasticsearchRepositories(basePackages = "club.hicode.dockerhi")
那么就可以使用了,下面给出一个插入的代码

  1. @Test
  2. public void testBookSave() {
  3. Book book = new Book();
  4. book.setName("Kotlin开发手册");
  5. book.setAuthor("文在寅");
  6. book.setId("012");
  7. book.setPublishDate(LocalDate.now());
  8. book.setPublishDateTime(LocalDateTime.now());
  9. Book result = bookEsMapper.save(book);
  10. System.out.println(result);
  11. }
  12. @Test
  13. public void testBaseApi() {
  14. Optional<Book> optBook = bookEsMapper.findById("012");
  15. optBook.ifPresent(System.out::println);
  16. Iterable<Book> all = bookEsMapper.findAll();
  17. all.forEach(System.out::println);
  18. bookEsMapper.deleteById("012");
  19. System.out.println("删除 ok");
  20. }

执行成功后,通过 DevTools 的指令 GET /book 可以查看到如下信息

  1. {
  2. "book" : {
  3. "aliases" : { },
  4. "mappings" : {
  5. "properties" : {
  6. "_class" : {
  7. "type" : "text",
  8. "fields" : {
  9. "keyword" : {
  10. "type" : "keyword",
  11. "ignore_above" : 256
  12. }
  13. }
  14. },
  15. "author" : {
  16. "type" : "text",
  17. "analyzer" : "ue-ngram"
  18. },
  19. "id" : {
  20. "type" : "text",
  21. "fields" : {
  22. "keyword" : {
  23. "type" : "keyword",
  24. "ignore_above" : 256
  25. }
  26. }
  27. },
  28. "name" : {
  29. "type" : "text",
  30. "analyzer" : "ik_max_word",
  31. "search_analyzer" : "ik_smart"
  32. },
  33. "publishDate" : {
  34. "type" : "date",
  35. "format" : "date"
  36. },
  37. "publishDateTime" : {
  38. "type" : "date",
  39. "format" : "date_time"
  40. }
  41. }
  42. },
  43. "settings" : {
  44. "index" : {
  45. "routing" : {
  46. "allocation" : {
  47. "include" : {
  48. "_tier_preference" : "data_content"
  49. }
  50. }
  51. },
  52. "number_of_shards" : "1",
  53. "provided_name" : "book",
  54. "creation_date" : "1611655951204",
  55. "analysis" : {
  56. "filter" : {
  57. "my_pinyin" : {
  58. "keep_joined_full_pinyin" : "true",
  59. "lowercase" : "true",
  60. "none_chinese_pinyin_tokenize" : "false",
  61. "keep_original" : "true",
  62. "keep_none_chinese_together" : "true",
  63. "keep_first_letter" : "true",
  64. "trim_whitespace" : "true",
  65. "type" : "pinyin",
  66. "keep_none_chinese" : "true",
  67. "keep_full_pinyin" : "false"
  68. }
  69. },
  70. "char_filter" : {
  71. "ue_char_filter" : {
  72. "type" : "mapping",
  73. "mappings" : [
  74. "- => ,",
  75. "— => ,"
  76. ]
  77. }
  78. },
  79. "analyzer" : {
  80. "ue_ik_pinyin_analyzer" : {
  81. "filter" : [
  82. "my_pinyin"
  83. ],
  84. "char_filter" : [
  85. "html_strip",
  86. "ue_char_filter"
  87. ],
  88. "type" : "custom",
  89. "tokenizer" : "ik_max_word"
  90. },
  91. "ue-ngram" : {
  92. "type" : "custom",
  93. "char_filter" : [
  94. "html_strip",
  95. "ue_char_filter"
  96. ],
  97. "tokenizer" : "ngram_tokenizer"
  98. }
  99. },
  100. "tokenizer" : {
  101. "ngram_tokenizer" : {
  102. "token_chars" : [
  103. "letter",
  104. "digit"
  105. ],
  106. "min_gram" : "2",
  107. "type" : "ngram",
  108. "max_gram" : "3"
  109. }
  110. }
  111. },
  112. "number_of_replicas" : "1",
  113. "uuid" : "ipVtuyH6Qsy422WWQ39gQw",
  114. "version" : {
  115. "created" : "7100199"
  116. }
  117. }
  118. }
  119. }
  120. }

描述下 settings 的内容就是上文代码中@Setting(settingPath = "es/es-setting.json")设置中的内容。

扩展 API

下面给出实例代码

  1. public interface BookEsMapper extends PagingAndSortingRepository<Book, String> {
  2. List<Book> findByAuthorContains(String author);
  3. List<Book> findByAuthorEquals(String author);
  4. List<Book> findByNameContaining(String name);
  5. List<Book> findByNameEquals(String name);
  6. }

当我们调用这些代码的时候,为了更方便的进行调试和展示 Query语句,建议在配置文件application.properties添加

  1. logging.level.tracer=TRACE

个人不建议使用 在中文语义下使用 JPA 进行Text的查询,因为这其实挺让人迷糊的,我在学习的时候踩了不少坑。
举个例子:
Book类中定义了 name字段的索引分词是 ik_max_word,检索分词用的是 ik_smart
对于kotlin 开发手册,索引的时候分词为:kotlin/开发/手册
但是如果使用findByNameContaining("发手")的时候,虽然在语义上是发手是包含于开发手册,但是因为分词的缘故,检索不到。
综上,个人建议 JPA检索上的使用更应该是基于 Term的查询和英文检索。新增、修改、删除是没有问题的。

总结

本文主要是 2 个点:

  • 实体类构建:关键注解、Setting 设置
  • JPA的基本 API和简单 API,更重要的是用于检索的时候,因为 API语义问题,可能给初学者造成误解。