好久没写博文了, 最近项目中使用到了ElaticSearch相关的一些内容, 刚好自己也来做个总结。
现在自己也只能算得上入门, 总结下自己在工作中使用Java操作ES的一些小经验吧。

本文总共分为三个部分:
一:ES相关基本概念及原理
二:ES使用场景介绍
三:使用Java进行ES的增删改查及代码讲解

一:ES相关基本概念:
ElasticSearch(简称ES)是一个基于Lucene构建的开源、分布式、RESTful的全文本搜索引擎。

不过,ElasticSearch却也不仅只是一个全文本搜索引擎,它还是一个分布式实时文档存储,其中每个field均是被索引的数据且可被搜索;也是一个带实时分析功能的分布式搜索引擎,并且能够扩展至数以百计的服务器存储及处理PB级的数据。

如前所述,ElasticSearch在底层利用Lucene完成其索引功能,因此其许多基本概念源于Lucene。
我们先说说ES的基本概念。

  • 索引 Index:对数据的逻辑存储(倒排索引),不存储原
    始值。
  • 类型 Type:对索引的逻辑分类,可以有⼀个或多个分类。
  • ⽂档 Document:基本数据单元,JSON。
  • 字段 Filed

关系型数据与ES对比:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields

这里再说下ES中很重要的概念--倒排索引。这同样也是solr,lucene中所使用的索引方式。

例如我们正常的索引:

当我们在关系型数据库中,都是有id索引的, 我们通过id去查value速度是很快的。
但是如果我们想查value中包含字母b的值呢?特别是数据量很大的时候, 这种以id为索引的方式是不是就不适合了?
那么这里就适合使用倒排索引了:

这里将value进行分词, 然后将分词结果拿出来当做索引
跟正向的索引比较,也就是做了一个倒置,这就是倒排索引的思想

二,ES使用场景介绍
1、全文搜索(搜索引擎)
在一组文档中查找某一单词所在文档及位置
2、模糊匹配
通过用户的输入去匹配词库中符合条件的词条
3、商品搜索
通过商品的关键字去数据源中查找符合条件的商品

在我自己的项目中使用的情况是我有上百万的文章需要被通过各种条件检索到, 所以这里就直接使用ES, 现在线上检索速度都是10ms之内返回。

下面看看ES数据在浏览器的展示形式以及可视化界面的搜索:

三:使用Java进行ES的增删改查及代码讲解
1, 使用ES进行增加和更新操作。

  1. //首先在项目启动的时候生成esClient, 这个我们公司自己封装好了的。
  2. @PostConstruct
  3. public void init() {
  4. esClient = new ESClient<>(esConfig.getAddress(), esConfig.getCluster(), esConfig.getIndex(),
  5. esConfig.getUsername(), esConfig.getPassword(), ES_TYPE_MIXEDDATA, EsMixedDataDto.class);
  6. }

上面EsMixedDataDto是自己构建的一个类, ES中保存的字段就是这个类中的所有字段。
接着是增加和更新操作了:

  1. //同步到ES中
  2. articleEsService.upsertDocument(esMixedDataDto);
  3. /**
  4. * 创建或更新索引
  5. *
  6. * @param esMixedDataDto
  7. * @return
  8. */
  9. public boolean upsertDocument(EsMixedDataDto esMixedDataDto) {
  10. return esClient.upsertDocument(esMixedDataDto.getMixId(), esMixedDataDto);
  11. }

这个还是调用了系统封装好的esClient中的insertOrUpdate方法,最后我会把ESClient中所有封装的方法都贴出来, 其内部就是调用了ES原生的insert或者update方法的。

2,使用ES进行删除操作

  1. /**
  2. * 删除索引
  3. */
  4. public void deleteIndex(String id) throws Exception{
  5. esClient.deleteDocument(id);
  6. }

同上,也是使用了esClient中的delete方法,后面我会贴上esClient中所有方法。

3,使用ES进行查询
3.1 当然ES最重要的还是多维度的查询, 这里也是我要讲的重点。
首先来个最简单的搜索一篇文章的标题:

  1. //通过关键词来查询文章集合
  2. public PageResponse<EsMixedDataDto> queryForKeyword(String searchText, boolean highlight, PageRequest pageRequesto) {
  3. SearchRequestBuilder searchRequestBuilder = esClient.prepareSearch()
  4. .setTypes(ES_TYPE_MIXEDDATA)
  5. .setSearchType(SearchType.QUERY_THEN_FETCH)
  6. .setQuery(QueryBuilders.multiMatchQuery(searchText, "title").type(MultiMatchQueryBuilder.Type.BEST_FIELDS))
  7. .setFrom(pageRequest.getOffset())
  8. .setSize(pageRequest.getLimit())
  9. .setExplain(false)
  10. ;
  11. if (highlight) {
  12. searchRequestBuilder.addHighlightedField("title", 100, 1)
  13. .setHighlighterPreTags("<font color='red'>")
  14. .setHighlighterPostTags("</font>");
  15. }
  16. try {
  17. //这里就是给es发送搜索指令了
  18. return getMixedData(searchRequestBuilder);
  19. } catch (Exception e) {
  20. log.error("ES搜索异常!", e.getMessage());
  21. throw new RuntimeException(e);
  22. }
  23. }

这里先说说search_type, 也就是上面setSearchType(SearchType.QUERY_THEN_FETCH)的内容:

  • query_then_fetch:执⾏查询得到对⽂档进⾏排序的所需信息(在所
    有分⽚上执⾏),然后在相关分⽚上查询⽂档实际内容。返回结果的
    最⼤数量等于size参数的值。
  • query_and_fetch:查询在所有分⽚上并⾏执⾏,所有分⽚返回等于
    size值的结果数,最终返回结果的最⼤数量等于size的值乘以分⽚
    数。分⽚较多时会消耗过多资源。
  • count:只返回匹配查询的⽂档数量。
  • scan:⼀般在需要返回⼤量结果时使⽤。在发送第⼀次请求后,ES
    会返回⼀个滚动标识符,类似于数据库中的游标。

我这里使用的是query_then_fetch。

3.2 紧接着说个多条件复杂的查询:

  1. /**
  2. * @param jiaxiaoId: 驾校id
  3. * @param title 文章的title关键词
  4. * @param publishStatus 发布状态
  5. * @param stickStatus 置顶状态
  6. * @param pageRequest 请求的页码和条数
  7. * @param highlight 搜索结果是否高亮显示
  8. */
  9. public PageResponse<EsMixedDataDto> queryByConditions(Long jiaxiaoId, String title, PageRequest pageRequest, int publishStatus, int stickStatus, boolean highlight) {
  10. BoolQueryBuilder booleanQueryBuilder = QueryBuilders.boolQuery();
  11. booleanQueryBuilder.must(QueryBuilders.termQuery("jiaxiaoId", jiaxiaoId));
  12. if (StringUtils.isNotBlank(title)) {
  13. booleanQueryBuilder.must(QueryBuilders.multiMatchQuery(title, "title").type(MultiMatchQueryBuilder.Type.BEST_FIELDS));
  14. }
  15. //这里是添加是否发布的搜索条件, 默认是只展示已发布的文章
  16. if (publishStatus == CommonConstants.DataStatus.INIT_STATUS) {
  17. booleanQueryBuilder.must(QueryBuilders.termQuery("publishStatus", CommonConstants.DataStatus.INIT_STATUS));
  18. } else {
  19. booleanQueryBuilder.mustNot(QueryBuilders.termQuery("publishStatus", CommonConstants.DataStatus.INIT_STATUS));
  20. }
  21. //这里是添加是否置顶的搜索条件
  22. if (stickStatus == CommonConstants.DataStatus.PUBLISH_STATUS) {
  23. booleanQueryBuilder.must(QueryBuilders.termQuery("stickStatus", CommonConstants.DataStatus.PUBLISH_STATUS));
  24. } else if(stickStatus == CommonConstants.DataStatus.INIT_STATUS){
  25. booleanQueryBuilder.mustNot(QueryBuilders.termQuery("stickStatus", CommonConstants.DataStatus.PUBLISH_STATUS));
  26. }
  27. SearchRequestBuilder searchRequestBuilder = esClient.prepareSearch()
  28. .setTypes(ES_TYPE_MIXEDDATA)
  29. .setSearchType(SearchType.QUERY_THEN_FETCH)
  30. .setQuery(booleanQueryBuilder)
  31. .setFrom(pageRequest.getOffset())
  32. .setSize(pageRequest.getLimit())
  33. .addSort("stickStatus", SortOrder.DESC)
  34. .setExplain(false)
  35. ;
  36. if (jiaxiaoId == null) {
  37. BoolFilterBuilder filterBuilder = FilterBuilders.boolFilter()
  38. .must(FilterBuilders.missingFilter("jiaxiaoId"));
  39. searchRequestBuilder.setPostFilter(filterBuilder);
  40. }
  41. if (highlight) {
  42. searchRequestBuilder.addHighlightedField("title", 100, 1)
  43. .setHighlighterPreTags("<font color='red'>")
  44. .setHighlighterPostTags("</font>");
  45. } else {
  46. searchRequestBuilder.addSort("publishTime", SortOrder.DESC);
  47. }
  48. try {
  49. return getMixedData(searchRequestBuilder);
  50. } catch (Exception e) {
  51. log.error("ES搜索异常!", e.getMessage());
  52. throw new RuntimeException(e);
  53. }
  54. }

这里不用的就是使用query和filterBuilder,searchRequestBuilder中可以设置query和postFilter。
Debug到这里, 其实写的查询语句最终还是拼接成了一个ES可读的结构化查询语句:

3.3 最后贴上最重要的一个类ESClient.java, 这是我们针对于ElasticSearch封装的一个类。

  1. public class ESClient<T> {
  2. private static final Logger LOG = LoggerFactory.getLogger(ESClient.class);
  3. private static final String DEFAULT_ANALYZER = "ik_smart";
  4. private static final DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
  5. private final Client client;
  6. private String index;
  7. private Class<T> clazz;
  8. private String type;
  9. private BulkProcessor bulkProcessor;
  10. private List<String> serverHttpAddressList = Lists.newArrayList();
  11. private Map<String, JSONObject> sqlJsonMap = Maps.newHashMap();
  12. /**
  13. * 初始化一个连接ElasticSearch的客户端
  14. *
  15. * @param addresses ES服务器的Transport地址和端口的列表,多个服务器用逗号分隔,例如 localhost:9300,localhost:9300,...
  16. * @param clusterName 集群名称
  17. * @param index 索引名称,这里应该使用项目名称
  18. * @param username 用户名称
  19. * @param password 用户密码
  20. * @param type 索引类型
  21. * @param clazz 存储类
  22. */
  23. public ESClient(String addresses, String clusterName, String index,
  24. String username, String password, String type, Class<T> clazz) {
  25. if (StringUtils.isBlank(addresses)) {
  26. throw new RuntimeException("没有给定的ES服务器地址。");
  27. }
  28. this.index = index;
  29. this.type = type;
  30. this.clazz = clazz;
  31. // 获得链接地址对象列表
  32. List<InetSocketTransportAddress> addressList = Lists.transform(
  33. Splitter.on(",").trimResults().omitEmptyStrings().splitToList(addresses),
  34. new Function<String, InetSocketTransportAddress>() {
  35. @Override
  36. public InetSocketTransportAddress apply(String input) {
  37. String[] addressPort = input.split(":");
  38. String address = addressPort[0];
  39. Integer port = Integer.parseInt(addressPort[1]);
  40. serverHttpAddressList.add(address + ":" + 9200);
  41. return new InetSocketTransportAddress(address, port);
  42. }
  43. }
  44. );
  45. // 建立关于ES的配置
  46. ImmutableSettings.Builder builder = ImmutableSettings.settingsBuilder()
  47. .put("cluster.name", clusterName)
  48. .put("client.transport.sniff", false);
  49. if (StringUtils.isNotBlank(username)) {
  50. builder.put("shield.user", username + ":" + password);
  51. }
  52. Settings settings = builder.build();
  53. // 生成原生客户端
  54. TransportClient transportClient = new TransportClient(settings);
  55. for (InetSocketTransportAddress address : addressList) {
  56. transportClient.addTransportAddress(address);
  57. }
  58. client = transportClient;
  59. bulkProcessor = BulkProcessor.builder(
  60. client, new BulkProcessor.Listener() {
  61. @Override
  62. public void beforeBulk(long executionId, BulkRequest request) {
  63. }
  64. @Override
  65. public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
  66. }
  67. @Override
  68. public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
  69. throw new RuntimeException(failure);
  70. }
  71. }).build();
  72. }
  73. /**
  74. * 初始化连接ElasticSearch的客户端
  75. *
  76. * @param client 原生客户端
  77. * @param index 索引名称
  78. * @param type 类型
  79. * @param clazz 存储类
  80. */
  81. public ESClient(Client client, String index, String type, Class<T> clazz) {
  82. this.client = client;
  83. this.index = index;
  84. this.type = type;
  85. this.clazz = clazz;
  86. }
  87. /**
  88. * 向ES发送存储请求,将一个对象存储到服务器。
  89. *
  90. * @param id 该对象的id
  91. * @param t 存储实例
  92. * @return 是否存储成功
  93. */
  94. public boolean indexDocument(String id, T t) {
  95. return indexDocument(id, type, t);
  96. }
  97. /**
  98. * 向ES发送存储请求,将一个对象存储到服务器。
  99. *
  100. * @param t 存储实例
  101. * @return 返回存储之后在ES服务器内生成的随机ID
  102. */
  103. public String indexDocument(T t) {
  104. IndexResponse indexResponse = client.prepareIndex(index, type)
  105. .setSource(toJSONString(t))
  106. .execute()
  107. .actionGet();
  108. return indexResponse.getId();
  109. }
  110. /**
  111. * 向ES发送存储请求,将一个对象存储到服务器,这个方法允许用户手动指定该对象的存储类型名称
  112. *
  113. * @param id 对象id
  114. * @param type 存储类型
  115. * @param t 存储实例
  116. * @return 是否存储成功
  117. */
  118. public boolean indexDocument(String id, String type, T t) {
  119. IndexResponse indexResponse = client.prepareIndex(index, type, id)
  120. .setSource(toJSONString(t))
  121. .execute()
  122. .actionGet();
  123. return true;
  124. }
  125. /**
  126. * 向ES发送批量储存请求, 请求不会马上提交,而是会等待到达bulk设置的阈值后进行提交.<br/>
  127. * 最后客户端需要调用{@link #flushBulk()}方法.
  128. *
  129. * @param id 对象id
  130. * @param t 存储实例
  131. * @return 成功表示放入到bulk成功, 可能会抛出runtimeException
  132. */
  133. public boolean indexDocumentBulk(String id, T t) {
  134. return indexDocumentBulk(id, type, t);
  135. }
  136. /**
  137. * 向ES发送批量存储请求,将一个对象存储到服务器,这个方法允许用户手动指定该对象的存储类型名称
  138. *
  139. * @param id 对象id
  140. * @param type 存储类型
  141. * @param t 存储实例
  142. * @return 成功表示放入到bulk成功, 可能会抛出runtimeException
  143. * @see #indexDocument(String, Object)
  144. */
  145. public boolean indexDocumentBulk(String id, String type, T t) {
  146. IndexRequest indexRequest = new IndexRequest(index, type, id).source(toJSONString(t));
  147. bulkProcessor.add(indexRequest);
  148. return true;
  149. }
  150. /**
  151. * 向ES发送批量存储请求, 请求不会马上提交,而是会等待到达bulk设置的阈值后进行提交.<br/>
  152. * 最后客户端需要调用{@link #flushBulk()}方法.
  153. *
  154. * @param t 存储实例
  155. * @return 成功表示放入到bulk成功, 可能会抛出runtimeException
  156. */
  157. public boolean indexDocumentBulk(T t) {
  158. IndexRequest indexRequest = new IndexRequest(index, type).source(toJSONString(t));
  159. bulkProcessor.add(indexRequest);
  160. return true;
  161. }
  162. public boolean indexDocumentBulk(List<T> list) {
  163. for (T t : list) {
  164. indexDocumentBulk(t);
  165. }
  166. return true;
  167. }
  168. /**
  169. * 向ES发送批量存储请求, 允许传入一个Function, 用来从对象中获取ID.
  170. *
  171. * @param list 对象列表
  172. * @param idFunction 获取ID
  173. * @return 成功表示放入到bulk成功, 可能会抛出runtimeException
  174. */
  175. public boolean indexDocumentBulk(List<T> list, Function<T, String> idFunction) {
  176. for (T t : list) {
  177. indexDocumentBulk(idFunction.apply(t), t);
  178. }
  179. return true;
  180. }
  181. /**
  182. * 向ES发送更新文档请求,将一个对象更新到服务器,会替换原有对应ID的数据。
  183. *
  184. * @param id id
  185. * @param t 存储对象
  186. * @return 是否更新成功
  187. */
  188. public boolean updateDocument(String id, T t) {
  189. return updateDocument(id, type, t);
  190. }
  191. /**
  192. * 向ES发送更新文档请求,将一个对象更新到服务器,会替换原有对应ID的数据。
  193. *
  194. * @param id id
  195. * @param type 存储类型
  196. * @param t 存储对象
  197. * @return 是否更新成功
  198. */
  199. public boolean updateDocument(String id, String type, T t) {
  200. client.prepareUpdate(index, type, id).setDoc(toJSONString(t))
  201. .execute().actionGet();
  202. return true;
  203. }
  204. /**
  205. * 向ES发送批量更新请求
  206. *
  207. * @param id 索引ID
  208. * @param t 存储对象
  209. * @return 成功表示放入到bulk成功, 可能会抛出runtimeException
  210. */
  211. public boolean updateDocumentBulk(String id, T t) {
  212. UpdateRequest updateRequest = new UpdateRequest(index, type, id).doc(toJSONString(t));
  213. bulkProcessor.add(updateRequest);
  214. return true;
  215. }
  216. /**
  217. * 向ES发送upsert请求, 如果该document不存在将会新建这个document, 如果存在则更新.
  218. *
  219. * @param id id
  220. * @param t 存储对象
  221. * @return 是否执行成功
  222. */
  223. public boolean upsertDocument(String id, T t) {
  224. return upsertDocument(id, type, t);
  225. }
  226. /**
  227. * 向ES发送upsert请求, 如果该document不存在将会新建这个document, 如果存在则更新.
  228. *
  229. * @param id id
  230. * @param type 存储类型
  231. * @param t 存储对象
  232. * @return 是否执行成功
  233. */
  234. public boolean upsertDocument(String id, String type, T t) {
  235. client.prepareUpdate(index, type, id).setDocAsUpsert(true).setDoc(toJSONString(t))
  236. .execute().actionGet();
  237. return true;
  238. }
  239. /**
  240. * 向ES发送批量upsert的请求.
  241. *
  242. * @param id id
  243. * @param t 储存对象
  244. * @return 是否执行成功
  245. */
  246. public boolean upsertDocumentBulk(String id, T t) {
  247. UpdateRequest updateRequest = new UpdateRequest(index, type, id)
  248. .doc(toJSONString(t));
  249. updateRequest.docAsUpsert(true);
  250. bulkProcessor.add(updateRequest);
  251. return true;
  252. }
  253. /**
  254. * 向ES发送获取指定ID文档的请求
  255. *
  256. * @param id id
  257. * @return 搜索引擎实例
  258. * @throws Exception
  259. */
  260. public T getDocument(String id) throws Exception {
  261. try {
  262. GetResponse getResponse = client.prepareGet(index, type, id)
  263. .execute().actionGet();
  264. if (getResponse.getSource() == null) {
  265. return null;
  266. }
  267. JSONObject jsonObject = new JSONObject(getResponse.getSource());
  268. T t = clazz.newInstance();
  269. toObject(t, jsonObject);
  270. return t;
  271. } catch (Exception e) {
  272. throw new RuntimeException(e);
  273. }
  274. }
  275. /**
  276. * 向ES发送删除指定ID文档的请求
  277. *
  278. * @param id id
  279. * @return 是否删除成功
  280. * @throws Exception
  281. */
  282. public boolean deleteDocument(String id) throws Exception {
  283. return deleteDocument(id, type);
  284. }
  285. /**
  286. * 向ES发送删除指定ID文档的请求
  287. *
  288. * @param id id
  289. * @param type 存储类型
  290. * @return 是否删除成功
  291. * @throws Exception
  292. */
  293. public boolean deleteDocument(String id, String type) throws Exception {
  294. DeleteResponse deleteResponse = client.prepareDelete(index, type, id)
  295. .execute().actionGet();
  296. return deleteResponse.isFound();
  297. }
  298. /**
  299. * 向ES发送搜索文档的请求,返回分页结果
  300. *
  301. * @param searchText 搜索内容
  302. * @return 分页结果
  303. * @throws Exception
  304. */
  305. public PageResponse<T> searchDocument(String searchText) throws Exception {
  306. PageRequest pageRequest = WebContext.get().page();
  307. SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index)
  308. .setTypes(type)
  309. .setQuery(QueryBuilders.matchQuery("_all", searchText))
  310. .setFrom(pageRequest.getOffset())
  311. .setSize(pageRequest.getLimit())
  312. .setFetchSource(true);
  313. return searchDocument(searchRequestBuilder);
  314. }
  315. /**
  316. * 向ES发送搜索文档的请求,返回列表结果
  317. *
  318. * @param searchText 搜索内容
  319. * @param start 起始位置
  320. * @param size 获取数据大小
  321. * @return 返回数据列表
  322. * @throws Exception
  323. */
  324. public List<T> searchDocument(String searchText, int start, int size) throws Exception {
  325. SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index)
  326. .setTypes(type)
  327. .setQuery(QueryBuilders.matchQuery("_all", searchText))
  328. .setFrom(start)
  329. .setSize(size)
  330. .setFetchSource(true);
  331. PageResponse<T> pageResponse = searchDocument(searchRequestBuilder);
  332. return pageResponse.getItemList();
  333. }
  334. /**
  335. * 向ES发送搜索文档的请求,返回列表结果
  336. *
  337. * @param searchText 搜索内容
  338. * @param type 类型
  339. * @param start 起始位置
  340. * @param size 数据大小
  341. * @return 返回数据列表
  342. * @throws Exception
  343. */
  344. public List<T> searchDocument(String searchText, String type, int start, int size) throws Exception {
  345. SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index)
  346. .setTypes(type)
  347. .setQuery(QueryBuilders.matchQuery("_all", searchText))
  348. .setFrom(start)
  349. .setSize(size)
  350. .setFetchSource(true);
  351. PageResponse<T> pageResponse = searchDocument(searchRequestBuilder);
  352. return pageResponse.getItemList();
  353. }
  354. /**
  355. * 向ES发送搜索文档的请求,返回分页结果
  356. *
  357. * @param searchRequestBuilder 搜索构造器
  358. * @return 分页结果
  359. * @throws Exception
  360. */
  361. public PageResponse<T> searchDocument(SearchRequestBuilder searchRequestBuilder) throws Exception {
  362. SearchResponse searchResponse = search(searchRequestBuilder);
  363. return searchResponseToPageResponse(searchResponse);
  364. }
  365. /**
  366. * 获得scrollId对应的数据. 请查看{@link #getScrollId(SearchRequestBuilder, int, int)}.<br/>
  367. * 可以反复调用该方法, 直到返回数据为0.
  368. *
  369. * @param scrollId 给定的scrollId
  370. * @param keepSeconds scroll数据保留时间
  371. * @return 分页结果
  372. * @throws Exception
  373. */
  374. public PageResponse<T> scrollSearchDocument(String scrollId, int keepSeconds) throws Exception {
  375. return searchResponseToPageResponse(scrollSearch(scrollId, keepSeconds));
  376. }
  377. /**
  378. * 向ES发送搜索请求,然后直接返回原始结果。
  379. *
  380. * @param searchRequestBuilder 搜索构造器
  381. * @return 返回结果
  382. */
  383. public SearchResponse search(SearchRequestBuilder searchRequestBuilder) {
  384. return searchRequestBuilder.setTypes(type).execute().actionGet();
  385. }
  386. /**
  387. * 向ES发送搜索请求,然后直接返回原始结果。
  388. *
  389. * @param searchRequestBuilder 搜索构造器
  390. * @param type 类型
  391. * @return 返回结果
  392. */
  393. @Deprecated
  394. public SearchResponse search(SearchRequestBuilder searchRequestBuilder, String type) {
  395. return searchRequestBuilder.setTypes(type).execute().actionGet();
  396. }
  397. /**
  398. * 通过scrollId获得数据.请查看{@link #getScrollId(SearchRequestBuilder, int, int)}.<br/>
  399. * 可以反复调用该方法, 直到返回数据为0.
  400. *
  401. * @param scrollId 给定的scrollId
  402. * @param keepSeconds scroll继续保留的时间, 建议60秒
  403. * @return 返回获取的数据
  404. */
  405. public SearchResponse scrollSearch(String scrollId, int keepSeconds) {
  406. return client.prepareSearchScroll(scrollId).setScroll(new TimeValue(keepSeconds * 1000))
  407. .execute().actionGet();
  408. }
  409. /**
  410. * 提供搜索构造器来获得搜索scrollId, 这个scrollId用作{@link #scrollSearch(String, int)}
  411. * 和{@link #scrollSearchDocument(String, int)}的参数. <br/>
  412. * 当需要获取大量数据的时候, 请使用scrollSearch来进行.
  413. *
  414. * @param searchRequestBuilder 搜索构造器
  415. * @param keepSeconds scroll搜索保留时间, 建议60秒
  416. * @param sizePerShard 每次每个分片获取的尺寸
  417. * @return 返回scrollId, 用于scrollSearch方法.
  418. */
  419. public String getScrollId(SearchRequestBuilder searchRequestBuilder, int keepSeconds, int sizePerShard) {
  420. SearchResponse searchResponse = searchRequestBuilder.setSearchType(SearchType.SCAN)
  421. .setScroll(new TimeValue(keepSeconds * 1000))
  422. .setSize(sizePerShard).execute().actionGet();
  423. return searchResponse.getScrollId();
  424. }
  425. /**
  426. * 返回搜索指定内容后,总共ES找到匹配的数据量。
  427. *
  428. * @param searchText 搜索内容
  429. * @return 搜索结果数据量
  430. */
  431. @Deprecated
  432. public long countSearchResult(String searchText) {
  433. CountRequestBuilder countRequestBuilder = client.prepareCount(index)
  434. .setTypes(type)
  435. .setQuery(QueryBuilders.matchQuery("_all", searchText));
  436. return countSearchResult(countRequestBuilder);
  437. }
  438. /**
  439. * 返回搜索指定内容后,总共ES找到匹配的数据量。
  440. *
  441. * @param searchText 搜索内容
  442. * @param type 类型
  443. * @return 搜索结果数据量
  444. */
  445. @Deprecated
  446. public long countSearchResult(String searchText, String type) {
  447. CountRequestBuilder countRequestBuilder = client.prepareCount(index)
  448. .setTypes(type)
  449. .setQuery(QueryBuilders.matchQuery("_all", searchText));
  450. return countSearchResult(countRequestBuilder);
  451. }
  452. /**
  453. * 返回搜索指定内容后,总共ES找到匹配的数据量。
  454. *
  455. * @param countRequestBuilder 计数请求构造器实例
  456. * @return 搜索结果数据量
  457. * @see #prepareCount()
  458. */
  459. @Deprecated
  460. public long countSearchResult(CountRequestBuilder countRequestBuilder) {
  461. return countRequestBuilder.execute().actionGet().getCount();
  462. }
  463. /**
  464. * 用默认的分词器进行文本分词。
  465. *
  466. * @param docText 给定的文本
  467. * @param order 是否使用排序,如果使用排序,则相同分词会被合并,并且出现次数最高的排在返回列表最头部。
  468. * @return 分词器将文本分词之后的词语列表
  469. */
  470. public List<String> analyzeDocument(String docText, boolean order) {
  471. List<AnalyzeToken> tokenList = analyzeDocument(docText, DEFAULT_ANALYZER);
  472. if (order) {
  473. // 如果是使用排序,按照分词出现次数进行排序,并且会合并相同的分词。
  474. // 构造分词Map,key为分词,value为出现次数。
  475. Map<String, Integer> tokenMap = Maps.newHashMap();
  476. for (AnalyzeToken token : tokenList) {
  477. if (tokenMap.get(token.getTerm()) == null) {
  478. tokenMap.put(token.getTerm(), 1);
  479. } else {
  480. tokenMap.put(token.getTerm(), tokenMap.get(token.getTerm()) + 1);
  481. }
  482. }
  483. // 将分词Map进行排序
  484. List<Map.Entry<String, Integer>> tokenSortList = Ordering.from(new Comparator<Map.Entry<String, Integer>>() {
  485. @Override
  486. public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
  487. return o2.getValue().compareTo(o1.getValue());
  488. }
  489. }).sortedCopy(tokenMap.entrySet());
  490. // 返回分词列表。
  491. return Lists.transform(tokenSortList, new Function<Map.Entry<String, Integer>, String>() {
  492. @Override
  493. public String apply(Map.Entry<String, Integer> input) {
  494. return input.getKey();
  495. }
  496. });
  497. } else {
  498. // 返回所有分词结果
  499. return Lists.transform(tokenList, new Function<AnalyzeToken, String>() {
  500. @Override
  501. public String apply(AnalyzeToken input) {
  502. return input.getTerm();
  503. }
  504. });
  505. }
  506. }
  507. /**
  508. * 用指定分词器来分析给定的文本
  509. *
  510. * @param docText 给定的文本
  511. * @param analyzer 指定的分析器
  512. * @return 分词器将文本分词之后的词语列表
  513. */
  514. public List<AnalyzeToken> analyzeDocument(String docText, String analyzer) {
  515. AnalyzeResponse analyzeResponse = client.admin().indices().prepareAnalyze(index, docText)
  516. .setAnalyzer(analyzer)
  517. .execute().actionGet();
  518. return analyzeResponse.getTokens();
  519. }
  520. /**
  521. * 获得一个搜索请求构造器的实例,通过这个实例,可以进行查询相关操作。<br/>
  522. * 使用这个方法{@link ESClient#searchDocument(SearchRequestBuilder)}进行查询。
  523. * <pre>
  524. * prepareSearch("telepathy")
  525. * .setTypes("article")
  526. * .setSearchType(SearchType.QUERY_THEN_FETCH)
  527. * .setQuery(QueryBuilders.matchQuery("_all", searchText))
  528. * .setFrom(pageRequest.getLimit() * (pageRequest.getPage() - 1))
  529. * .setSize(pageRequest.getLimit())
  530. * .setExplain(true)
  531. * .addHighlightedField("title", 100, 1)
  532. * .setFetchSource(new String[]{}, new String[]{});
  533. * </pre>
  534. *
  535. * @return 搜索请求构造器实例
  536. */
  537. public SearchRequestBuilder prepareSearch() {
  538. return client.prepareSearch(index);
  539. }
  540. /**
  541. * 获得一个计数请求构造器的实例,通过这个实例可以进行查询选项的构造。
  542. *
  543. * @return 计数请求构造器实例
  544. * @see #prepareSearch()
  545. */
  546. @Deprecated
  547. public CountRequestBuilder prepareCount() {
  548. return client.prepareCount(index);
  549. }
  550. /**
  551. * 获得一个Document的term vector (doc frequency, positions, offsets)
  552. *
  553. * @return TermVectorResponse
  554. * @see #termVector()
  555. */
  556. public ActionFuture<TermVectorResponse> termVector(TermVectorRequest request) {
  557. return client.termVector(request);
  558. }
  559. /**
  560. * 将SQL转换成ES的JSON查询对象.
  561. *
  562. * @param sql 给定的SQL
  563. * @return JSON对象
  564. */
  565. public JSONObject convertSqlToJSON(String sql) {
  566. if (sqlJsonMap.get(sql) != null) {
  567. return sqlJsonMap.get(sql);
  568. }
  569. List<String> addresses = Lists.newArrayList(serverHttpAddressList);
  570. while (addresses.size() > 0) {
  571. String sqlPluginUrl = "http://" + addresses.remove(RandomUtils.nextInt(0, addresses.size())) + "/_sql/_explain";
  572. try {
  573. JSONObject json = JSONObject.parseObject(
  574. MucangHttpClient.getDefault().httpPostBody(sqlPluginUrl, sql, "text/plain")
  575. );
  576. sqlJsonMap.put(sql, json);
  577. return json;
  578. } catch (Exception e) {
  579. LOG.error("调用elasticsearch-sql插件时遇到错误, 原因:{}", e);
  580. }
  581. }
  582. throw new RuntimeException("调用elasticsearch-sql插件多次失败, 请检查服务器或者插件功能是否正常.");
  583. }
  584. /**
  585. * 用SQL语句进行搜索. 使用${keyName}的方式代表需要替换的字符串(需要替换的字符串请用双引号或者单引号引起来, 否则插件不能解析)<br/>
  586. * 例如: select * from table where mediaId="${mediaId}"<br/>
  587. *
  588. * @param sql 指定的SQL
  589. * @param kvPairs 替换键值对
  590. * @return 搜索结果
  591. * @throws Exception
  592. */
  593. public SearchResponse searchSql(String sql, final Map<String, String> kvPairs) throws Exception {
  594. JSONObject jsonQuery = convertSqlToJSON(sql);
  595. PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}");
  596. String queryString = propertyPlaceholderHelper.replacePlaceholders(
  597. jsonQuery.toJSONString(),
  598. new PropertyPlaceholderHelper.PlaceholderResolver() {
  599. @Override
  600. public String resolvePlaceholder(String placeholderName) {
  601. if (StringUtils.isBlank(kvPairs.get(placeholderName))) {
  602. return "";
  603. } else {
  604. return kvPairs.get(placeholderName);
  605. }
  606. }
  607. });
  608. SearchRequestBuilder searchRequestBuilder = prepareSearch()
  609. .setSource(XContentFactory.jsonBuilder().value(JSONObject.parseObject(queryString)));
  610. return search(searchRequestBuilder);
  611. }
  612. /**
  613. * 将给定的对象转换成JSON字符串,如果有特殊需求,可以覆盖该方法。
  614. *
  615. * @param t 给定的对象
  616. * @return JSON字符串
  617. */
  618. public String toJSONString(T t) {
  619. return JSON.toJSONString(t);
  620. }
  621. /**
  622. * 将给定的Map里面的值注入到目标对象。如果有特殊需求,可以覆盖该方法。
  623. *
  624. * @param t 目标对象
  625. * @param map 给定的map
  626. * @throws Exception
  627. */
  628. public void toObject(T t, Map<String, ?> map) throws Exception {
  629. dozerBeanMapper.map(map, t);
  630. }
  631. /**
  632. * 将BulkProcessor的缓冲内容进行立即提交.
  633. */
  634. public void flushBulk() {
  635. this.bulkProcessor.flush();
  636. }
  637. public BulkProcessor getBulkProcessor() {
  638. return bulkProcessor;
  639. }
  640. public void setBulkProcessor(BulkProcessor bulkProcessor) {
  641. this.bulkProcessor = bulkProcessor;
  642. }
  643. public PageResponse<T> searchResponseToPageResponse(SearchResponse searchResponse) throws Exception {
  644. PageResponse<T> pageResponse = new PageResponse<>();
  645. for (SearchHit searchHit : searchResponse.getHits().getHits()) {
  646. // 将结果实例化成对应的类型实例
  647. T t = this.clazz.newInstance();
  648. Map<String, Object> hitMap;
  649. if (searchHit.getSource() != null) {
  650. hitMap = searchHit.getSource();
  651. } else {
  652. hitMap = Maps.newHashMap(Maps.transformValues(searchHit.getFields(),
  653. new Function<SearchHitField, Object>() {
  654. @Override
  655. public Object apply(SearchHitField input) {
  656. return input.getValues();
  657. }
  658. }
  659. ));
  660. }
  661. for (HighlightField highlightField : searchHit.getHighlightFields().values()) {
  662. hitMap.put(highlightField.getName(),
  663. StringUtils.join(highlightField.getFragments(), "..."));
  664. }
  665. // 将数据转换成对应的实例
  666. toObject(t, hitMap);
  667. pageResponse.getItemList().add(t);
  668. }
  669. pageResponse.setTotal(searchResponse.getHits().getTotalHits());
  670. return pageResponse;
  671. }
  672. /**
  673. * 关闭native的链接.
  674. */
  675. public void close() {
  676. IOUtils.closeQuietly(bulkProcessor);
  677. this.client.close();
  678. }
  679. }

如果有问题大家可以留言一起交流, 我也是一个es初学者。

【ES】ElasticSearch初体验之使用Java进行最基本的增删改查~的更多相关文章

  1. java对xml文件做增删改查------摘录

    java对xml文件做增删改查 package com.wss; import java.io.File;import java.util.ArrayList;import java.util.Lis ...

  2. 使用java对sql server进行增删改查

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...

  3. Java API实现Hadoop文件系统增删改查

    Java API实现Hadoop文件系统增删改查 Hadoop文件系统可以通过shell命令hadoop fs -xx进行操作,同时也提供了Java编程接口 maven配置 <project x ...

  4. Es图形化软件使用之ElasticSearch-head、Kibana,Elasticsearch之-倒排索引操作、映射管理、文档增删改查

    今日内容概要 ElasticSearch之-ElasticSearch-head ElasticSearch之-安装Kibana Elasticsearch之-倒排索引 Elasticsearch之- ...

  5. ElasticSearch6(三)-- Java API实现简单的增删改查

    基于ElasticSearch6.2.4, Java API创建索引.查询.修改.删除,pom依赖和获取es连接 可查看此文章. package com.xsjt.learn; import java ...

  6. Java项目——模拟电话薄联系人增删改查

    该项目模拟了电话本记录联系人的业务功能,用来练习对数据库的增删改查等操作. 菜单类:Menu -- 用来封装主菜单和个选项的子菜单 Person类: Person--联系人的实体类 TelNoteRe ...

  7. Java Web(十) JDBC的增删改查,C3P0等连接池,dbutils框架的使用

    前面做了一个非常垃圾的小demo,真的无法直面它,菜的抠脚啊,真的菜,好好努力把.菜鸡. --WH 一.JDBC是什么? Java Data Base Connectivity,java数据库连接,在 ...

  8. java对sql server的增删改查

    package Database; import java.sql.*; public class DBUtil { //这里可以设置数据库名称 private final static String ...

  9. Java连接GBase并封装增删改查

    1.介绍 GBase 是南大通用数据技术有限公司推出的自主品牌的数据库产品,目前在国内数据库市场具有较高的品牌知名度;GBase品牌的系列数据库都具有自己鲜明的特点和优势:GBase 8a 是国内第一 ...

随机推荐

  1. 用户权限模块之oauth2.0

    主要是在springsecurity上面扩展即可,所以内容也是基于上一个, sql: CREATE TABLE `auth_access_token` ( `id` int(11) NOT NULL ...

  2. Java基础——抽象类和接口

    之所以将抽象类和接口放在一起做笔记,是因为他们之间很难区分又各自独立.在学习完Java程序设计的三大特点(封装.继承.多态)之后,我最大的收获是,慢慢理解了Java语言这种面向对象程序设计的优越性,它 ...

  3. Vulkan Tutorial 18 重构交换链

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 现在我们已经成功的在屏幕上绘制出三角形,但是在某些情况下, ...

  4. Linux命令 比较文件

    cmp [功能说明] 比较文件 #cmp可以比较任何类型的文件,并在标准输出设备上显示文件的第一次不同处的行号和字节号,分别从1开始,但是一般用于比较文本文件 [语法格式] Cmp[参数][文件1][ ...

  5. window对象screen、history

    Window 对象属性 http://www.runoob.com/jsref/obj-window.html

  6. tomcat抬头有“选择”或“选定”,导致tomcat无法运行问题

    2. 遇到tomcat抬头有"选择"或"选定",导致tomcat无法运行问题 解决:在tomcat抬头右键--属性,去掉"快速编辑模式"勾选 ...

  7. PHP设计模式:工厂方法

    示例代码详见https://github.com/52fhy/design_patterns 工厂方法 工厂方法是针对每一种产品提供一个工厂类.通过不同的工厂实例来创建不同的产品实例. 相比简单工厂, ...

  8. hibernate sql查询转换成VO返回list

    hibernate sql查询转换成VO @Override public List<FenxiVo> getTuanDuiFenxiList(FenxiVo FenxiVo,Intege ...

  9. 探索Windows命令行系列(5):几个实用的命令例解

    1.关机命令(shutdown) 2.管理 Windows 服务(sc) 3.管理任务进程(tasklist.taskkill) 4.显示 TCP/IP 配置值(ipconfig) 5.网络诊断工具( ...

  10. 【Android Developers Training】 16. 暂停和恢复一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...