SpringBoot整合ElasticSearch实现多版本的兼容

前言

上一篇学习SpringBoot中,整合了Mybatis、Druid和PageHelper并实现了多数据源的操作。本篇主要是介绍和使用目前最火的搜索引擎ElastiSearch,并和SpringBoot进行结合使用。

ElasticSearch介绍

ElasticSearch是一个基于Lucene的搜索服务器,其实就是对Lucene进行封装,提供了 REST API 的操作接口 ElasticSearch作为一个高度可拓展的开源全文搜索和分析引擎,可用于快速地对大数据进行存储,搜索和分析。
ElasticSearch主要特点:分布式、高可用、异步写入、多API、面向文档 。
ElasticSearch核心概念:近实时,集群,节点(保存数据),索引,分片(将索引分片),副本(分片可设置多个副本) 。它可以快速地储存、搜索和分析海量数据。
ElasticSearch使用案例:维基百科、Stack Overflow、Github 等等。

SpringBoot整合Elasticsearch

在使用SpringBoot整合Elasticsearch 之前,我们应该了解下它们之间对应版本的关系。

Spring Boot Version (x) Spring Data Elasticsearch Version (y) Elasticsearch Version (z)
x <= 1.3.5 y <= 1.3.4 z <= 1.7.2*
x >= 1.4.x 2.0.0 <=y < 5.0.0** 2.0.0 <= z < 5.0.0**

这里我们使用的SpringBoot的版本是1.5.9,Elasticsearch的版本是2.3.5。

使用SpringBoot整合Elasticsearch,一般都是使用 SpringData 进行封装的,然后再dao层接口继承ElasticsearchRepository 类,该类实现了很多的方法,比如常用的CRUD方法。

SpringData的使用

首先,在使用之前,先做好相关的准备。

Maven的配置如下:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
             <version>1.5.9.RELEASE</version>
        </dependency>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
             <version>1.5.9.RELEASE</version>
        </dependency>

application.properties的配置

spring.data.elasticsearch.repositories.enabled = true
spring.data.elasticsearch.cluster-nodes =127.0.0.1\:9300

注: 9300 是 Java 客户端的端口。9200 是支持 Restful HTTP 的接口。

更多的配置:

spring.data.elasticsearch.cluster-name Elasticsearch 集群名。(默认值: elasticsearch)
spring.data.elasticsearch.cluster-nodes 集群节点地址列表,用逗号分隔。如果没有指定,就启动一个客户端节点。
spring.data.elasticsearch.propertie 用来配置客户端的额外属性。
spring.data.elasticsearch.repositories.enabled 开启 Elasticsearch 仓库。(默认值:true。)

代码编写

实体类

@Document(indexName = "userindex", type = "user")
public class User implements Serializable{
     /**
     *
     */
    private static final long serialVersionUID = 1L;
    /** 编号 */
     private Long id;
     /** 姓名 */
     private String name;

     /** 年龄 */
     private Integer age;

     /** 描述 */
     private String description;

     /** 创建时间 */
     private String createtm;

    // getter和setter 略
}

使用SpringData的时候,它需要在实体类中设置indexNametype ,如果和传统型数据库比较的话,就相当于。需要注意的是indexNametype都必须是小写!!!

dao层

public interface UserDao extends ElasticsearchRepository<User, Long>{
}

dao层这里就比较简单了,只需继承ElasticsearchRepository该类就行了。其中主要的方法就是 save、delete和search。其中save方法相当如insert和update,没有就新增,有就覆盖。delete方法主要就是删除数据以及索引库。至于search就是查询了,包括一些常用的查询,如分页、权重之类的。

Service层

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public boolean insert(User user) {
        boolean falg=false;
        try{
            userDao.save(user);
            falg=true;
        }catch(Exception e){
            e.printStackTrace();
        }
        return falg;
    }

    @Override
    public List<User> search(String searchContent) {
          QueryStringQueryBuilder builder = new QueryStringQueryBuilder(searchContent);
          System.out.println("查询的语句:"+builder);
          Iterable<User> searchResult = userDao.search(builder);
          Iterator<User> iterator = searchResult.iterator();
          List<User> list=new ArrayList<User>();
          while (iterator.hasNext()) {
            list.add(iterator.next());
          }
       return list;
    }

    @Override
    public List<User> searchUser(Integer pageNumber, Integer pageSize,String searchContent) {
         // 分页参数
        Pageable pageable = new PageRequest(pageNumber, pageSize);
        QueryStringQueryBuilder builder = new QueryStringQueryBuilder(searchContent);
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable).withQuery(builder).build();
        System.out.println("查询的语句:" + searchQuery.getQuery().toString());
        Page<User> searchPageResults = userDao.search(searchQuery);
        return searchPageResults.getContent();
    }

    @Override
    public List<User> searchUserByWeight(String searchContent) {
     // 根据权重进行查询
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()
                .add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", searchContent)),
                    ScoreFunctionBuilders.weightFactorFunction(10))
                .add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("description", searchContent)),
                        ScoreFunctionBuilders.weightFactorFunction(100)).setMinScore(2);
        System.out.println("查询的语句:" + functionScoreQueryBuilder.toString());
        Iterable<User> searchResult = userDao.search(functionScoreQueryBuilder);
        Iterator<User> iterator = searchResult.iterator();
        List<User> list=new ArrayList<User>();
        while (iterator.hasNext()) {
            list.add(iterator.next());
        }
        return list;
    }
}

这里我就简单的写了几个方法,其中主要的方法是查询。查询包括全文搜索,分页查询和权重查询。其中需要说明的是权重查询这块,权重的分值越高,查询的结果也越靠前,如果没有对其它的数据设置分值,它们默认的分值就是1,如果不想查询这些语句,只需使用setMinScore将其设为大于1即可。

代码测试

调用接口进行添加数据

新增数据:

POST http://localhost:8086/api/user

{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师","createtm":"2018-4-25 11:07:42"}
{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}
{"id":3,"name":"王五","age":25,"description":"王五是个运维工程师","createtm":"2016-8-21 06:11:32"}

进行全文查询
请求

http://localhost:8086/api/user?searchContent=工程师

返回

[{"id":2,"name":"李四","age":14,"description":"李四是个测试工程师","createtm": "1980-2-15 19:01:32"},
{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师", "createtm": "2018-4-25 11:07:42"},
{"id":3,"name":"王五","age":25,"description":"王五是个运维工程师","createtm": "2016-8-21 06:11:32"}]

进行分页查询
请求

http://localhost:8086/api/user?pageNumber=0&pageSize=2&searchContent=工程师

返回

[{"id":2,"name":"李四","age":14,"description":"李四是个测试工程师"},{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师"}]

进行权重查询
请求

http://localhost:8086/api/user2?searchContent=李四

返回

[{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}]

权重查询打印的语句:

查询的语句:{{
  "function_score" : {
    "functions" : [ {
      "filter" : {
        "bool" : {
          "should" : {
            "match" : {
              "name" : {
                "query" : "李四",
                "type" : "boolean"
              }
            }
          }
        }
      },
      "weight" : 10.0
    }, {
      "filter" : {
        "bool" : {
          "should" : {
            "match" : {
              "description" : {
                "query" : "李四",
                "type" : "boolean"
              }
            }
          }
        }
      },
      "weight" : 100.0
    } ],
    "min_score" : 2.0
  }
}

注:测试中,因为设置了setMinScore最小权重分为2的,所以无关的数据是不会显示出来的。如果想显示的话,在代码中去掉即可。

新增完数据之后,可以在浏览器输入:http://localhost:9200/_plugin/head/
然后点击基本查询,便可以查看添加的数据。如果想用语句查询,可以将程序中控制台打印的查询语句粘贴到查询界面上进行查询!

注:这里的ElasticSearch是我在windows上安装的,并安装了ES插件head,具体安装步骤在文章末尾。

除了SpringData之外,其实还有其它的方法操作ElasticSearch的。
比如使用原生ElasticSearch的Api,使用TransportClient类实现。
或者使用由Spring封装,只需在Service层,进行注入Bean即可。
示例:

@Autowired
 ElasticsearchTemplate elasticsearchTemplate; 

但是,上述方法中都有其局限性,也就是随着ElasticSearch的版本变更,相关的Java API也在做不断的调整,就是ElasticSearch服务端版本进行更改之后,客户端的代码可能需要重新编写。
因此介绍一个相当好用的第三方工具JestClient,它对ElasticSearch进行封装,填补了 ElasticSearch HttpRest接口 客户端的空白,它适用于ElasticSearch2.x以上的版本,无需因为ElasticSearch服务端版本更改而对代码进行更改!

JestClient

首先在Maven中添加如下依赖:

    <dependency>
        <groupId>io.searchbox</groupId>
         <artifactId>jest</artifactId>
        <version>5.3.3</version>
    </dependency>

然后编写相关的测试代码。
代码中的注释应该很完整,所以这里就不再对代码过多的讲述了。

import java.util.ArrayList;
import java.util.List;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import com.pancm.pojo.User;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestClientFactory;
import io.searchbox.client.JestResult;
import io.searchbox.client.config.HttpClientConfig;
import io.searchbox.core.Bulk;
import io.searchbox.core.BulkResult;
import io.searchbox.core.Delete;
import io.searchbox.core.DocumentResult;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.indices.CreateIndex;
import io.searchbox.indices.DeleteIndex;
import io.searchbox.indices.mapping.GetMapping;
import io.searchbox.indices.mapping.PutMapping;

public class JestTest {
        private static JestClient jestClient;
        private static String indexName = "userindex";
//      private static String indexName = "userindex2";
        private static String typeName = "user";
        private static String elasticIps="http://192.169.2.98:9200";
//      private static String elasticIps="http://127.0.0.1:9200";

        public static void main(String[] args) throws Exception {
            jestClient = getJestClient();
            insertBatch();
            serach1();
            serach2();
            serach3();
            jestClient.close();  

        }

        private static  JestClient getJestClient() {
            JestClientFactory factory = new JestClientFactory();
            factory.setHttpClientConfig(new HttpClientConfig.Builder(elasticIps).connTimeout(60000).readTimeout(60000).multiThreaded(true).build());
            return factory.getObject();
        }  

        public static void insertBatch() {
            List<Object> objs = new ArrayList<Object>();
            objs.add(new User(1L, "张三", 20, "张三是个Java开发工程师","2018-4-25 11:07:42"));
            objs.add(new User(2L, "李四", 24, "李四是个测试工程师","1980-2-15 19:01:32"));
            objs.add(new User(3L, "王五", 25, "王五是个运维工程师","2016-8-21 06:11:32"));
            boolean result = false;
            try {
                result = insertBatch(jestClient,indexName, typeName,objs);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("批量新增:"+result);
        }

        /**
         * 全文搜索
         */
        public static void serach1() {
            String query ="工程师";
            try {
                SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                 searchSourceBuilder.query(QueryBuilders.queryStringQuery(query));
                 //分页设置
                 searchSourceBuilder.from(0).size(2);
                System.out.println("全文搜索查询语句:"+searchSourceBuilder.toString());
                System.out.println("全文搜索返回结果:"+search(jestClient,indexName, typeName, searchSourceBuilder.toString()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 精确搜索
         */
        public static void serach2() {
            try {
                SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                searchSourceBuilder.query(QueryBuilders.termQuery("age", 24));
                System.out.println("精确搜索查询语句:"+searchSourceBuilder.toString());
                System.out.println("精确搜索返回结果:"+search(jestClient,indexName, typeName, searchSourceBuilder.toString()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 区间搜索
         */
        public static void serach3() {
            String createtm="createtm";
            String from="2016-8-21 06:11:32";
            String to="2018-8-21 06:11:32";

            try {
                SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                searchSourceBuilder.query(QueryBuilders.rangeQuery(createtm).gte(from).lte(to));
                System.out.println("区间搜索语句:"+searchSourceBuilder.toString());
                System.out.println("区间搜索返回结果:"+search(jestClient,indexName, typeName, searchSourceBuilder.toString()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 创建索引
         * @param indexName
         * @return
         * @throws Exception
         */
        public boolean createIndex(JestClient jestClient,String indexName) throws Exception {
            JestResult jr = jestClient.execute(new CreateIndex.Builder(indexName).build());
            return jr.isSucceeded();
        }  

        /**
         * 新增数据
         * @param indexName
         * @param typeName
         * @param source
         * @return
         * @throws Exception
         */
        public boolean insert(JestClient jestClient,String indexName, String typeName, String source) throws Exception {
            PutMapping putMapping = new PutMapping.Builder(indexName, typeName, source).build();
            JestResult jr = jestClient.execute(putMapping);
            return jr.isSucceeded();
        }  

         /**
          * 查询数据
          * @param indexName
          * @param typeName
          * @return
          * @throws Exception
          */
        public static String getIndexMapping(JestClient jestClient,String indexName, String typeName) throws Exception {
            GetMapping getMapping = new GetMapping.Builder().addIndex(indexName).addType(typeName).build();
            JestResult jr =jestClient.execute(getMapping);
            return jr.getJsonString();
         }  

       /**
        * 批量新增数据
        * @param indexName
        * @param typeName
        * @param objs
        * @return
        * @throws Exception
        */
        public static boolean insertBatch(JestClient jestClient,String indexName, String typeName, List<Object> objs) throws Exception {
            Bulk.Builder bulk = new Bulk.Builder().defaultIndex(indexName).defaultType(typeName);
            for (Object obj : objs) {
                Index index = new Index.Builder(obj).build();
                 bulk.addAction(index);
            }
            BulkResult br = jestClient.execute(bulk.build());
            return br.isSucceeded();
           }  

        /**
         * 全文搜索
         * @param indexName
         * @param typeName
         * @param query
         * @return
         * @throws Exception
         */
        public static String search(JestClient jestClient,String indexName, String typeName, String query) throws Exception {
             Search search = new Search.Builder(query)
             .addIndex(indexName)
             .addType(typeName)
             .build();
            JestResult jr = jestClient.execute(search);
//          System.out.println("--"+jr.getJsonString());
//          System.out.println("--"+jr.getSourceAsObject(User.class));
            return jr.getSourceAsString();
         }  

       /**
        * 删除索引
        * @param indexName
        * @return
        * @throws Exception
        */
        public boolean delete(JestClient jestClient,String indexName) throws Exception {
            JestResult jr = jestClient.execute(new DeleteIndex.Builder(indexName).build());
            return jr.isSucceeded();
        }  

       /**
        * 删除数据
        * @param indexName
        * @param typeName
        * @param id
        * @return
        * @throws Exception
        */
        public boolean delete(JestClient jestClient,String indexName, String typeName, String id) throws Exception {
            DocumentResult dr = jestClient.execute(new Delete.Builder(id).index(indexName).type(typeName).build());
            return dr.isSucceeded();
        }  

注:测试之前先说明下,本地windows系统安装的是ElasticSearch版本是2.3.5,linux服务器上安装的ElasticSearch版本是6.2。

测试结果

全文搜索

全文搜索查询语句:{
  "from" : 0,
  "size" : 2,
  "query" : {
    "query_string" : {
      "query" : "工程师"
    }
  }
}

全文搜索返回结果:{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师","createtm":"2018-4-25 11:07:42"},{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}

匹配搜索

精确搜索查询语句:{
  "query" : {
    "term" : {
      "age" : 24
    }
  }
}

精确搜索返回结果:{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}

时间区间搜索

区间搜索语句:{
  "query" : {
    "range" : {
      "createtm" : {
        "from" : "2016-8-21 06:11:32",
        "to" : "2018-8-21 06:11:32",
        "include_lower" : true,
        "include_upper" : true
      }
    }
  }
}
区间搜索返回结果:{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师","createtm":"2018-4-25 11:07:42"}

新增完数据之后,我们可以上linux的 Kibana中进行相关的查询,查询结果如下:

注:Kibana 是属于ELK中一个开源软件。Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。

上述代码中测试返回的结果符合我们的预期。其中关于JestClient只是用到了很少的一部分,更多的使用可以查看JestClient的官方文档。

Windows安装ElasticSearch

1,文件准备
下载地址:
https://www.elastic.co/downloads
选择ElasticSearch相关版本, 然后选择后缀名为ZIP文件进行下载,下载之后进行解压。

2,启动Elasticsearch
进入bin目录下,运行 elasticsearch.bat
然后在浏览上输入: localhost:9200
成功显示一下界面表示成功!

3,安装ES插件
web管理界面head 安装
进入bin目录下,打开cmd,进入dos界面
输入:plugin install mobz/elasticsearch-head
进行下载
成功下载之后,在浏览器输入:http://localhost:9200/_plugin/head/
若显示一下界面,则安装成功!

4,注册服务
进入bin目录下,打开cmd,进入dos界面
依次输入:
service.bat install
service.bat start
成功之后,再输入
services.msc
跳转到Service服务界面,可以直接查看es的运行状态!

其它

ElasticSearch官网API地址:
https://www.elastic.co/guide/en/elasticsearch/client/java-api/2.3/index.html

JestClientGithub地址:
https://github.com/searchbox-io/Jest

项目我放到github上面去了。
https://github.com/xuwujing/springBoot

如果觉得不错,希望顺便给个star。
到此,本文结束,谢谢阅读。

版权声明:
作者:虚无境
博客园出处:http://www.cnblogs.com/xuwujing
CSDN出处:http://blog.csdn.net/qazwsxpcm    
个人博客出处:http://www.panchengming.com
原创不易,转载请标明出处,谢谢!

原文地址:http://blog.51cto.com/12965378/2325599

时间: 2024-08-03 10:15:25

SpringBoot整合ElasticSearch实现多版本的兼容的相关文章

SpringBoot整合elasticsearch(三)

Docker安装elasticsearch 启动注意2点,1是内存,2是线程数(此处进行简单安装,后面会详细补充es文档) 1 [[email protected] ~]# docker images 2 REPOSITORY TAG IMAGE ID CREATED SIZE 3 elasticsearch latest 874179f19603 12 days ago 771 MB 4 springbootdemo4docker latest cd13bc7f56a0 2 weeks ago

SpringBoot整合Elasticsearch框架

新建SpringBoot项目: 修改pom.xml文件,引入spring-boot-data-elasticsearch Jar 包: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <version>2.2.2.RELEASE</ver

【SpringBoot整合Elasticsearch】SpringBoot整合ElasticSearch

一.Linux下安装ElasticSearch 1.检测是否安装了Elasticsearch 1 ps aux |grep elasticsearch 2.安装JDK 3.下载Elasticsearch 1 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0.tar.gz 解压Elasticsearch tar -zxvf elasticsearch-6.0.0.tar.gz 移动Elasti

springboot 整合 elasticsearch

1引入jar包 <!--elasticsearch--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> 2实体类 package com.miracle.config.elasticsearch.ent

springboot整合elasticsearch遇到的错误

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.2018-12-29 11:54:39.572 ERROR 7563 --- [ main] o.s.boot.SpringApplication : Application startup failed org.springframework.beans.

SpringBoot整合ElasticSearch

1.docker安装ElasticSearch ps:前提是电脑必须安装好java并配置好环境变量 从远程仓库拉取镜像 docker pull elasticsearch:6.8.0 由于es需要的内存比较大,所以配置JVM修改内存 docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name elastic elasticsearch:6.8.0 原文地址:https://www.

springboot整合es客户端操作elasticsearch(二)

在上章节中整合elasticsearch客户端出现版本问题进行了处理,这章来进行springboot整合得操作 环境:elaticsearch6.2.1,springboot 2.1.8 客户端版本采用6.6.1 一 pom.xml依赖引入 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns

io.dubbo.springboot版本不兼容dubbo-2.5.3

在dubbo整合springboot的时候,使用io.dubbo.springboot的jar包,配合的是dubbo2.5.3的版本,会出现Bean创建失败的错误. pom: <!-- SpringBoot 整合 Dubbo 依赖 --> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId&

SpringBoot检索篇Ⅳ --- 整合ElasticSearch

知识储备:  关于ElasticSearch的基本使用我已经在上一篇文章介绍过了(传送门),本篇文章主要讲述的是SpringBoot与ElasticSearch的整合以及简单的使用. 原文地址:https://www.cnblogs.com/wangxiayun/p/10271122.html