Spring Boot集成ElasticSearch


本次Spring Boot集成ES,是基于前几篇文章的数据测试,因此在此不再细说,如有不懂的地方,可以先去查看ES前几篇文章介绍。

传送门:

ElasticSearch入门:https://ramona-chen.top/2020/02/29/elasticsearch-ru-men/

ElasticSearch基本用法:https://ramona-chen.top/2020/03/01/elasticsearch-ji-ben-yong-fa/

ElasticSearch高级查询:https://ramona-chen.top/2020/03/02/elasticsearch-gao-ji-cha-xun/

一、Springboot集成ElasticSearch

分布式ES配置调用方式核心代码:

package com.imooc.es.easticsearch;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Configuration
public class MyConfig {

    @Bean
    public TransportClient client() throws UnknownHostException {
        // 此处对应你自己的cluster.name
        Settings settings = Settings.builder().put("cluster.name", "cyh").build();
        PreBuiltTransportClient client = new PreBuiltTransportClient(settings);
        int[] local_ports = {9300,9301,9302};
        for (int port : local_ports) {
            client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"),port));
        }
        return client;
    }
}

遇到的问题(运行报错):

org.elasticsearch.client.transport.NoNodeAvailableException: None of the configured nodes are available

原因:

Elasticsearch版本客户端与服务端版本不一致问题。

解决:

更改elasticsearch5.X调用方式。

错误代码:

for (int port : local_ports) {
client.addTransportAddress(new TransportAddress(
                    InetAddress.getByName("localhost"), port));
                }

改正之后的代码:

for (int port : local_ports) {
            client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"),port));
        }

参考:

被遗忘的博客:https://wenchao.ren/2019/01/org-elasticsearch-client-transport-NoNodeAvailableException-None-of-the-configured-nodes-are-available/

王卫东:https://blog.csdn.net/wwd0501/article/details/79203040

二、接口开发

图书管理系统接口开发数据准备:

图书信息

1、查询接口开发

// 查询接口
    @GetMapping("/get/book/novel")
    public ResponseEntity get(@RequestParam(name = "id", defaultValue = "") String id) {
        if (id.isEmpty()) {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        GetResponse response = client.prepareGet("book", "novel", id).get();
        if (!response.isExists()) {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(response.getSource(), HttpStatus.OK);
    }

接口测试:

查询接口测试

2、增加接口开发

// 增加接口
    @PostMapping("/add/book/novel")
    public ResponseEntity add(
            @RequestParam(name = "title") String title,
            @RequestParam(name = "author") String author,
            @RequestParam(name = "word_count") int wordCount,
            @RequestParam(name = "publish_date")
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date publishDate) {

        try {
            XContentBuilder content = XContentFactory.jsonBuilder().startObject()
                    .field("title", title)
                    .field("author", author)
                    .field("word_count", wordCount)
                    .field("publish_date", publishDate.getTime())
                    .endObject();

            IndexResponse response = client.prepareIndex("book", "novel")
                    .setSource(content)
                    .get();

            return new ResponseEntity(response.getId(), HttpStatus.OK);
        } catch (IOException e) {
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
        }

    }

接口测试:

增加接口测试

遇到的问题:

使用Postman测试接口,可以看到在使用默认的form-data类型时出现了HTTP Status 400 - Required String parameter ‘xx’ is not present这个错误(如下图),更换成x-www-form-urlencoded类型时返回成功。

如果使用@RequestBody接收参数,同@RequestParam一样,都是需要设置x-www-form-urlencoded类型才能正确返回。

错误页

原因:

@RequestBody这个一般处理的是在ajax请求中声明contentType: “application/json; charset=utf-8”时候。也就是json数据或者xml。
@RequestParam这个一般就是在ajax里面没有声明contentType的时候,为默认的urlencode格式时,用这个。

详细说明参考:

已是寒冬:https://blog.csdn.net/qq_26761587/article/details/73691189

3、删除接口开发

// 删除接口
    @DeleteMapping("/delete/book/novel")
    @ResponseBody
    public ResponseEntity delete(@RequestParam(name = "id") String id) {
        DeleteResponse response = client.prepareDelete("book", "novel", id).get();
        return new ResponseEntity(response.getResult().toString(), HttpStatus.OK);
    }

接口测试:

删除接口测试

4、更新接口开发

// 更新接口
    @PutMapping("/update/book/novel")
    @ResponseBody
    public ResponseEntity update(
            @RequestParam(name = "id") String id,
            @RequestParam(name = "title", required = false) String title,
            @RequestParam(name = "author", required = false) String author,
            @RequestParam(name = "word_count", required = false) Integer wordCount,
            @RequestParam(name = "publish_date", required = false)
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date publishDate) {

        try {
            XContentBuilder builder = XContentFactory.jsonBuilder().startObject();

            if (title != null) {
                builder.field("title", title);
            }
            if (author != null) {
                builder.field("author", author);
            }
            if (wordCount != null) {
                builder.field("word_count", wordCount);
            }
            if (publishDate != null) {
                builder.field("publish_date", publishDate.getTime());
            }
            builder.endObject();

            UpdateRequest update = new UpdateRequest("book", "novel", id);
            update.doc(builder);
            UpdateResponse response = client.update(update).get();
            return new ResponseEntity(response.getResult().toString(), HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
        }

    }

接口测试:

更新接口测试

4、复合查询开发

// 复合查询
    @PostMapping("query/book/novel")
    @ResponseBody
    public ResponseEntity query(
            @RequestParam(name = "author", required = false) String author,
            @RequestParam(name = "title", required = false) String title,
            @RequestParam(name = "gt_word_count", defaultValue = "0") int gtWordCount,
            @RequestParam(name = "lt_word_count", required = false) Integer ltWordCount) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        if (author != null) {
            boolQuery.must(QueryBuilders.matchQuery("author", author));
        }

        if (title != null) {
            boolQuery.must(QueryBuilders.matchQuery("title", title));
        }

        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("word_count").from(gtWordCount);
        if (ltWordCount != null && ltWordCount > 0) {
            rangeQuery.to(ltWordCount);
        }
        boolQuery.filter(rangeQuery);

        SearchRequestBuilder builder = client.prepareSearch("book").setTypes("novel")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQuery)
                .setFrom(0)
                .setSize(10);
        SearchResponse response = builder.get();

        List result = new ArrayList<Map<String, Object>>();
        for (SearchHit hit : response.getHits()) {
            result.add(hit.getSourceAsMap());
        }
        return new ResponseEntity(result, HttpStatus.OK);

    }

接口测试:

复合查询接口测试

本文完整代码示例:https://github.com/cyh756085049/ElasticSearch-java


评论
评论
  目录