springboot集成elasticsearch7.2

上篇文章我们讲解了elasticsearch的安装,这次我们来搞一下,如何在自己的项目中集成elasticsearch。 正常来讲spring-data中都会提供相应的starter,让我们方便的使用各种Template操作对应的组件,比如常用RedisTemplate, JdbcTemplate等,其实spring-data中也提供的相应的elasticsearch的对应工具。但是我这里并没有使用,而是直接使用的elasticsearch原生api实现的。为什么这么做呢,因为spring-data-elasticsearch 最新的版本3.2,最高支持的elasticsearch版本为6.8, 而我们用的是7.2的版本,并且官方建议我们使用的jar版本最好和软件版本一致。

还有一个问题, 是关于客户端的, spring-data-elasticsearch中默认使用的是TransportClient, 这个客户端在7这个版本中已经不再建议使用了,并且将会在8的版本中彻底移除。而我们用的是7这个版本,目前推荐使用的elasticsearch的高级客户端,HighLevelRestClient. spring-data-es中声明会一直支持TransportClient,只要你的这个es版本支持。当然,spring-data-es中也是支持高级别客户端的,但是还有由于支持版本过低的问题,所以我最后还是决定采用原生客户端。如果大家用的es版本比较低,还是可以使用spring-data-es的。

接下来我们来集成项目,集成之前,大家需要了解一下es中的一些专有名词,比如什么是索引,类型,文档,同时你要了解es是干什么用的。es最主要的功能就是查询,也就是他查东西的速度非常快,并且支持分词,全文检索。如果我们在mysql中查询一遍文章的内容,其实是非常痛苦的,我们可能必须得使用 like 或者拼接or去查询多个字段,并且有些场景是无法实现的,比如你的文章中的内容中包含 ”一朵鲜花“, 而你去搜索 ”一朵花“ 这种情况你是查不到的,但是es可以,因为es可以分词, 他会一朵鲜花, 分成  ”一朵“  ”鲜花“ 两个词,再把 ”一朵花“ 分成  ”一朵“ 和 ”花“  (注: 这里是个人方便理解,可能具体分词不是这么分的,大家领悟精髓)。 就很容易做到查询。 同时es查询的比较快,也是因为他的内部采用了倒叙索引,关于倒叙索引的原理,大家可以去找找资料,这里就不展开说了。

一。 引入jar包

        <dependency>             <groupId>org.elasticsearch.client</groupId>             <artifactId>elasticsearch-rest-high-level-client</artifactId>             <version>7.2.0</version>         </dependency>          <dependency>             <groupId>org.elasticsearch.client</groupId>             <artifactId>elasticsearch-rest-client</artifactId>             <version>7.2.0</version>         </dependency>         <dependency>             <groupId>org.elasticsearch</groupId>             <artifactId>elasticsearch</artifactId>             <version>7.2.0</version>         </dependency>

二。封装工具类,这里主要使用高级别客户端封装, 主要封装了创建索引,判断索引是否存在,删除索引, 插入文档的功能,还有一些高级功能还没有 研究完,比如高亮和分页,我会一边研究一边更新,先给出一些简单的操作demo.后续文章我们在深入展开。

@Component @Slf4j public class EsUtil {      @Resource     private RestHighLevelClient restHighLevelClient;       /**      * 创建索引(默认分片数为5和副本数为1)      * @param indexName      * @throws IOException      */     public boolean createIndex(String indexName) throws IOException {         CreateIndexRequest request = new CreateIndexRequest(indexName);         request.settings(Settings.builder()                 // 设置分片数为3, 副本为2                 .put("index.number_of_shards", 3)                 .put("index.number_of_replicas", 2)         );         request.mapping(generateBuilder());         CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);         // 指示是否所有节点都已确认请求         boolean acknowledged = response.isAcknowledged();         // 指示是否在超时之前为索引中的每个分片启动了必需的分片副本数         boolean shardsAcknowledged = response.isShardsAcknowledged();         if (acknowledged || shardsAcknowledged) {             log.info("创建索引成功!索引名称为{}", indexName);             return true;         }         return false;     }        /**      * 判断索引是否存在      * @param indexName      * @return      */     public boolean isIndexExists(String indexName){         boolean exists = false;         try {             GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);             getIndexRequest.humanReadable(true);             exists = restHighLevelClient.indices().exists(getIndexRequest,RequestOptions.DEFAULT);         } catch (IOException e) {             e.printStackTrace();         }         return exists;     }      /**      * 删除索引      * @param indexName      * @return      */     public boolean delIndex(String indexName){         boolean acknowledged = false;         try {             DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);             deleteIndexRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);             AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);             acknowledged = delete.isAcknowledged();         } catch (IOException e) {             e.printStackTrace();         }         return acknowledged;     }      /**      * 更新索引(默认分片数为5和副本数为1):      * 只能给索引上添加一些不存在的字段      * 已经存在的映射不能改      *      * @param clazz 根据实体自动映射es索引      * @throws IOException      */     public boolean updateIndex(Class clazz) throws Exception {         Document declaredAnnotation = (Document )clazz.getDeclaredAnnotation(Document.class);         if(declaredAnnotation == null){             throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", clazz.getName()));         }         String indexName = declaredAnnotation.index();         PutMappingRequest request = new PutMappingRequest(indexName);          request.source(generateBuilder(clazz));         AcknowledgedResponse response = restHighLevelClient.indices().putMapping(request, RequestOptions.DEFAULT);         // 指示是否所有节点都已确认请求         boolean acknowledged = response.isAcknowledged();          if (acknowledged ) {             log.info("更新索引索引成功!索引名称为{}", indexName);             return true;         }         return false;     }      /**      * 添加单条数据      * 提供多种方式:      *  1. json      *  2. map      *      Map<String, Object> jsonMap = new HashMap<>();      *      jsonMap.put("user", "kimchy");      *      jsonMap.put("postDate", new Date());      *      jsonMap.put("message", "trying out Elasticsearch");      *      IndexRequest indexRequest = new IndexRequest("posts")      *          .id("1").source(jsonMap);      *  3. builder      *      XContentBuilder builder = XContentFactory.jsonBuilder();      *      builder.startObject();      *      {      *          builder.field("user", "kimchy");      *          builder.timeField("postDate", new Date());      *          builder.field("message", "trying out Elasticsearch");      *      }      *      builder.endObject();      *      IndexRequest indexRequest = new IndexRequest("posts")      *      .id("1").source(builder);      * 4. source:      *      IndexRequest indexRequest = new IndexRequest("posts")      *     .id("1")      *     .source("user", "kimchy",      *         "postDate", new Date(),      *         "message", "trying out Elasticsearch");      *      *   报错:  Validation Failed: 1: type is missing;      *      加入两个jar包解决      *      * @return      */     public IndexResponse add(String indexName, Object o) throws IOException {         IndexRequest request = new IndexRequest(indexName);         String userJson = JSON.toJSONString(o);         request.source(userJson, XContentType.JSON);         IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);         return indexResponse;     }      private XContentBuilder generateBuilder() throws IOException {         XContentBuilder builder = XContentFactory.jsonBuilder();         builder.startObject();         {             builder.startObject("properties");             {                 // es7及以后去掉了映射类型--person                 builder.startObject("name");                     {                         builder.field("type", "text");                         builder.field("analyzer", "ik_smart");                     }                     builder.endObject();             }             {                 builder.startObject("age");                 {                     builder.field("type", "integer");                 }                 builder.endObject();             }             {                 builder.startObject("desc");                 {                     builder.field("type", "text");                     builder.field("analyzer", "ik_smart");                 }                 builder.endObject();             }             {                 builder.startObject("id");                 {                     builder.field("type", "integer");                 }                 builder.endObject();             }             builder.endObject();         }         builder.endObject();         /*.startObject().field("properties")             .startObject().field("person")                 .startObject("name")                     .field("type" , "text")                     .field("analyzer", "ik_smart")                 .endObject()                 .startObject("age")                     .field("type" , "int")                 .endObject()                 .startObject("desc")                     .field("type", "text")                     .field("analyzer", "ik_smart")                 .endObject()             .endObject()         .endObject();*/         return builder;     }    }

上面工具类中给出的索引结构是一个用户,只有id, name , age, desc 四个简单字段的结构

同时desc字段和姓名字段都是使用的ik-smart做的分词。

接下来大家就可以使用controller或者junittest来进行调用, 配合head插件观察数据。 整体的大致流程就是, index定义索引结构,然后我们把按格式数据存到es中, 使用es提供的高效api来做查询。 这篇文章先到这里,其实这里有一个痛点就是如果我们的数据结构比较复杂, 那么我们在创建索引的时候可能需要写出大量的代码,四个字段就这么多

所以这里其实我们可以根据实体的结构自动设计索引结构,像spring-data-es中就是根据我们在实体类上的注解,自动创建索引的。我这里也实现了自定义注解来创建es索引结构的方法,下一篇文章给大家介绍一下。