Bootstrap

SpringBoot集成ES(ElasticSearch)

1.导入依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

导入依赖后,注意在依赖中查看对应的版本是否与本机ES对应

2.创建配置并编写测试类

@Configuration
public class ElasticSearchConfig {
    // 注册 rest高级客户端
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("127.0.0.1",9200,"http")
                )
        );
        return client;
    }
}

测试类测试各种常用API:

@SpringBootTest(classes = ElasticSearchApp.class)
@RunWith(SpringRunner.class)
public class ElasticSearchTest {

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    // 测试索引的创建, Request PUT liuyou_index
    @Test
    public void createIndex() throws IOException {
        //创建索引请求
        CreateIndexRequest request=new CreateIndexRequest("create");
        //客户端执行请求  请求后获得响应
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(createIndexResponse);


        //获取索引
        GetIndexRequest getIndexRequest=new GetIndexRequest("create");
        //查看索引是否存在
        boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        System.out.println(exists);

        //删除索引
        DeleteIndexRequest deleteIndexRequest=new DeleteIndexRequest();
        AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        System.out.println(delete.isAcknowledged());

    }
    //删除索引
    @Test
    public void DeleteIndex() throws IOException {


        DeleteIndexRequest deleteIndexRequest=new DeleteIndexRequest("create");
        AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        System.out.println(delete.isAcknowledged());

    }

    //添加文档
    @Test
    public void addDocument() throws IOException {
        //创建对象
        Manager manager=new Manager(1,"王五","123");
        IndexRequest indexRequest=new IndexRequest("test2");
        //规则  PUT  /test2/_doc/1
        indexRequest.id("1");
        indexRequest.timeout(TimeValue.timeValueSeconds(1));
        //将数据类型放入请求                         JSON格式
        indexRequest.source(JSON.toJSONString(manager), XContentType.JSON);

        //客户端发送请求 获取响应结果
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println(indexResponse.toString());
        System.out.println(indexResponse.status());  //对应我们命令返回的状态结果
    }


    //判断文档是否存在
    @Test
    public void testGetDocumentExists() throws IOException {
        //获取文档 判断是否存在
        GetRequest getRequest=new GetRequest("test2","1");
        //不获取_source的上下文了
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
        /*-----------------------------------*/
        //获取文档信息

    }
    //获取文档的信息
    @Test
    public void testGetDocument() throws IOException {
        //获取文档 判断是否存在
        GetRequest getRequest=new GetRequest("test2","1");
        //不获取_source的上下文了
        GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        System.out.println(documentFields.getSourceAsString());//打印文档内容
        System.out.println(documentFields); //返回的是全部内容 和命令一样
    }

    //更新文档
    @Test
    public void updateDocument() throws IOException {
        //获取文档 判断是否存在
        UpdateRequest updateRequest=new UpdateRequest("test2","1");
        Manager manager=new Manager(1,"老六","666");
        //将JSON格式的对象添加
        updateRequest.doc(JSON.toJSONString(manager),XContentType.JSON);
        UpdateResponse update = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println(update.status());
        restHighLevelClient.close();
    }

    //删除文档
    @Test
    public void DeleteDocument() throws IOException {
        DeleteRequest deleteRequest=new DeleteRequest("test2","1");
        deleteRequest.timeout("1s");

        DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println(delete.status());
        restHighLevelClient.close();
    }


    //批量插入数据  真实环境一般都是批量插入
    @Test
    public void BulkDocument() throws IOException {
        BulkRequest bulkRequest=new BulkRequest();
        bulkRequest.timeout("10s");   //数据越大最好设置越大
        ArrayList<Manager> list=new ArrayList<>();
        list.add(new Manager(2,"哈哈结","886"));
        list.add(new Manager(2,"特朗噗","856"));
        list.add(new Manager(2,"王某某","999"));
        //批处理请求
        for (int i = 0; i < list.size(); i++) {
            bulkRequest.add(
                    new IndexRequest("test2")
                    .id(""+(i+1))
                    .source(JSON.toJSONString(list.get(i)),XContentType.JSON));
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulk.hasFailures()); //是否失败  返回false 代表成功

    }
    // 查询
    // SearchRequest 搜索请求
    //  SearchSourceBuilder 条件构造
    // HighLightBuilder  构建高亮
    // TermQueryBuilder 精确查询
    //


    //搜索
    @Test
    public void testSearch() throws IOException {
        SearchRequest searchRequest=new SearchRequest("test2");
        //构建搜索条件
        SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
        searchSourceBuilder.highlighter(); //高亮
        //查询条件,可以使用QueryBuilders工具来实现
        //QueryBuilders.termQuery 精确
        //QueryBuilders.matchAllQuery()  匹配所有
        TermQueryBuilder termQueryBuilder= QueryBuilders.termQuery("username","肖");
        searchSourceBuilder.query(termQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);


    }
}

API总结:其使用规则一般先创建对应的请求Request,例如updateRequest、GetRequest.... 然后请求对象中输入对应的索引值。然后操作各个对象的属性,最后使用client对象进行操作。

3.具体业务实现(个人项目演示)

💡思路:

1.首先需要将数据库中的所有数据,同步到ElasticSearch中。这边先编写好对应的添加类。

2.数据同步完成后,编写相应的搜索业务类。

3.还应该封装一个ES各种操作类,如增删查改等(也可以用现成的ESTemplate)。

💡注意事项:

将文档的id值和数据的id值设为一个值。

在编写ES的操作类时想到一个问题,一个文档数据在es中有一个id值,相当于数据库中每一行的一个主键id值。而操作文档数据需要用到这个id值(发生修改时等),如若文档id值和数据本身的一个id值不一致的话,那么会非常不便。所以需要在批量插入数据的时候,需要将文档id和数据id一一对应。

 //批量插入升级版,文档id值和对象id值一一对应(便于后续删改操作)
    //其中的id值--指的是在es索引中文档的id值  相当于数据库中每一行数据的id值  需要将这两个id值设为同一个
    public void bulkByIdDocument(Map<String,Object> map, String index){
        ConnectElasticSearch.connect(client -> {
            BulkRequest bulkRequest=new BulkRequest(index);
            bulkRequest.timeout(EsConfig.DSTimeOut);
            for (String id : map.keySet()) {
                Object o = map.get(id);
                bulkRequest.add(
                        new IndexRequest(index)
                        .id(id)
                        .source(JSON.toJSONString(map.get(id)),XContentType.JSON)
                );
            }
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            System.out.println("添加是否失败=" + bulkResponse.hasFailures()); //返回false代表成功
        });
    }

将传入参数改为Map<String,Object>结构,其中String对象的id值。Object为该对象。在外部将List中的对象循环取出依次存入这个map即可。

  public void addPhone(){
        List<EsPhone> list = esPhoneDao.findAll();
        Map<String,Object> map=new HashMap<>();
        for (EsPhone esPhone : list) {
            map.put(String.valueOf(esPhone.getId()),esPhone);
        }
        esService.bulkByIdDocument(map,EsConfig.index);
    }

结果显示如图:

💡关于分词器与搜索匹配的坑:

在使用term匹配时候,发现输入两个字以上的中文时搜索不出任何东西出来。后续是排查出索引字段是用的默认standard分词器,而默认中文分词器会把中文全部拆开,所以两字以上无法匹配出来。后续又尝试修改索引分词器,但没有效果。最终删除索引,手动创建索引并给相应的字段添加ik分词器后得到解决。

自定义索引创建规则

PUT /xiaomi-phone
{
  "mappings": {
    "properties": {
      "cap":{
        "type": "text"
      },
      "color":{
        "type": "text"
      },
      "price":{
        "type": "float"
      },
      "num":{
        "type": "long"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "pid":{
        "type": "long"
      },
      "id":{
        "type": "long"
      },
      "brand":{
        "type": "text"
        , "analyzer": "ik_smart"
      },
      "pics":{
        "type": "text"
      }
    }
  }
}

;