spring boot 2.X集成ES 进行CRUD操作  完整版

内容包括:

=========================================================================================

1.CRUD:单字段查询、复合查询、分页查询、评分查询√

2.时间范围查询√

3.GET方法传入时间类型解析不了的问题√

4.term和match查询的区别√

5.filter+query查询的区别√

6.自定义ES的mapping,自定义settings√

7.解决@Field注解 设置分词器无效的问题、解决@Document注解 设置分区 以及备份无效的问题√

8.pinyin查询以及繁简体转化查询的集成√

9.同一个字段设置多种分词器的解决方案√

10.不同分词器的区别。读时分词和写时分词√

11.索引数据迁移

12.keyword与text类型区别以及引出的相关问题√

13.index创建的索引状态为yellow以及启动集群后对于index状态、分片、备份的影响

=======================================================================================

要求:

spring boot 2.0.1

elasticsearch 6.5.4

spring-boot-starter-data-elasticsearch

es中要求已经安装了ik分词器、pingyin分词器、繁简体转化分词器[安装步骤]

=======================================================================================

注明:

下文中红色字体部分,即为集成过程中解决的问题。

=======================================================================================

集成项目结构:

===================================================================================

正文

一、spring boot 集成ES基本操作的步骤

1.pom.xml引入jar包

  1. <!-- spring-boot-starter-data-elasticsearch -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  5. </dependency>

2.ES连接信息,配置在application.properties中

  1. #elasticsearch相关配置
  2. #es的cluster集群名称可以查看服务器安装的集群名称 curl http://192.168.92.130:9200 获取到集群名称
  3. spring.data.elasticsearch.cluster-name=docker-cluster
  4. #注意端口为9300 9300 是 Java 客户端的端口,支持集群之间的通信。9200 是支持 Restful HTTP 的接口
  5. spring.data.elasticsearch.cluster-nodes=192.168.92.130:9300

3.测试实体Builder

  1. package com.sxd.swapping.domain;
  2.  
  3. import com.fasterxml.jackson.annotation.JsonFormat;
  4. import lombok.*;
  5. import org.springframework.data.elasticsearch.annotations.*;
  6.  
  7. import javax.persistence.Id;
  8. import java.util.Date;
  9.  
  10. /**
  11. * es的index的settings 和 mapping 设置,是最初的第一次设置。后续即使更改,也不起作用。
  12. * 但是mapping中的属性名称以及属性个数如果更改了,会更新到ES中。这样会导致数据的丢失。需要注意。
  13. */
  14.  
  15. @Setter
  16. @Getter
  17.  
  18. //ES的三个注解
  19. //指定index索引名称为项目名 指定type类型名称为实体名
  20. @Document(indexName = "swapping",type = "builder")
  21. //相当于ES中的mapping 注意对比文件中的json和原生json 最外层的key是没有的
  22. @Mapping(mappingPath = "/esConfig/builder-mapping.json")
  23. //相当于ES中的settings 注意对比文件中的json和原生json 最外层的key是没有的
  24. @Setting(settingPath = "/esConfig/builder-setting.json")
  25. public class Builder {
  26.  
  27. //id 测试长整型数据 注意与es中索引本身id区分开
  28. @Id
  29. private Long id;
  30.  
  31. //在创建初始化索引开始 就要去查看mapping是否ik分词创建成功 否则 需要进行索引数据的迁移操作
  32.  
  33. //指定查询分词器 为ik分词器 存储分词器为 ik分词器
  34. //在@Field中指定的ik分词器没起作用,因此采用上面的两个注解 可以完全自定义类型Field的各个属性
  35. //@Field(searchAnalyzer = "ik_max_word",analyzer = "ik_max_word")
  36.  
  37. //类型定义为text 可测试ik分词 繁简体转化 pinyin分词 查询效果
  38. //名称 测试字符串类型
  39. private String buildName;
  40.  
  41. //类型定义为text 可测试大文本
  42. private String remark;
  43.  
  44. //类型定义为keyword 可测试是否分词 以及查询效果
  45. private String email;
  46.  
  47. //数量 测试整型数据
  48. private int buildNum;
  49.  
  50. //时间也可以进行范围查询,但是查询传入参数,应该为mapping中定义的时间字段的 格式化字符串 或 时间戳 否则,ES无法解析格式会报错
  51. //时间 测试时间类型
  52. private Date buildDate;
  53.  
  54. //积分比率 测试浮点型数据
  55. private Double integral;
  56.  
  57. //分页大小
  58. private Integer pageNum = 0;
  59. //分页数量
  60. private Integer pageSize = 10;
  61.  
  62. }

注释1:

  @Document注解,注明index名字是 swapping 项目名;type名字是 builder 实体名。【都是可以自定义的,如果可以,在settings中设置也是可以的】

注释2:

  @Document @Field注解详解

注释3:

  自定义mapping

  @Field注解中指定分词器无效的问题,是通过设置自定义mapping解决的。

  同样,自定义mapping也解决了同一个字段指定多种分词器的问题

注释4:

  自定义settings

  自定义settings的设置目的是为了,创建分词器规则以及对于index的自定义设置。因为@Document注解中设置分区和备份 可能无效的问题。通过自定义setting也可以解决。

注释5:

  对于实体的mapping和settings的设置,在初始化启动的第一次,就创建成功了。

  之后即使程序中自定义的mapping更改了,对于ES中index的mapping的设置也不会发生改动。

  这也就意味着,如果在第一次创建index的时候,如果属性类型指定错误,或者分词器未设置,或者@Field中设置ik分词器无效等这些问题。那这些问题就一直存在,因为ES中的index的mapping在创建成功后就不能更改了。

  因此,如果需要解决上述的这些问题,

  要么就是在初次创建的时候,就使用[注释3]中的方式,自定义mapping。

  要么就是在出现问题之后,使用 elasticsearch 提供的 reindex api 来迁移数据,创建新的索引。这样可以实现不影响线上的访问,需要无缝切换到新的索引上。

  

4.自定义index的settings

  1. {
  2. "index": {
  3. "number_of_shards": "2",
  4. "number_of_replicas": "0",
  5. "analysis": {
  6.  
  7. "filter": {
  8. "edge_ngram_filter": {
  9. "type": "edge_ngram",
  10. "min_gram": 1,
  11. "max_gram": 50
  12. },
  13. "pinyin_simple_filter": {
  14. "type": "pinyin",
  15. "first_letter": "prefix",
  16. "padding_char": " ",
  17. "limit_first_letter_length": 50,
  18. "lowercase": true
  19. }
  20. },
  21.  
  22. "char_filter": {
  23. "tsconvert": {
  24. "type": "stconvert",
  25. "convert_type": "t2s"
  26. }
  27. },
  28.  
  29. "analyzer": {
  30. "ikSearchAnalyzer": {
  31. "type": "custom",
  32. "tokenizer": "ik_max_word",
  33. "char_filter": [
  34. "tsconvert"
  35. ]
  36. },
  37. "pinyinSimpleIndexAnalyzer": {
  38. "tokenizer": "keyword",
  39. "filter": [
  40. "pinyin_simple_filter",
  41. "edge_ngram_filter",
  42. "lowercase"
  43. ]
  44. }
  45. }
  46.  
  47. }
  48. }
  49. }

注释1:

  对比原生的settings,可以发现最外层的key是没有的。这里附上一份原生的settings,只做参考

  1. {
  2. "settings": {
  3. "index": {
  4. "refresh_interval": "1s",
  5. "number_of_shards": "5",
  6. "provided_name": "swapping",
  7. "creation_date": "1550472518470",
  8. "store": {
  9. "type": "fs"
  10. },
  11. "number_of_replicas": "1",
  12. "uuid": "e0UG1DH8RLG9_UYOzijSvw",
  13. "version": {
  14. "created": "6050499"
  15. }
  16. }
  17. },
  18. "defaults": {
  19. "index": {
  20. "max_inner_result_window": "100",
  21. "unassigned": {
  22. "node_left": {
  23. "delayed_timeout": "1m"
  24. }
  25. },
  26. "max_terms_count": "65536",
  27. "routing_partition_size": "1",
  28. "max_docvalue_fields_search": "100",
  29. "merge": {
  30. "scheduler": {
  31. "max_thread_count": "1",
  32. "auto_throttle": "true",
  33. "max_merge_count": "6"
  34. },
  35. "policy": {
  36. "reclaim_deletes_weight": "2.0",
  37. "floor_segment": "2mb",
  38. "max_merge_at_once_explicit": "30",
  39. "max_merge_at_once": "10",
  40. "max_merged_segment": "5gb",
  41. "expunge_deletes_allowed": "10.0",
  42. "segments_per_tier": "10.0",
  43. "deletes_pct_allowed": "33.0"
  44. }
  45. },
  46. "max_refresh_listeners": "1000",
  47. "max_regex_length": "1000",
  48. "load_fixed_bitset_filters_eagerly": "true",
  49. "number_of_routing_shards": "5",
  50. "write": {
  51. "wait_for_active_shards": "1"
  52. },
  53. "mapping": {
  54. "coerce": "false",
  55. "nested_fields": {
  56. "limit": "50"
  57. },
  58. "depth": {
  59. "limit": "20"
  60. },
  61. "ignore_malformed": "false",
  62. "total_fields": {
  63. "limit": "1000"
  64. }
  65. },
  66. "source_only": "false",
  67. "soft_deletes": {
  68. "enabled": "false",
  69. "retention": {
  70. "operations": "0"
  71. }
  72. },
  73. "max_script_fields": "32",
  74. "query": {
  75. "default_field": [
  76. "*"
  77. ],
  78. "parse": {
  79. "allow_unmapped_fields": "true"
  80. }
  81. },
  82. "format": "0",
  83. "sort": {
  84. "missing": [],
  85. "mode": [],
  86. "field": [],
  87. "order": []
  88. },
  89. "priority": "1",
  90. "codec": "default",
  91. "max_rescore_window": "10000",
  92. "max_adjacency_matrix_filters": "100",
  93. "gc_deletes": "60s",
  94. "optimize_auto_generated_id": "true",
  95. "max_ngram_diff": "1",
  96. "translog": {
  97. "generation_threshold_size": "64mb",
  98. "flush_threshold_size": "512mb",
  99. "sync_interval": "5s",
  100. "retention": {
  101. "size": "512mb",
  102. "age": "12h"
  103. },
  104. "durability": "REQUEST"
  105. },
  106. "auto_expand_replicas": "false",
  107. "mapper": {
  108. "dynamic": "true"
  109. },
  110. "requests": {
  111. "cache": {
  112. "enable": "true"
  113. }
  114. },
  115. "data_path": "",
  116. "highlight": {
  117. "max_analyzed_offset": "-1"
  118. },
  119. "routing": {
  120. "rebalance": {
  121. "enable": "all"
  122. },
  123. "allocation": {
  124. "enable": "all",
  125. "total_shards_per_node": "-1"
  126. }
  127. },
  128. "search": {
  129. "slowlog": {
  130. "level": "TRACE",
  131. "threshold": {
  132. "fetch": {
  133. "warn": "-1",
  134. "trace": "-1",
  135. "debug": "-1",
  136. "info": "-1"
  137. },
  138. "query": {
  139. "warn": "-1",
  140. "trace": "-1",
  141. "debug": "-1",
  142. "info": "-1"
  143. }
  144. }
  145. },
  146. "throttled": "false"
  147. },
  148. "fielddata": {
  149. "cache": "node"
  150. },
  151. "default_pipeline": "_none",
  152. "max_slices_per_scroll": "1024",
  153. "shard": {
  154. "check_on_startup": "false"
  155. },
  156. "xpack": {
  157. "watcher": {
  158. "template": {
  159. "version": ""
  160. }
  161. },
  162. "version": "",
  163. "ccr": {
  164. "following_index": "false"
  165. }
  166. },
  167. "percolator": {
  168. "map_unmapped_fields_as_text": "false",
  169. "map_unmapped_fields_as_string": "false"
  170. },
  171. "allocation": {
  172. "max_retries": "5"
  173. },
  174. "indexing": {
  175. "slowlog": {
  176. "reformat": "true",
  177. "threshold": {
  178. "index": {
  179. "warn": "-1",
  180. "trace": "-1",
  181. "debug": "-1",
  182. "info": "-1"
  183. }
  184. },
  185. "source": "1000",
  186. "level": "TRACE"
  187. }
  188. },
  189. "compound_format": "0.1",
  190. "blocks": {
  191. "metadata": "false",
  192. "read": "false",
  193. "read_only_allow_delete": "false",
  194. "read_only": "false",
  195. "write": "false"
  196. },
  197. "max_result_window": "10000",
  198. "store": {
  199. "stats_refresh_interval": "10s",
  200. "fs": {
  201. "fs_lock": "native"
  202. },
  203. "preload": []
  204. },
  205. "queries": {
  206. "cache": {
  207. "enabled": "true"
  208. }
  209. },
  210. "ttl": {
  211. "disable_purge": "false"
  212. },
  213. "warmer": {
  214. "enabled": "true"
  215. },
  216. "max_shingle_diff": "3",
  217. "query_string": {
  218. "lenient": "false"
  219. }
  220. }
  221. }
  222. }

注释2:

  自定义settings中,设置分片是2,备份是0。如果是备份设置为1,则代表每个分片都有一个备份,则总共是4分。

  因为ES只启动了一个node节点,所以会导致index的状态为yellow。因为一个节点,而分片又要创建备份的缘故,会导致备份创建无效。最后的index的state状态会显示为total=4,而success=2。虽然这样并不影响使用。

  因此建议ES启动为多个nodes节点,启动为集群。

  关于index设置分区和备份数量分别为多少,需要慎重!

注释3:

  自定义setting中,JSON作用是创建两个分析器名为ikSearchAnalyzer,pinyinSimpleIndexAnalyzer,前者使用ik中文分词器繁体转简体char_filter过滤,使得引用此分词器的字段在设置时,将会自动对中文进行分词和繁简体转换。

  pinyinSimpleIndexAnalyzer 使用pinyin分词器,并进行edge_ngram 过滤,大写转小写过滤。

注释4:

  通过自定义setting,实现了对同一字段设置多种分词器

注释5:

  关于ES的内置分词器,可以详细看看。

注释6:

  ES的分词器,其实就是插件,是工具。而对于分词的使用,其实可以分为读时分词写时分词

  读时分词,发生在用户查询时,ES 会即时地对用户输入的关键词进行分词,分词结果只存在内存中,当查询结束时,分词结果也会随即消失。

  写时分词,发生在文档写入时,ES 会对文档进行分词后,将结果存入倒排索引,该部分最终会以文件的形式存储于磁盘上,不会因查询结束或者 ES 重启而丢失。

  看到这里,其实就明白了。写时分词,是在自定义mapping中指定的,而且一经指定就不能再修改,若要修改必须新建索引。

  所以,查询的时候,我们可以自定义按照哪种想要的分词效果进行查询。没有指定,就是mapping中指定的查询分词。

  写的时候就是按照mapping中指定的分词进行存储,如果没有指定,则按照ES默认的分词器进行分词存储!

注释7:

  analyzer中设置的自定义的分词器的名字在mapping中会被引用

5.自定义Index的mapping

  1. {
  2. "builder": {
  3. "properties": {
  4. "id": {
  5. "type": "long"
  6. },
  7. "buildName": {
  8. "type": "text",
  9. "analyzer": "ikSearchAnalyzer",
  10. "search_analyzer": "ikSearchAnalyzer",
  11. "fields": {
  12. "pinyin": {
  13. "type": "text",
  14. "analyzer": "pinyinSimpleIndexAnalyzer",
  15. "search_analyzer": "pinyinSimpleIndexAnalyzer"
  16. }
  17. }
  18. },
  19. "remark": {
  20. "type": "text",
  21. "analyzer": "ikSearchAnalyzer",
  22. "search_analyzer": "ikSearchAnalyzer",
  23. "fields": {
  24. "pinyin": {
  25. "type": "text",
  26. "analyzer": "pinyinSimpleIndexAnalyzer",
  27. "search_analyzer": "pinyinSimpleIndexAnalyzer"
  28. }
  29. }
  30. },
  31. "email": {
  32. "type": "keyword",
  33. "ignore_above": 50
  34. },
  35. "buildNum": {
  36. "type": "long"
  37. },
  38. "buildDate": {
  39. "type": "date",
  40. "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
  41. },
  42. "integral": {
  43. "type": "float"
  44. },
  45. "pageNum": {
  46. "type": "long"
  47. },
  48. "pageSize": {
  49. "type": "long"
  50. },
  51. "query": {
  52. "properties": {
  53. "match_all": {
  54. "type": "object"
  55. }
  56. }
  57. }
  58. }
  59. }
  60. }

注释1:

  mapping中只要是对实体的各个属性对应的类型,以及分词器进行指定。

注释2:

  尤其是时间字段,类型需要设置为date,时间格式需要设置为文件中指定的。

  时间类型设置为date的目的,是对时间进行范围查询是可操作的。如果类型设置为text类型,则时间范围查询就无法实现。

  时间格式的指定,是需要解析ES对接java程序,进行查询时候,传入参数可以是"yyyy-MM-dd"的时间字符串,也可以是时间戳。

  需要注意的是:时间字段在ES中存储是时间戳,因此想要进行精准的时间查询以及时间范围查询,其实可以通过传入时间戳进行查询。后面的controller中有具体的方法。

  如果ES查询时间报错:Caused by: ElasticsearchParseException[failed to parse date field [Sun Dec 31 16:00:00 UTC 2017] with format [yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis]];

注释3:

  这里需要注意的是字符串类型的两种数据类型text和keyword

  text类型:支持分词、全文检索,不支持聚合、排序操作。适合大字段存储,如:文章详情、content字段等.

  keyword类型:支持精确匹配,支持聚合、排序操作。适合精准字段匹配,如:url、name、email、title等字段.

  

  keyword支持的最大长度为32766个UTF-8字符,且如果超过了ignore_above设置的字符串最大长度后,数据将不会被索引,无法通过term精确匹配查询.

  text则不受长度限制

  本点相关联的问题:

  问题1:设置为keyword类型的字段,插入很长的大段内容后,报字符超出异常,无法插入。

  问题2:检索超过ignore_above设定长度的字段后,无法返回结果

注释4:

  在自定义mapping中实现了对同一字段设置多个分词器

注释5:

  对于上面字段中fields的设置,例如 pinyin,是自定义的,会在controller中查询时候,指定按照mapping中设置好的分词器查询时候,用到。

  1. QueryBuilder ikSTQuery = QueryBuilders.matchQuery("buildName",pinyinStr).boost(1f);
  2. QueryBuilder pinyinQuery = QueryBuilders.matchQuery("buildName.pinyin",pinyinStr);

  当然,除了可以用mapping中预先设定好的强大的分词器之外,也可以自己指定分词器进行查询。[前提是你的ES中默认有或者你自己安装了的分词器]

  1. QueryBuilder matchBuilder = QueryBuilders.matchQuery( "buildName" ,str).analyzer("ik_max_word");

  这里的分词器,可以参考4中注释5的 ES中默认的分词器 进行赋值。甚至更多。

注释6:

  这里的分词器名称,采用自定义settings中预先设置的分词器名称。

6.继承ElasticsearchRepository的BuilderDao

  1. package com.sxd.swapping.esDao;
  2.  
  3. import com.sxd.swapping.domain.Builder;
  4. import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
  5.  
  6. public interface BuilderDao extends ElasticsearchRepository<Builder,Long>{
  7.  
  8. }

7.controller层CRUD

  1. package com.sxd.swapping.controller;
  2.  
  3. import com.sxd.swapping.base.UniVerResponse;
  4. import com.sxd.swapping.domain.Builder;
  5. import com.sxd.swapping.esDao.BuilderDao;
  6. import org.elasticsearch.index.query.*;
  7. import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.propertyeditors.CustomDateEditor;
  10. import org.springframework.data.domain.Page;
  11. import org.springframework.data.domain.PageRequest;
  12. import org.springframework.data.domain.Pageable;
  13. import org.springframework.data.domain.Sort;
  14. import org.springframework.data.elasticsearch.core.query.SearchQuery;
  15. import org.springframework.web.bind.WebDataBinder;
  16. import org.springframework.web.bind.annotation.*;
  17.  
  18. import java.text.SimpleDateFormat;
  19. import java.util.*;
  20.  
  21. /**
  22. * 使用方式有两种:
  23. * 1.一种是经过 SpringData 封装过的,直接在 dao 接口继承 ElasticsearchRepository 即可
  24. * 2.一种是经过 Spring 封装过的,直接在 Service/Controller 中引入该 bean 即可 ElasticsearchTemplate
  25. */
  26. @RestController
  27. @RequestMapping("/es")
  28. public class ESBuilderController {
  29.  
  30. @Autowired
  31. BuilderDao builderDao;
  32.  
  33. /**
  34. * 方式1
  35. *
  36. * 单个保存索引
  37. * @return
  38. */
  39. @RequestMapping(value = "/save", method = RequestMethod.POST)
  40. public UniVerResponse<Builder> save(@RequestBody Builder builder){
  41. builder = builder == null ? new Builder() : builder;
  42. UniVerResponse<Builder> res = new UniVerResponse<>();
  43. Builder builder2 = builderDao.save(builder);
  44. res.beTrue(builder2);
  45. return res;
  46. }
  47.  
  48. /**
  49. * 方式1
  50. *
  51. * 根据ID获取单个索引
  52. * @param id
  53. * @return
  54. */
  55. @RequestMapping(value = "/get", method = RequestMethod.GET)
  56. public UniVerResponse<Builder> get(Long id){
  57. UniVerResponse<Builder> res = new UniVerResponse<>();
  58. Optional<Builder> get = builderDao.findById(id);
  59. res.beTrue(get.isPresent() == false ? null : get.get());
  60. return res;
  61. }
  62.  
  63. /**
  64. * ============================单条件查询==================================
  65. */
  66.  
  67. /**
  68. * 方式1
  69. *
  70. * 通过match进行模糊查询
  71. * 根据传入属性值,检索指定属性下是否有匹配
  72. *
  73. * 例如:
  74. * name:中国人
  75. * 那么查询会将 中国人 进行分词, 中国 人 国人 等。之后再进行查询匹配
  76. *
  77. * @param name
  78. * @return
  79. */
  80. @RequestMapping(value = "/searchNameByMatch", method = RequestMethod.GET)
  81. public UniVerResponse<List<Builder>> searchNameByMatch(String name){
  82. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  83. MatchQueryBuilder matchBuilder = QueryBuilders.matchQuery("buildName",name);
  84. Iterable<Builder> search = builderDao.search(matchBuilder);
  85. Iterator<Builder> iterator = search.iterator();
  86. List<Builder> list = new ArrayList<>();
  87. while (iterator.hasNext()){
  88. list.add(iterator.next());
  89. }
  90.  
  91. res.beTrue(list);
  92. return res;
  93. }
  94.  
  95. /**
  96. * 方式1
  97. *
  98. * 通过term进行全量完全匹配查询
  99. * 根据传入属性值,检索指定属性下是否有属性值完全匹配的
  100. *
  101. * 例如:
  102. * name:中国人
  103. * 那么查询不会进行分词,就是按照 包含完整的 中国人 进行查询匹配
  104. *
  105. * 此时ik中文分词 并没有起作用【此时是在@Field注解 指定的ik分词器】
  106. * 例如存入 张卫健 三个字,以ik_max_word 分词存入,查询也指定以ik查询,但是 以张卫健 查询 没有结果
  107. * 以 【张】 或 【卫】 或 【健】 查询 才有结果,说明分词是以默认分词器 进行分词 ,也就是一个中文汉字 进行一个分词的效果。
  108. *
  109. *
  110. *
  111. * @param name
  112. * @return
  113. */
  114. @RequestMapping(value = "/searchNameByTerm", method = RequestMethod.GET)
  115. public UniVerResponse<List<Builder>> searchNameByTerm(String name){
  116. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  117. TermQueryBuilder termBuilder = QueryBuilders.termQuery("buildName",name);
  118. Iterable<Builder> search = builderDao.search(termBuilder);
  119. Iterator<Builder> iterator = search.iterator();
  120. List<Builder> list = new ArrayList<>();
  121. while (iterator.hasNext()){
  122. list.add(iterator.next());
  123. }
  124.  
  125. res.beTrue(list);
  126. return res;
  127. }
  128.  
  129. /**
  130. * 方式1
  131. *
  132. * 根据range进行范围查询
  133. *
  134. * 时间也可以进行范围查询,但时间传入值应该为yyyy-MM-dd HH:mm:ss 格式的时间字符串或时间戳 或其他定义的时间格式
  135. * 只有在mapping中定义的时间格式,才能被ES查询解析成功
  136. *
  137. * @param num
  138. * @return
  139. */
  140. @RequestMapping(value = "/searchNumByRange", method = RequestMethod.GET)
  141. public UniVerResponse<List<Builder>> searchNumByRange(Integer num){
  142. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  143. RangeQueryBuilder rangeBuilder = QueryBuilders.rangeQuery("buildNum").gt(0).lt(num);
  144. Iterable<Builder> search = builderDao.search(rangeBuilder);
  145. Iterator<Builder> iterator = search.iterator();
  146. List<Builder> list = new ArrayList<>();
  147. while (iterator.hasNext()){
  148. list.add(iterator.next());
  149. }
  150.  
  151. res.beTrue(list);
  152. return res;
  153. }
  154.  
  155. //处理GET请求的时间转化
  156. @InitBinder
  157. public void initBinder(WebDataBinder binder) {
  158. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  159. dateFormat.setLenient(false);
  160. binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
  161. }
  162.  
  163. /**
  164. * ============================复合条件查询==================================
  165. */
  166.  
  167. /**
  168. * 方式1
  169. *
  170. * 使用bool进行复合查询,使用filter比must query性能好
  171. *
  172. * filter是过滤,1.文档是否包含于结果 2.不涉及评分 3.更快
  173. * query是查询,1.文档是否匹配于结果 2.计算文档匹配评分 3.速度慢
  174. *
  175. *
  176. * @param builder
  177. * @return
  178. */
  179. @RequestMapping(value = "/searchByBool", method = RequestMethod.GET)
  180. public UniVerResponse<Page<Builder>> searchByBool(Builder builder){
  181. UniVerResponse<Page<Builder>> res = new UniVerResponse<>();
  182. BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
  183.  
  184. //多个字段匹配 属性值 must query
  185. MultiMatchQueryBuilder matchQueryBuilder =
  186. QueryBuilders.multiMatchQuery(builder.getBuildName(),"buildName","buildName2");
  187. boolBuilder.must(matchQueryBuilder);
  188.  
  189. //filter 分别过滤不同字段,缩小筛选范围
  190. TermQueryBuilder numQuery = QueryBuilders.termQuery("buildNum",builder.getBuildNum());
  191. boolBuilder.filter(numQuery);
  192.  
  193. RangeQueryBuilder dateQuery = QueryBuilders.rangeQuery("buildDate").lt(builder.getBuildDate().getTime());
  194. boolBuilder.filter(dateQuery);
  195.  
  196. //排序 + 分页
  197. Sort sort = Sort.by(Sort.Direction.DESC,"buildNum");
  198. PageRequest pageRequest = PageRequest.of(builder.getPageNum()-1,builder.getPageSize(),sort);
  199.  
  200. Page<Builder> search = builderDao.search(boolBuilder, pageRequest);
  201. res.beTrue(search);
  202. return res;
  203. }
  204.  
  205. /**
  206. * 方式1
  207. * 时间范围查询
  208. * ES中时间字段需要设置为 date类型,才能查询时间范围
  209. * 时间范围要想准确查询,需要将时间转化为时间戳进行查询
  210. *
  211. * ES中date字段存储是 时间戳存储
  212. *
  213. *
  214. * from[包含] - to[包含]
  215. * gt - lt
  216. * gte - lte
  217. *
  218. *
  219. *
  220. * @return
  221. */
  222. @RequestMapping(value = "/searchByTimeRange", method = RequestMethod.GET)
  223. public UniVerResponse<List<Builder>> searchByTimeRange(Builder builder){
  224. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  225.  
  226. QueryBuilder queryBuilder = QueryBuilders.rangeQuery("buildDate").from(builder.getBuildDate().getTime());
  227. Iterable<Builder> search = builderDao.search(queryBuilder);
  228. Iterator<Builder> iterator = search.iterator();
  229. List<Builder> list = new ArrayList<>();
  230. while (iterator.hasNext()){
  231. list.add(iterator.next());
  232. }
  233.  
  234. res.beTrue(list);
  235. return res;
  236. }
  237.  
  238. /**
  239. * 方式1
  240. *
  241. * 检索所有索引
  242. * @return
  243. */
  244. @RequestMapping(value = "/searchAll", method = RequestMethod.GET)
  245. public UniVerResponse<List<Builder>> searchAll(){
  246. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  247. QueryBuilder queryBuilder = QueryBuilders.boolQuery();
  248. Iterable<Builder> search = builderDao.search(queryBuilder);
  249. Iterator<Builder> iterator = search.iterator();
  250. List<Builder> list = new ArrayList<>();
  251. while (iterator.hasNext()){
  252. list.add(iterator.next());
  253. }
  254.  
  255. res.beTrue(list);
  256. return res;
  257. }
  258.  
  259. /**
  260. * 方式1
  261. *
  262. * 根据传入属性值 全文检索所有属性
  263. * 关于QueryStringQueryBuilder的使用,如果不指定分词器,那么查询的时候,会使用ES默认的分词器进行查询。
  264. * 结果就是 会查询出与查询内容丝毫不相干的结果。
  265. *
  266. *
  267. * 关于ES内置分词器:
  268. * https://blog.csdn.net/u013795975/article/details/81102010
  269. *
  270. *
  271. *
  272. * @return
  273. */
  274. @RequestMapping(value = "/findByStr", method = RequestMethod.GET)
  275. public UniVerResponse<List<Builder>> findByStr(String paramStr){
  276. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  277. QueryStringQueryBuilder qsqb = new QueryStringQueryBuilder(paramStr).analyzer("standard");
  278. Iterable<Builder> search = builderDao.search(qsqb);
  279. Iterator<Builder> iterator = search.iterator();
  280. List<Builder> list = new ArrayList<>();
  281. while (iterator.hasNext()){
  282. list.add(iterator.next());
  283. }
  284.  
  285. res.beTrue(list);
  286. return res;
  287.  
  288. }
  289.  
  290. /**
  291. * 方式1
  292. *
  293. * 选择用term或match方式查询
  294. * 查询字段buildName或者buildName2
  295. * 指定以分词器 ik_max_word 或 ik_smart 或 standard[es默认分词器] 或 english 或 whitespace 分词器进行分词查询
  296. *
  297. *
  298. *
  299. * @param analyzer 分词器
  300. * @param str 查询属性值
  301. * @param param 指定是参数1[buildName] 还是 参数2[remark]
  302. * @return
  303. */
  304. @RequestMapping(value = "/searchByIK", method = RequestMethod.GET)
  305. public UniVerResponse<List<Builder>> searchByIK(String analyzer,String str,Integer param){
  306. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  307. QueryBuilder matchBuilder = QueryBuilders.matchQuery(param ==1 ? "buildName" : "remark",str).analyzer(analyzer);
  308.  
  309. Iterable<Builder> search = builderDao.search(matchBuilder);
  310. Iterator<Builder> iterator = search.iterator();
  311. List<Builder> list = new ArrayList<>();
  312. while (iterator.hasNext()){
  313. list.add(iterator.next());
  314. }
  315.  
  316. res.beTrue(list);
  317. return res;
  318. }
  319.  
  320. /**
  321. * 方式1
  322. *
  323. * 繁简体转化查询、拼音查询,并且加入评分查询
  324. * 评分规则详情:https://blog.csdn.net/paditang/article/details/79098830
  325. * @param pinyinStr
  326. * @return
  327. */
  328. @RequestMapping(value = "/searchByPinYin",method = RequestMethod.GET)
  329. public UniVerResponse<List<Builder>> searchByPinYin(String pinyinStr){
  330. UniVerResponse<List<Builder>> res = new UniVerResponse<>();
  331. DisMaxQueryBuilder disMaxQuery = QueryBuilders.disMaxQuery();
  332. QueryBuilder ikSTQuery = QueryBuilders.matchQuery("buildName",pinyinStr).boost(1f);
  333. QueryBuilder pinyinQuery = QueryBuilders.matchQuery("buildName.pinyin",pinyinStr);
  334.  
  335. disMaxQuery.add(ikSTQuery);
  336. disMaxQuery.add(pinyinQuery);
  337.  
  338. Iterable<Builder> search = builderDao.search(disMaxQuery);
  339. Iterator<Builder> iterator = search.iterator();
  340. List<Builder> list = new ArrayList<>();
  341. while (iterator.hasNext()){
  342. list.add(iterator.next());
  343. }
  344.  
  345. res.beTrue(list);
  346. return res;
  347. }
  348.  
  349. /**
  350. *
  351. * @param builder
  352. * @return
  353. */
  354. @RequestMapping(value = "/delete", method = RequestMethod.POST)
  355. public UniVerResponse<Builder> delete(@RequestBody Builder builder){
  356. UniVerResponse<Builder> res = new UniVerResponse<>();
  357.  
  358. builderDao.deleteById(builder.getId());
  359. res.beTrue(builder);
  360. return res;
  361. }
  362.  
  363. }

注释1:

  注意GET请求接受时间转化

注释2:

  filter和query的区别 

  filter是过滤,1.文档是否匹配 2.不涉及评分 3.更快 4.会自动缓存,下次查询速度会更快
  query是查询,1.文档是否匹配查詢,相关度高不高 2.计算文档匹配评分 3.速度慢 4.查询的结果要比filter可能更多一些,因为涉及评分,所以更精确

注释3:

  term和match的区别

  1. * 通过match进行模糊查询
  2. * 根据传入属性值进行分词,检索指定属性下是否有匹配
  3. *
  4. * 例如:
  5. * name:中国人
  6. * 那么查询会将 中国人 进行分词, 中国 国人 等。之后再进行查询匹配
  1. * 通过term进行全量完全匹配查询
  2. * 根据传入属性值,检索指定属性下是否有属性值完全匹配的
  3. *
  4. * 例如:
  5. * name:中国人
  6. * 那么查询不会进行分词,就是按照 完整的 中国人 进行查询匹配

============================================================================

二、kibana管理index

1.Discover下查询

1.1输入上面操作的type名builder,可以看到ES中已经存入的document

1.2点击每一条Document左侧的三角,可以下拉查看document详情

2.Monitoring下管理

3.Management管理

=================================告一段落================================================

  

【ELK】4.spring boot 2.X集成ES spring-data-ES 进行CRUD操作 完整版+kibana管理ES的index操作的更多相关文章

  1. spring boot / cloud (三) 集成springfox-swagger2构建在线API文档

    spring boot / cloud (三) 集成springfox-swagger2构建在线API文档 前言 不能同步更新API文档会有什么问题? 理想情况下,为所开发的服务编写接口文档,能提高与 ...

  2. Spring Boot HikariCP 一 ——集成多数据源

    其实这里介绍的东西主要是参考的另外一篇文章,数据库读写分离的. 参考文章就把链接贴出来,里面有那位的代码,简单明了https://gitee.com/comven/dynamic-datasource ...

  3. Spring Boot 2.0(八):Spring Boot 集成 Memcached

    Memcached 介绍 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站 ...

  4. Spring Boot系列——如何集成Log4j2

    上篇<Spring Boot系列--日志配置>介绍了Spring Boot如何进行日志配置,日志系统用的是Spring Boot默认的LogBack. 事实上,除了使用默认的LogBack ...

  5. Spring Boot(十八):使用Spring Boot集成FastDFS

    Spring Boot(十八):使用Spring Boot集成FastDFS 环境:Spring Boot最新版本1.5.9.jdk使用1.8.tomcat8.0 功能:使用Spring Boot将文 ...

  6. Spring Boot之Swagger2集成

    一.Swagger2简单介绍 Swagger2,它可以轻松的整合到Spring Boot中,并与Spring MVC程序配合组织出强大RESTful API文档.它既可以减少我们创建文档的工作量,同时 ...

  7. Spring Boot 数据访问集成 MyBatis 与事物配置

    对于软件系统而言,持久化数据到数据库是至关重要的一部分.在 Java 领域,有很多的实现了数据持久化层的工具和框架(ORM).ORM 框架的本质是简化编程中操作数据库的繁琐性,比如可以根据对象生成 S ...

  8. 携程Apollo(阿波罗)配置中心在Spring Boot项目快速集成

    前提:先搭建好本地的单机运行项目:http://www.cnblogs.com/EasonJim/p/7643630.html 说明:下面的示例是基于Spring Boot搭建的,对于Spring项目 ...

  9. Spring Boot 2.X(六):Spring Boot 集成Redis

    Redis 简介 什么是 Redis Redis 是目前使用的非常广泛的免费开源内存数据库,是一个高性能的 key-value 数据库. Redis 与其他 key-value 缓存(如 Memcac ...

随机推荐

  1. 2013 ACM/ICPC 杭州网络赛C题

    题意:驴和老虎,在一个矩阵的两个格子里,有各自的起始方向.两者以相同的速度向前移动,前方不能走时驴总是向右,老虎总是向左.他们不能超出矩阵边界也不能走自己走过的格子(但可以走对方走过的格子).如果不能 ...

  2. http://blog.csdn.net/u011001723/article/details/45621027

    http://blog.csdn.net/u011001723/article/details/45621027 scp  + 脚本 config 外置 http://www.cnblogs.com/ ...

  3. 几个node项目实例-《转载》

    1. 词搜索 根据一个特效匹配模式搜索整个英语词典词.这个程序是一个相当实在的应用.有足够的不平常代码,帮助你学习NodeJS应用架构以及如何使用NodeJS做一些有用的平台. 它使用expressw ...

  4. leetcode 题集

    775. Global and Local Inversions 统计相邻元素组成的逆序对(local inversion)和全局逆序对的数量(global inversion) 思路:local i ...

  5. python学习之算法、自定义模块、系统标准模块(上)

    算法.自定义模块.系统标准模块(time .datetime .random .OS .sys .hashlib .json和pickle) 一:算法回顾: 冒泡算法,也叫冒泡排序,其特点如下: 1. ...

  6. 1.网站应用程序 - 《APS.NET本质论》

    1.1.HTTP协议 浏览器与WEB服务器的协议是应用层协议,当前遵循HTTP/1.1,HTTP协议是无状态的协议 客户机与服务器通过请求和响应完成一次会话(Session),每次会话中,双方发送的数 ...

  7. 微信小程序Http高级封装 es6 promise

    公司突然要开放微信小程序,持续蒙蔽的我还不知道小程序是个什么玩意. 于是上网查了一下,就开始着手开发..... 首先开发客户端的东西,都有个共同点,那就是  数据请求! 看了下小程序的请求方式大概和a ...

  8. STM32 串口通信使用奇偶校验

    STM32串口通信如果使用奇偶校验,需要设置数据位长度为9bit USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USAR ...

  9. streaming优化:spark.default.parallelism调整处理并行度

    官方是这么说的: Cluster resources can be under-utilized if the number of parallel tasks used in any stage o ...

  10. P1102 A-B数对

    P1102 A-B数对用map过掉,可以当高效的桶排用,map<long long,int>m;意思是m[long long]==int; #include<iostream> ...