java操作elasticsearch实现常用的功能,elasticsearch的增删改查以及搜索提示

准备工作

首先要求电脑安装elasticsearch以及kibana,本人使用的elasticsearch版本是7.2.1,建议初学者和我使用同一个版本,不然可能会出现莫名其貌的错误。
下载地址:
kibana:链接:https://pan.baidu.com/s/1rvzbFsLej0Ri_vkwmUenIw
提取码:89su
elasticsearch:链接:https://pan.baidu.com/s/1-vUv–bXyBJHnYSFVtztNQ
提取码:dsdf
ik分词器: 下载 https://github.com/medcl/elasticsearch-analysis-ik/releases
安装 解压安装到elasticsearch的plugins目录

启动elasticsearch以及kibana

下载后将两款软件解压,然后首先运行elasticsearch的bin目录的elasticsearch.bat,运行成功后在浏览器输入http://localhost:9200,其中9200是elasticsearch的默认端口号,浏览器返回如下信息即表示启动成功elasticsearch启动成功
当elasticsearch启动成功后,打开kibana的bin目录下的kibana.bat,启动kibana,kibana启动会比较慢,大概需要1分钟左右,也不知道是不是因为电脑配置问题。
启动之后,在浏览器访问http://localhost:5601,5601是kibana的默认端口,访问后浏览器出现如下页面,则说明启动成功。
在这里插入图片描述
如果启动kibana后页面显示是英文,则打开kibana的conf目录下的kibana.yml,在最后添加
i18n.locale: “zh-CN”
之后,保存,重新启动kibana即可汉化为中文。

elasticsearch与kibana的简介

Elasticsearch是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
其实以上都是废话,简单来说,elasticsearch就是一个能够帮我们存储数据并且能够很好的完成搜索的搜索引擎。
kibana是Elasticsearch的可视化⼯工具,使我们不用再使用postman等软件发送rest请求来操作elasticsearch。

elasticsearch与关系型数据库比较:
在这里插入图片描述
这个图很关键,因为本文就是教大家如何使用java代码来操作图中的概念。

kibana的简单使用

首先点击后进入控制台,在这里可以进行elasticsearch命令的发送。在这里插入图片描述
我们可以在控制台输入elasticsearch,操作elasticsearch。

然后最下面小齿轮是我们管理界面,在该界面可以查看我们的索引以及映射等信息。
在这里插入图片描述

开始构建

首先在springboot的配置文件中加入elasticsearch的配置

elasticsearch:
  host: localhost
  port: 9200

之后编写一个spring bean来注入spring

@Configuration
public class EsConfig {
    @Value("${elasticsearch.host}")
    private String host;
    @Value("${elasticsearch.port}")
    private Integer port;

    @Bean(destroyMethod = "close")
    public RestHighLevelClient client() {
        return new RestHighLevelClient(RestClient.builder(
                new HttpHost(host, port, "http")
        ));
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }
}

到这里,相当于springboot与elasticsearch已经整合完成了。
下面上核心操作api,以下的代码每操作一部,大家都可以去kibana上面通过RESTful请求查看是否成功。

创建索引index

创建索引index,就相当于我们使用java创建数据库。

	@Resource
    private RestHighLevelClient client;//连接elasticsearch的客户端,在配置文件中已经注入
    private static final String INDEX = "item"; //索引名称

public Object createIndex() {
        try {
            //构建创造索引请求 构造器参数为你想要创建的索引名称
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(INDEX);
            //发送请求
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            //isAcknowledged()是否创建完成
            if (createIndexResponse.isAcknowledged()) {
                return true;
            }
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

创建映射Mapping

创建索引mapping相当于我们创建数据库表,各个字段分别匹配实体类的属性。
首先我们需要一个Java实体类来对应es的mapping

public class Item {

    private Long id;

    private String type;

    private String title;

    private String sellPoint;

    private BigDecimal price;

    private Integer num;

    private Integer status;

    private Date createTime;

    private Date updateTime;

    public Item() {
    }

    public Item(Long id, String type, String title, String sellPoint, BigDecimal price, Integer num, Integer status, Date createTime, Date updateTime) {
        this.id = id;
        this.type = type;
        this.title = title;
        this.sellPoint = sellPoint;
        this.price = price;
        this.num = num;
        this.status = status;
        this.createTime = createTime;
        this.updateTime = updateTime;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getSellPoint() {
        return sellPoint;
    }

    public void setSellPoint(String sellPoint) {
        this.sellPoint = sellPoint;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    /**
     * 需要分词的字段
     * @return
     */
    public static List<String> list(){
        List<String> list = new ArrayList<>();
        list.add("title");
        return list;
    }
}

操作类:

@Override
    public Object createMapping() {
        //构建PutMapping请求
        PutMappingRequest putMappingRequest = new PutMappingRequest(INDEX);
        try {
            //XContentBuilder构造器,构造一个el使用的类json字符串来创建mapping
            XContentBuilder xContentBuilder = setMapping(new Item());
            //将XContentBuilder构造器放入请求中
            putMappingRequest.source(xContentBuilder);
            //构建的类json字符串,不懂得可以将下面这行代码打印,即可知道数据类型
            xContentBuilder.getOutputStream().toString();
            //获取IndicesClient客户端
            IndicesClient indicesClient = client.indices();
            //发送请求
            AcknowledgedResponse acknowledgedResponse = indicesClient.putMapping(putMappingRequest, RequestOptions.DEFAULT);
            //创建成功isAcknowledged()为true
            if (acknowledgedResponse.isAcknowledged()) {
                return true;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

/**
     * 构建XContentBuilder
     * @param obj
     * @return
     */
    private static XContentBuilder setMapping(Object obj) {
        List<Field> fieldList = getFields(obj);
        XContentBuilder mapping = null;
        try {
            mapping = jsonBuilder().startObject().startObject("properties");
            for (Field field : fieldList) {
                //修饰符是static的字段不处理
                if (Modifier.isStatic(field.getModifiers())) {
                    continue;
                }
                String name = field.getName();
                if (Item.list().contains(name)) {
                    //需要分词的字段
                    mapping.startObject(name)
                            .field("type", getElasticSearchMappingType(field.getType().getSimpleName().toLowerCase()))
                            .field("index", "true")
                            //使用IK分词器
                            .field("analyzer", "ik_max_word")
                            .field("search_analyzer", "ik_max_word")
                            .startObject("fields")
                            .startObject("suggest")
                            .field("type", "completion")
                            .field("analyzer", "ik_max_word")
                            .endObject()
                            .endObject()
                            .endObject();
                } else {
                    //不需要分词的字段
                    mapping.startObject(name)
                            .field("type", getElasticSearchMappingType(field.getType().getSimpleName().toLowerCase()))
                            .field("index", "true")
                            .endObject();
                }
            }
            mapping.endObject().endObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mapping;
    }

/**
     * 获取字段名
     *
     * @param obj
     * @return
     */
    private static List<Field> getFields(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        List<Field> fieldList = new ArrayList<>();
        fieldList.addAll(Arrays.asList(fields));
        return fieldList;
    }


/**
     * java基础类型转el类型
     *
     * @param varType
     * @return
     */
    private static String getElasticSearchMappingType(String varType) {
        String es;
        switch (varType) {
            case "date":
                es = "date";
                break;
            case "double":
                es = "double";
                break;
            case "long":
                es = "long";
                break;
            case "int":
                es = "long";
                break;
            default:
                es = "text";
                break;
        }
        return es;
    }

插入数据

/**
     * 插入一条数据
     * @param item 需要插入数据的实体类对象
     * @param id 该对象的唯一标识(一般为id)
     * @return
     */
    public Boolean addItem(Item item, String id) {
        //单条插入
        IndexRequest request = new IndexRequest(INDEX).id(id).source(beanToMap(item));
        try {
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
            if (response == null) {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    
/**
     * 批量导入数据
     * @return
     */
    @Override
    public Object insertByBulk() {
        //批量插入请求
        BulkRequest bulkRequest = new BulkRequest();
        //从数据库中查询所有数据
        List<Item> list = mapper.selectList(null);
        //遍历构造bulk条件
        for (int i = 0; i < list.size(); i++) {
            Item item = list.get(i);
            //这里必须每次都使用new IndexRequest(index,type),不然只会插入最后一条记录(这样插入不会覆盖已经存在的Id,也就是不能更新)
            bulkRequest.add(new IndexRequest(INDEX).id(item.getId().toString())
                    .source(BeanMap.create(item)));
        }
        BulkResponse responses = null;
        try {
            //客户端返回
            responses = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return JSONObject.toJSON(responses);
    }

删除

/**
     * 删除一条数据
     *
     * @param id
     * @return
     */
    @Override
    public boolean deleteOne(Long id) {
        //构建delete请求
        DeleteRequest request = new DeleteRequest(INDEX).id(id + "");
        DeleteResponse response = null;
        try {
            response = client.delete(request, RequestOptions.DEFAULT);
            System.out.println(response);
            if (response == null) {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();	
        }
        return false;
    }
/**
     * 删除所有数据
     * @return
     */
    @Override
    public Object deleteAll() {
        //构建批量删除请求
        DeleteByQueryRequest request = new DeleteByQueryRequest(INDEX);
        //匹配所有
        request.setQuery(new MatchAllQueryBuilder());
        BulkByScrollResponse response = null;
        try {
            response = client.deleteByQuery(request, RequestOptions.DEFAULT);
            System.out.println(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return JSONObject.toJSON(response);
    }
    
/**
     * 批量删除
     * @param ids
     * @return
     */
    @Override
    public Object deleteList(List<String> ids){
        //构建批量删除请求
        DeleteByQueryRequest request = new DeleteByQueryRequest(INDEX);
        IdsQueryBuilder queryBuilder = new IdsQueryBuilder();
        for(String id : ids){
            queryBuilder.addIds(id);
        }
        //匹配所有
        request.setQuery(queryBuilder);
        BulkByScrollResponse response = null;
        try {
            response = client.deleteByQuery(request, RequestOptions.DEFAULT);
            System.out.println(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return JSONObject.toJSON(response);
    }

修改

public Object updateItem(Item item){
        UpdateRequest updateRequest = new UpdateRequest(INDEX, item.getId().toString());
        updateRequest.doc(beanToMap(item));
        try {
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
            if(updateResponse!=null && updateResponse.status().getStatus() == 200){
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

查询

/**
     * 查询所有
     * @return
     */
public Page<Item> selectAll() {
        List<Item> result = new ArrayList<>();
        Page<Item> page = new Page<>();
        //构造请求
        SearchRequest request = new SearchRequest(INDEX);
        //搜索条件构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //matchAllQuery查询所有
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        //分页(查询所有可不要)
        searchSourceBuilder.from(1);
        searchSourceBuilder.size(1000);
        request.source(searchSourceBuilder);
        try {
            //返回结果封装
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            page.setSize(1000);
            page.setCurrent(1);
            page.setTotal(hits.length);
            for (SearchHit hit : hits) {
                Item item = JSONObject.parseObject(hit.getSourceAsString(), Item.class);
                result.add(item);
            }
            page.setRecords(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return page;
    }


/**
     * 基本查询 带高亮显示
     * @param key 字段名称
     * @param value 查询值
     * @param current 当前页
     * @param size 每页显示条数
     * @return
     */
    public Object selectByKey(String key, String value, Integer current, Integer size) {
        //分页返回结果
        Page<Item> page = new Page<>();
        //构建搜索请求
        SearchRequest request = new SearchRequest(INDEX);
        //搜索条件构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //设置高亮显示
        HighlightBuilder hiBuilder = new HighlightBuilder();
        //设置高亮标签
        hiBuilder.preTags("<a style='color: #e4393c'>");
        hiBuilder.postTags("</a>");
        //高亮字段
        hiBuilder.field(key);
        /**
         * 使用QueryBuilder
         * termQuery("key", obj) 完全匹配
         * termsQuery("key", obj1, obj2..)   一次匹配多个值
         * matchQuery("key", Obj) 单个匹配, field不支持通配符, 前缀具高级特性
         * multiMatchQuery("text", "field1", "field2"..);  匹配多个字段, field有通配符忒行
         */
        //搜索条件 matchQuery
        searchSourceBuilder.query(QueryBuilders.matchQuery(key, value));
        //搜索结果分页 当前页
        searchSourceBuilder.from(current);
        //每页显示条数
        searchSourceBuilder.size(size);
        //高亮显示添加到构造器(不需要高亮显示则不添加)
        searchSourceBuilder.highlighter(hiBuilder);
        //构造器添加到搜索请求
        request.source(searchSourceBuilder);
        //客户端返回信息
        SearchResponse response = null;
        try {
            //请求返回
            response = client.search(request, RequestOptions.DEFAULT);
            if (response == null) {
                return page;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //搜索结果
        SearchHit[] hits = response.getHits().getHits();
        //分页封装
        page.setSize(size);
        page.setCurrent(current);
        page.setTotal(hits.length);
        //所有结果封装
        List<Item> list = new ArrayList<>();
        //高亮字段封装
        for (SearchHit hit : hits) {
            //设置高亮字段
            Item item = JSONObject.parseObject(hit.getSourceAsString(), Item.class);
            String title = hit.getHighlightFields().get(key).getFragments()[0].toString();
            item.setTitle(title);
            //返回结果
            list.add(item);
        }
        page.setRecords(list);
        return page;
    }

/**
     * 查询建议 例如你输入小米,会提示你小米手机、小米电视、小米平板等,前提相应数据应该添加到el中
     * @param value
     * @return
     */
    public Object selectSuggest(String value) {
        List<String> result = new ArrayList<>();
        //搜索条件
        SearchRequest request = new SearchRequest(INDEX);
        //搜索条件构造器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        /**
         * PhraseSuggestionBuilder (org.elasticsearch.search.suggest.phrase)
         * CompletionSuggestionBuilder (org.elasticsearch.search.suggest.completion)
         * TermSuggestionBuilder (org.elasticsearch.search.suggest.term)
         */
        //提示搜索构造器, 使用PhraseSuggestionBuilder 构造器参数为搜索字段
        //PhraseSuggestionBuilder suggestionBuilder = new PhraseSuggestionBuilder("title");
        //搜索内容
        //suggestionBuilder.text(value);
        //提示搜索构造器, 使用TermSuggestionBuilder 构造器参数为搜索字段
        //TermSuggestionBuilder suggestionBuilder = new TermSuggestionBuilder("title");
        //suggestionBuilder.text(value);
        //提示搜索构造器, 使用CompletionSuggestionBuilder 构造器参数为搜索字段
        //如果使用使用CompletionSuggestionBuilder,需要在构建mapping时添加相对应的suggest
        CompletionSuggestionBuilder suggestionBuilder = new CompletionSuggestionBuilder("title.suggest");
        suggestionBuilder.text(value);
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        //添加Suggestion 第一个参数为搜索名称,可以随便打,与下面的搜索结果名称匹配即可 第二个参数为提示搜索构造器
        suggestBuilder.addSuggestion("my-suggest", suggestionBuilder);
        searchSourceBuilder.suggest(suggestBuilder);
        request.source(searchSourceBuilder);
        try {
            //返回内容
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //保存es返回结果 my-suggest需要与上面的搜索结果名称相同
            List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> list = response
                    .getSuggest().getSuggestion("my-suggest").getEntries();
            //返回内容格式可自行打断点查看,这里直接封装使用
            if (list == null) {
                return result;
            } else {
                //转为list保存结果字符串
                for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> e : list) {
                    for (Suggest.Suggestion.Entry.Option option : e) {
                        result.add(option.getText().toString());
                        System.out.println(option.getText().toString());
                    }
                }
            }
            System.out.println(result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

后记

通过以上的基本增删改查我们可以发现,java操作elasticsearch步骤其实是固定的,第一步都是首先获取相对应的连接,之后是添加构造器,最后发送请求即可。因此如果想要熟练的使用java操作elasticsearch,就必须先掌握elasticsearch的RESTful请求。
举个栗子:
如果我们想要使用kibana查询一条数据,我们需要发送的请求为:

POST localhost:9200/item/_update/1

这其中我们知道,首先这是一个post请求,后面是ip与端口号,再后面是索引,再后面是_update更新请求,最后是id=1,因为我们在Java中也是同样。我们首先需要构建一个修改请求:

UpdateRequest updateRequest = new UpdateRequest(INDEX, item.getId().toString());

之后我们添加需要修改的文档doc:

updateRequest.doc(beanToMap(item));

最后我们通过客户端发送请求获得回应即可:

UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);

elasticsearch虽然没有官方的java操作文档,但它的jar包源码命名都非常明朗,对应的增删改查的请求就是基本的IndexRequest,DeleteRequest,UpdateRequest,SearchRequest,批量的增删改查也有相应的queryRequest,只要调用相应的客户端即可。

最后附上项目的码云地址,就是非常基础的一个springboot项目,如果有不懂的小伙伴,可以留言,有更好的意见及建议的也可以告诉我,大家一起进步。
https://gitee.com/hzy_skk/elasticsearch.git

发布了11 篇原创文章 · 获赞 12 · 访问量 4101

猜你喜欢

转载自blog.csdn.net/qq_38991369/article/details/105199319