Elasticsearch Java客户端

Api Client 选择

客户端优点缺点说明
Java Low Level Rest Client与ES版本之间没有关系,适用于作为所有版本ES的客户端
Java High Level Rest Client使用最多使用需与ES版本保持一致基于Low Level Rest Client,注意:7.15版本之后将被弃用
TransportClient使用Transport 接口进行通信,能够使用ES集群中的一些特性,性能最好JAR包版本需与ES集群版本一致,ES集群升级,客户端也跟着升级到相同版本过时产品,7版本之后不再支持
Elasticsearch Java API Client最新的es客户端文档少

最稳方案:Java High Level Rest Client

官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html

spring starter官方依赖版本信息:https://docs.spring.io/spring-data/elasticsearch/docs/3.2.4.RELEASE/reference/html/#elasticsearch.versions

spring 官方对es的支持:https://docs.spring.io/spring-data/elasticsearch/docs/3.2.4.RELEASE/reference/html/#reference

ElasticSearch组件RestHighLevelClient用法详解:https://juejin.cn/post/6921479614304878599

注意:因为 Java High Level Rest Client 在使用的时候版本必须高度统一,springboot提供的starter:spring-data-elasticsearch 依赖版本是跟随springboot走,我们如果想要统一 es服务、分词器、api 客户端,最好是不使用官方 starter ,手动导入 Java High Level Rest Client 的pom依赖;

以下文档基于es 7.7.0,只给出关键的pom,部分api根据版本略有不同

				
		<properties>
        <elasticsearch.varsion>7.7.0</elasticsearch.varsion>
    </properties>
				......
				<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

创建客户端

@Configuration
public class HighLevelClientConfig {

    @Bean(destroyMethod = "close")
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("ip:xxx.xxx.xxx.xxx", 9200, "http")));
        return client;
    }
}

RestClient.builder 的参数是个可变参数,集群模式下,增加节点配置即可(多个new HttpHost)

使用客户端的时候注入即可,下文中所有esClient都来自这个类

索引操作

添加索引 - 方式一

特点:方便,不用每次设置分词器

private static Map<String, Object> fieldAttr(EsFieldTypeEnum type) {
        Map<String, Object> result = new HashMap<>();
        result.put("type", type.k);
        if ("text".equals(type.k)) {
            /*
            插入文档时,将text类型的字段做分词然后插入倒排索引
            ik_max_word:最细粒度分词(文档中的字可以反复出现)
            ik_smart:最粗粒度分词(每个字只会出现一次)
             */
            result.put("analyzer", "ik_max_word");
            result.put("search_analyzer", "ik_max_word");
            /*
            存储内容过长导致报错:Exceeding maximum length of field in elasticsearch - error in kibana
            可以开启下面的设置
             */
            // result.put("ignore_above", "");

            result.put("term_vector", "with_positions_offsets");
        }
        return result;
    }

public void createIndexM1() {

        try {
            /*
            设置索引名称
            */
            String IndexName = "test_index_create_1";
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(IndexName);
            /*
            设置字段以及字段类型
            */
            Map<String, Object> properties = new HashMap<>();
            properties.put("id", fieldAttr(EsFieldTypeEnum.FIELD_INTEGER));
            properties.put("title", fieldAttr(EsFieldTypeEnum.FIELD_TEXT));
            properties.put("tag", fieldAttr(EsFieldTypeEnum.FIELD_KEYWORD));
            properties.put("content", fieldAttr(EsFieldTypeEnum.FIELD_TEXT));
            /*
            设置properties
            */
            Map<String, Object> mapping = new HashMap<>();
            mapping.put("properties", properties);
            /*
            设置mapping
            mapping的结构:mapping : { properties : { fields } }
            */
            createIndexRequest.mapping(mapping);
            /*
            设置分片
             */
            createIndexRequest.settings(
                    Settings.builder()
                            .put("index.number_of_shards", 3)
                            .put("index.number_of_replicas", 2));
            /*
            执行创建请求
            */
            CreateIndexResponse createIndexResponse = esClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            /*
            判断成功与否
            */
            boolean acknowledged = createIndexResponse.isAcknowledged();
            log.info("acknowledged:{}", acknowledged);
            boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
            log.info("shardsAcknowledged:{}", shardsAcknowledged);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

添加索引二

特点:和请求体查询高度相似,灵活度高,方便控制每个字段的属性,但是写法比较繁琐

public void createIndexM2() {
        try {
            /*
            设置索引名称
             */
            String indexName = "test_index_create_2";
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);

            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject()
                    .startObject("mappings").startObject("properties")
                        .startObject("id")
                            .field("type", EsFieldTypeEnum.FIELD_INTEGER.k)
                        .endObject()
                        .startObject("title")
                            .field("type", EsFieldTypeEnum.FIELD_TEXT.k)
                            .field("analyzer","ik_max_word")
                            .field("search_analyzer","ik_smart")
                        .endObject()
                        .startObject("tag")
                            .field("type", EsFieldTypeEnum.FIELD_KEYWORD.k)
                        .endObject()
                        .startObject("content")
                            .field("type", EsFieldTypeEnum.FIELD_TEXT.k)
                            .field("analyzer","ik_max_word")
                            .field("search_analyzer","ik_smart")
                        .endObject()
                    .endObject().endObject()
                    .startObject("settings")
                        .field("number_of_shards",3)
                        .field("number_of_replicas",1)
                    .endObject()
                    .endObject();
            createIndexRequest.source(builder);
            CreateIndexResponse createIndexResponse = esClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            /*
            判断成功与否
            */
            boolean acknowledged = createIndexResponse.isAcknowledged();
            log.info("acknowledged:{}", acknowledged);
            boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
            log.info("shardsAcknowledged:{}", shardsAcknowledged);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

文档操作

CRUD

插入文档

public void insertDoc() {
        try {
            /*
            准备数据
            */
            String indexName = "test_index_create_2";
            int id = 12;
            Article article = new Article();
            article.setId(id);
            article.setTitle("java是世界上最好的语言");
            article.setTag("语言");
            article.setContent("程序员们放下手里的鼠标和键盘,抄起凳子打了起来");
            /*
            创建操作索引的request对象,设置目标索引
             */
            IndexRequest indexRequest = new IndexRequest(indexName);
            /*
            请求数据填充
            id:建议id和数据库主键保持同步
            source:文档数据
             */
            indexRequest.id(String.valueOf(id));
            indexRequest.source(JSON.toJSONString(article), XContentType.JSON);
            /*
            插入文档依赖:RestHighLevelClient.index 方法
             */
            IndexResponse indexResponse = esClient.index(indexRequest, RequestOptions.DEFAULT);
            log.info("indexResponse: " + JSON.toJSONString(indexResponse));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

获取文档

public void getDoc() {
        try {
            /*
            指定要查询的索引名称和文档id
             */
            String indexName = "test_index_create_2";
            String id = "123";
            /*
            创建查询request对象
             */
            GetRequest request = new GetRequest(indexName, id);
            /*
            获取文档依赖:RestHighLevelClient.get 方法
             */
            GetResponse response = esClient.get(request, RequestOptions.DEFAULT);
            if (response.isExists()) {
                String sourceAsString = response.getSourceAsString();
                log.info("source: " + sourceAsString);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

删除文档

public void deleteDoc() {
        try {
            /*
            指定要删除的索引名称和文档id
             */
            String indexName = "test_index_create_2";
            String id = "123";
            /*
            创建删除request对象
             */
            DeleteRequest request = new DeleteRequest(indexName, id);
            /*
            删除文档依赖:RestHighLevelClient.delete 方法
             */
            DeleteResponse response = esClient.delete(request, RequestOptions.DEFAULT);
            if (response.status() == RestStatus.OK) {
                log.info("删除成功");
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

修改文档

public void updateDoc() {
        try {
            /*
            指定要修改的索引名称和文档id
             */
            String indexName = "test_index_create_2";
            String id = "12";
            /*
            注意:这里的修改并不会覆盖原有数据,只是修改了部分数据
             */
            Article article = new Article();
            article.setTitle("java是世界上最好的语言-update");
            article.setContent("程序员们放下手里的鼠标和键盘,抄起凳子打了起来-update");
            /*
            创建 修改request对象
             */
            UpdateRequest request = new UpdateRequest(indexName, id);
            /*
            请求数据填充
             */
            request.doc(JSON.toJSONString(article), XContentType.JSON);
            /*
            修改文档依赖:RestHighLevelClient.update 方法
             */
            UpdateResponse response = esClient.update(request, RequestOptions.DEFAULT);
            log.info("response: " + JSON.toJSONString(response));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

文档搜索

分页搜索

public void pageSearch() {
        try {
            /*
            设置目标索引
             */
            String indexName = "test_index_create_2";
            /*
            创建 搜索request对象
             */
            SearchRequest request = new SearchRequest(indexName);
            /*
            构建搜索条件
             */
            SearchSourceBuilder builder = new SearchSourceBuilder();
            builder.from(0); // 从第几条开始
            builder.size(10); // 查几个
            builder.sort("id", SortOrder.DESC); // 根据什么排序
            builder.query(QueryBuilders.matchAllQuery()); // 查询全部
            /*
            请求数据填充
             */
            request.source(builder);
            /*
            搜索文档依赖:RestHighLevelClient.search 方法
             */
            SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
            if (response.status() != RestStatus.OK) {
                throw new Exception("搜索错误");
            }
            SearchHits hits = response.getHits();
            log.info("数据总数:" + hits.getTotalHits());
            log.info("搜索用时:" + response.getTook());
            log.info("搜索结果:");
            for (SearchHit hit : hits) {
                log.info("字符串" + hit.getSourceAsString());
                log.info("java对象:" + JSON.toJavaObject(JSON.parseObject(hit.getSourceAsString()), Article.class));
                log.info("map对象" + hit.getSourceAsMap().toString());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

简单搜索

public void simpleSearch() {
        try {
            /*
            创建 搜索request,设置目标索引
             */
            SearchRequest request = new SearchRequest("test_index_create_2");
            /*
            构建搜索条件
             */
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.size(10);
            /*
            单个字段匹配
             */
            MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "update");
            /*
            完全匹配
             */
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("content", "update");
            /*
            复合查询
             */
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.must(matchQueryBuilder).must(termQueryBuilder);
            /*
            请求数据填充
             */
            request.source(searchSourceBuilder.query(boolQueryBuilder));
            SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
            printResp(response);
        }catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

常用的条件构造器:

QueryBuilders.termQuery(“key”, obj) 完全匹配
QueryBuilders.termsQuery(“key”, obj1, obj2…) 一次匹配多个值
QueryBuilders.matchQuery(“key”, Obj) 单个匹配, field不支持通配符, 前缀具高级特性
QueryBuilders.multiMatchQuery(“text”, “field1”, “field2”…) 匹配多个字段, field有通配符忒行
QueryBuilders.matchAllQuery() 匹配所有文件

复合条件构造器:

QueryBuilders.boolQuery()

​ .must(termQuery(“content”, “test1”)) 相当于 与 & =
​ .mustNot(termQuery(“content”, “test2”)) 相当于 非 ~ !=
​ .should(termQuery(“content”, “test3”)) 相当于 或 | or
​ .filter(termQuery(“content”, “test5”)); 过滤

高亮搜索

public void highLightSearch() {
        try {
            /*
            单字段匹配
             */
            MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "java");
            /*
            高亮设置
             */
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.preTags("<span style=\"color:red\">");
            highlightBuilder.postTags("</span>");
            highlightBuilder.fields().add(new HighlightBuilder.Field("title"));
            /*
            构建搜索条件
             */
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(matchQueryBuilder);
            searchSourceBuilder.highlighter(highlightBuilder);

            /*
            创建 搜索request,设置目标索引
            */
            SearchRequest request = new SearchRequest("test_index_create_2");
            request.source(searchSourceBuilder);

            SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
            log.info(JSON.toJSONString(response));
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("title");
                Text[] fragments = highlightField.getFragments();
                for (Text fragment : fragments) {
                    log.info("高亮字段:" + fragment.toString());
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值