ElasticSearch 使用 High Level REST Client 实现搜索等功能实战

elasticsearch 7.9.3知识归纳整理(三)之 java实战操作es(rest-high-level java实战操作es 阅读详情

欢迎关注方志朋的博客,回复”666“获面试宝典

ES 全称 Elasticsearch 是一款分布式的全文搜索引擎,在互联网公司中,这款搜索引擎一直被程序员们所推崇。常见的使用场景如ELK日志分析,电商APP的商品推荐,社交APP的同城用户推荐等等。

16ab750bde1f0bf094093af3e41ae052.png

在ES的官网文档中,目前主要提供了两种方式访问,一种叫做Low Client,一种叫做High Level Rest Client。在今天这篇文章中,我们主要介绍High Level Rest Client的使用方式和一些经验分享。

07464a39e041bad254cd9616f9aa0f42.png

ES操作记录

那么我们该如何去通过High Level Rest Client的方式来使用es呢?来看接下来的这块实战案例。

首先我们需要合理的es配置依赖,下边这份是对应的pom文件配置:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>5.6.11</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>5.6.11</version>
</dependency>

在配置中指定了es依赖之后,我们开始定义一个用于测试es增删查改操作的对象类UserSearchRecordPO。

@EsDeclare(index = "user_search")
public class UserSearchRecordPO {
    @Id
    private long id;
    private String username;
    private String searchKeyWord;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSearchKeyWord() {
        return searchKeyWord;
    }
    public void setSearchKeyWord(String searchKeyWord) {
        this.searchKeyWord = searchKeyWord;
    }
}

在UserSearchRecordPO这个对象的头部我用了一个自定义的注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EsDeclare {


    String index() default StringUtils.EMPTY;
}

这个注解用于声明对象所映射的文档具体名称。

奇怪,为什么我们要声明这个注解呢?嘿嘿,别着急,在下边的这个EsDao中就有使用到这个注解的影子了。

在ESDao中,我的整体设计思路是,通过反射获取一个Bean对象是否携带有@EsDeclare注解,如果有,就从注解中提取对应的topic。这部分的核心逻辑如下所示:

/**
 * 获取topic和type
 *
 * @param clz
 * @return
 */
private Pair<String/* topic */, String/* type */> getTopicAndType(Class<?> clz) {
    //通过反射去获取注解中的index值
    EsDeclare esDeclare = clz.getAnnotation(EsDeclare.class);
    if (null == esDeclare || StringUtils.isEmpty(esDeclare.index())) {
        logger.warn("getTopicAndType , esDeclare is illegal , class:{}", clz);
        return null;
    }
    return Pair.of(esDeclare.index(), clz.getSimpleName());
}

这里有几个概念需要和大家简单梳理下,关于index,type,document三个概念的含义:

  • index可以类比为MySQL中的表这个概念,他是一类型数据存储的集合。

  • document其实就是index这个集合里面单条数据的一种称呼,这个概念和MySQL中的行记录比较类似。

  • type是这个代表document属于index中的哪个类别(type),一个index通常会划分为多个type,逻辑上对index中有些许不同的几类数据进行分类:因为一批相同的数据,可能有很多相同的fields,但是还是可能会有一些轻微的不同,可能会有少数fields是不一样的,举个例子,就比如说,商品,可能划分为电子商品,生鲜商品,日化商品,等等。

三者的关系如下图所示:

0c2c6de8f6c6395c38d2212111d3b912.png

好了,现在让我们再来看看基于ES进行CRUD该如何执行操作,具体代码见下边这个类:

package org.idea.es.project.template.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.idea.es.project.template.api.config.EsDeclare;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.stereotype.Repository;
import javax.naming.directory.SearchResult;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
@Repository
public class EsDao<T> {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private RestHighLevelClient restHighLevelClient;


    /**
     * 条件查询
     *
     * @return
     */
    public List<SearchResult> searchByCondition(String index, String type) {
        try {
            SearchRequest searchRequest = new SearchRequest(index).types(type);
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.from(1).size(2);
            searchRequest.source(sourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
            System.out.println(searchResponse);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public T queryOne(SearchSourceBuilder sourceBuilder, Class<T> clz) {
        List<T> resultList = this.queryList(sourceBuilder, clz);
        if (CollectionUtils.isNotEmpty(resultList)) {
            return resultList.get(0);
        }
        return null;
    }
    /**
     * 查询
     *
     * @param sourceBuilder
     * @param clz
     * @return
     */
    public List<T> queryList(SearchSourceBuilder sourceBuilder, Class<T> clz) {
        Pair<String, String> topicAndType = getTopicAndType(clz);
        if (null == topicAndType) {
            logger.warn("query , null topicAndType , clz:{}", clz);
            return Collections.emptyList();
        }
        Field idField = getIdField(clz);
        if (null == idField) {
            logger.warn("query , null id field , clz:{}", clz);
            return Collections.emptyList();
        }
        try {
            SearchRequest searchRequest = new SearchRequest(topicAndType.getLeft()).types(topicAndType.getRight()).source(sourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest);
            SearchHit[] hits = searchResponse.getHits().getHits();
            List<T> result = Lists.newArrayListWithCapacity(hits.length);
            for (SearchHit hit : hits) {
                T obj = JSON.parseObject(hit.getSourceAsString(), clz, Feature.AllowISO8601DateFormat);
                Object idObj = FieldUtils.readField(idField, obj, true);
                if (null == idObj) {
                    FieldUtils.writeField(idField, obj, hit.getId(), true);
                }
                result.add(obj);
            }
            return result;
        } catch (Exception e) {
            logger.warn("query , e:{}", e.getMessage());
            return Collections.emptyList();
        }
    }
    /**
     * 插入或者更新,根据id字段来判断是否已有数据
     *
     * @param po
     */
    public void saveOrUpdate(T po) {
        if (po == null) {
            throw new IllegalArgumentException("po can not be null!");
        }
        try {
            Pair<String/*topic*/, String /*type*/> pair = getTopicAndType(po.getClass());
            Field idField = getIdField(po.getClass());
            idField.setAccessible(true);
            Object idObj = idField.get(po);
            IndexRequest indexRequest = new IndexRequest(pair.getLeft(), pair.getRight(), idObj == null ? null : idObj.toString());
            IndexResponse indexResponse = restHighLevelClient.index(indexRequest.source(JSON.toJSONStringWithDateFormat(po, "yyyy-MM-dd'T'HH:mm:ss+08:00"), XContentType.JSON));
            System.out.println(indexResponse);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 删除单个元素
     *
     * @param po
     */
    public void deleteOne(T po) {
        if (po == null) {
            throw new IllegalArgumentException("po can not be null!");
        }
        try {
            Pair<String/*index*/, String /*type*/> pair = getTopicAndType(po.getClass());
            Field idField = getIdField(po.getClass());
            idField.setAccessible(true);
            Object idObj = idField.get(po);
            DeleteRequest deleteRequest = new DeleteRequest(pair.getLeft(), pair.getRight(), idObj == null ? null : idObj.toString());
            restHighLevelClient.delete(deleteRequest);
        } catch (IllegalAccessException | IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 根据id删除
     *
     * @param index
     * @param type
     * @param _id
     */
    public void deleteBy_Id(String index,String type,String _id){
        DeleteRequest deleteRequest = new DeleteRequest(index, type, _id);
        try {
            restHighLevelClient.delete(deleteRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取id的域
     *
     * @param clz
     * @return
     */
    public Field getIdField(Class<?> clz) {
        List<Field> listWithAnnotation = FieldUtils.getFieldsListWithAnnotation(clz, Id.class);
        if (listWithAnnotation.size() != 1) {
            logger.warn("getIdField , id is illeage , class:{}", clz);
            return null;
        }
        return listWithAnnotation.get(0);
    }
    /**
     * 获取topic和type
     *
     * @param clz
     * @return
     */
    private Pair<String/* topic */, String/* type */> getTopicAndType(Class<?> clz) {
        EsDeclare esDeclare = clz.getAnnotation(EsDeclare.class);
        if (null == esDeclare || StringUtils.isEmpty(esDeclare.index())) {
            logger.warn("getTopicAndType , esDeclare is illegal , class:{}", clz);
            return null;
        }
        return Pair.of(esDeclare.index(), clz.getSimpleName());
    }
}

这里需要注意下saveOrUpdate函数中,它会根据传入的对象参数中带有 @Id 注解的字段值去判断是否已经有具体数据,如果有的话则只做更新操作,反之就是插入操作。这一点就有点类似于MySQL的insertOrUpdate方法。

接下来就是对于我们所定义的对象实现crud操作了,下边是对应的service接口和相关的实现类,这部分的代码如下所示:

首先是接口部分的定义:

package org.idea.es.project.template.api.service;


import org.idea.es.project.template.api.bo.UserSearchRecordPO;


import javax.naming.directory.SearchResult;
import java.util.List;


public interface IUserSearchRecordService {


    /**
     * 条件查询
     *
     * @param index
     * @param type
     * @return
     */
    List<SearchResult> searchByCondition(String index,String type);


    /**
     * 查询操作
     *
     * @param userSearchRecordPO
     * @return
     */
    UserSearchRecordPO queryByParam(UserSearchRecordPO userSearchRecordPO);


    /**
     * 写入记录
     *
     * @return
     */
    UserSearchRecordPO saveOrUpdate();


    /**
     * 删除单个元素
     */
    void deleteOne(UserSearchRecordPO userSearchRecordPO);




}

接着是对应的service实现类部分:

package org.idea.es.project.template.api.service.impl;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.idea.es.project.template.api.bo.UserSearchRecordPO;
import org.idea.es.project.template.api.service.EsDao;
import org.idea.es.project.template.api.service.IUserSearchRecordService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.naming.directory.SearchResult;
import java.util.List;
@Service
public class UserSearchRecordServiceImpl implements IUserSearchRecordService {
    @Resource
    private EsDao<UserSearchRecordPO> esDao;
    @Override
    public List<SearchResult> searchByCondition(String index,String type) {
        return esDao.searchByCondition(index,type);
    }
    @Override
    public UserSearchRecordPO queryByParam(UserSearchRecordPO userSearchRecordPO) {
        try {
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                    .filter(QueryBuilders.termQuery("id", userSearchRecordPO.getId()));
            SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource().query(queryBuilder).from(0).size(1);
            return esDao.queryOne(sourceBuilder, UserSearchRecordPO.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public UserSearchRecordPO saveOrUpdate() {
        UserSearchRecordPO userSearchRecordPO = new UserSearchRecordPO();
        userSearchRecordPO.setId(System.currentTimeMillis());
        userSearchRecordPO.setUsername("idea");
        userSearchRecordPO.setSearchKeyWord("key-word");
        esDao.saveOrUpdate(userSearchRecordPO);
        return userSearchRecordPO;
    }
    @Override
    public void deleteOne(UserSearchRecordPO userSearchRecordPO) {
        esDao.deleteOne(userSearchRecordPO);
    }
}

最后是供外界调用的controller方法:

package org.idea.es.project.template.api.controller;
import org.idea.es.project.template.api.bo.UserSearchRecordPO;
import org.idea.es.project.template.api.service.IUserSearchRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/user-search-record")
public class UserSearchRecordController {
    @Autowired
    private IUserSearchRecordService iUserSearchRecordService;


    @GetMapping(value = "/save-or-update")
    public boolean saveOrUpdate(){
        iUserSearchRecordService.saveOrUpdate();
        System.out.println("success");
        return true;
    }


    @GetMapping(value = "/query-by-param")
    public UserSearchRecordPO queryByParam(Long id){
        UserSearchRecordPO userSearchRecordPO = new UserSearchRecordPO();
        userSearchRecordPO.setId(id);
        return iUserSearchRecordService.queryByParam(userSearchRecordPO);
    }


    @GetMapping(value = "/delete-one")
    public boolean deleteOne(long id){
        UserSearchRecordPO userSearchRecordPO = new UserSearchRecordPO();
        userSearchRecordPO.setId(id);
        iUserSearchRecordService.deleteOne(userSearchRecordPO);
        System.out.println("success");
        return true;
    }
}

将SpringBoot启动之后,分别触发这些http请求接口,就可以验证crud操作的正确性了。

好了。

另外,在测试es的时候,我们可以通过使用 elasticsearch-head 这款插件去查看es内部的数据是否符合我们的预期。

66d396ba94988baa9b8c8f0cf91f9d01.png

整体来说,通过 elasticsearch-rest-high-level-client 去访问es还是比较容易上手的。另外在实际业务场景中,如果遇到一些非常复杂的条件查询功能的话,自Elasticsearch 5.x之后,我们其实还可以通过使用painless脚本去操作es,可以看出es的功能在变得越来越强大了。

热门内容:

 

603f2df341c294d52323b3d2d3cb7473.jpeg


 
最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ

ElasticSearch RestHighLevelClient 教程(一)通用操作 前言 ​ 项目中使用ElasticSearch作为搜索引擎。而ES的环境搭建自然是十分简单,且本身就适应于分布式环境,因此这块就不多赘述。而其本身特性和查询语句这篇博文不会介绍,如果有机会会深入介绍。 ​ 所以这篇博文主要还是介绍Java客户端中如何使用查询搜索引擎中的数据。而使用Java客户端是官方新推出的RestHighLevelClient使用Http连接查询结果。但是网上 阅读详情

相关推荐

ElasticSearch——全文搜索引擎

一、为什么需要全文搜索引擎? 首先先来了解一个概念,在我们生活中一般来说有两种数据:结构化数据、非结构化数据 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。 非结构化数据: 非结构化数据又可称为全文数据,指不定长或无固定格式的数据,如邮件,word文档等。 对于结构化数据我们可以直接从数据库查询,而对于非结构化的数据此时就要用到全文检索的方式查询,比如对一个文档或者网页中的内容文本每个词建立一个索引,把这些非结构化的变成结构化的方式然后来查询。 全文搜索引擎百度百科定义:全文搜索引擎是目前

hl404的博客 1万+

elasticsearch学习(七):es客户端RestHighLevelClient

Java Low Level REST Client:ES官方的低级客户端。低级别的客户端通过http与Elasticearch集群通信。Java High Level REST Client:ES官方的高级客户端。基于上面的低级客户端,也是通过HTTP与ES集群进行通信。它提供了更多的接口。客户端(Client) Jar包的版本尽量不要大于Elasticsearch本体的版本,否则可能出现客户端中使用的某些API在Elasticsearch中不支持。

weixin_40482816的博客 4万+

SpringBoot工程整合ElasticSearch(通过Rest-High-Level-Client)并实现较复杂的查询

上一篇博客提到,REST Client的方式整合的项目实现复杂的查询比较麻烦,实现的方法是需要我们自己根据ES的语法写出符合语法的bean来,然后把这个bean发送http请求给服务端,这种方法也是我以前参与的一个项目的做法。但是ES本身是提供了java操作的api的,我们直接调用具体的类就能实现而不需要自己创建bean了。 上一篇博客只是实现了ES单节点根据id来实现简单的增删改查,上一篇博客的...

yang1519784522的博客 5781

Elasticsearch——RestHighLevelClient

介绍 java rest client有两个实现类,分别是RestClientRestHighLevelClient。前者是一个低级客户端,通过Http与elasticsearch集群进行通信,可以做到 负载均衡、故障转移、持久化链接、自动发现集群节点等功能,同时支持所有elasticsearch版本,但是需要自己对请求和相应做编解码(自己写JSON);后者是一个高级客户端,对增删改差进行了封...

qin_weilong的博客 4010

Elasticsearch rest-high-level-client 基本操作

Elasticsearch rest-high-level-client 基本操作 本篇主要讲解一下 rest-high-level-client 去操作 Elasticsearch , 虽然这个客户端在后续版本中会慢慢淘汰,但是目前大部分公司中使用Elasticsearch 版本都是6.x 所以这个客户端还是有一定的了解 前置准备 准备一个SpringBoot环境 2.2.11 版本 准备一个Elasticsearch 环境 我这里是8.x版本 引入依赖 elasticsearch-rest

qq_34285557的博客 5379

SpringBoot集成Elasticsearch | Java High Level Rest Client 方式

SpringBoot集成Elasticsearch 7.x | Java High Level Rest Client 方式

qq_20236937的博客 2909

elasticsearch high level API 使用示例

<dependencies> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</arti...

koukan3的博客 5481

elasticsearch-rest-high-level-client操作elasticsearch

文章目录摘要依赖yml配置连接配置索引结构普通常见非嵌套结构的CRUD新增更新(根据id更新)更新(高级,根据搜索条件更新,采用无痛painless脚本)删除删除(根据搜索条件删除)搜索高亮(普通高亮,空格多条件搜索)nested嵌套类型高级painless CRUDnested嵌套插入nested嵌套删除nested嵌套更新nested嵌套搜索结果高亮 摘要 闲的无聊,于是写了这一篇爽文,米娜桑可直接用,除非几乎不可能有bug,有bug当我没说(doge) QA:无想的一刀欧为啥不用springboot

UnicornRe_xiabin_无聊的时候,喜欢去超市买一大瓶纯牛奶,喝个精光 5929

ElasticsearchJava 客户端集成 High Level REST Client 实战

Java 模块系统(JPMS)迁移指南 遗留系统模块化改造实践

bbTPz23Xt的博客 1136

Elasticsearch Java High-Level REST Client 入门教程

Elasticsearch Java High-Level REST Client 入门教程 Elasticsearch是开源可扩展的全文检索和分析引擎,用于实时存储、搜索、分析海量信息。Java REST client是其官方提供的客户端,提供特定方法API并对请求和响应封装。本文聚焦如何使用High-Level REST Client。 1 环境准备 1.1 依赖 springboot 默认依...

neweastsun的专栏 4868

Springboot整合Elasticsearch(High-level-Client)

前言 通过学习Elasticsearch一小段时间来稍微认识了一点ES的体系架构。发现ES最大的坑就是版本兼容性问题了—在整合Springboot也不例外,但是,有一种方式能较好的解决—通过restclient。 项目github地址springboot_elasticsearch求star 内容 当前springboot整合ElasticSearch的方法主体分为2大种——restcl...

bigsai 1万+

SpringBoot高级篇】SpringBoot集成Elasticsearch搜索引擎

SpringBoot整合Elasticsearch1. 什么是Elasticsearch?2. 安装并运行Elasticsearch2.1 拉取镜像2.2 启动镜像3. 安装kibana3.1 拉取kibana镜像3.2 启动kibana镜像4. Elasticsearch基本概念索引(Index)文档(Document)类型(Type)文档元数据(Document metadata)字段(Fields)4.1 全文搜索(Full-text Search)倒排索引(Inverted Index)节点 &am

输入技术、输出想法 7361

十三、ElasticSearch——ES Client API之一(Java High Level REST Client

1 ES Client 简介 ES是一个服务,采用C/S结构 REST API ,端口 9200 Transport 连接 端口 9300 ES提供了多种编程语言客户 Client api 学习链接 https://www.elastic.co/guide/en/elasticsearch/client/index.html ES提供了两个JAVA REST client 1)Java...

m0_38143867的博客 4035

Elasticsearch教程(4) High Level REST Client API 查询 聚合 分组

ES High Level REST Client API 查询 聚合1 准备数据1.1 插入测试数据2 Maven引入ES3 创建Client4 查询API4.1 根据id查询单条数据4.2 根据多个id查询4.3 根据条件分页查询4.4 count4.5 max5 terms聚合5.1 根据1个字段group by5.2 根据多个字段group by 1 准备数据 首先启动好的ES6.8服务和...

Java Spring Elasticsearch 8280

elasticsearch实战Java High Level Rest ClientRestHighLevelClient)客户端操作elasticsearch

RestHighLevelClient操作elasticsearch

东风麦芽糖 2375
上一篇: 从实现原理讲,Nacos 为什么这么强
下一篇: 国内怎么就做不出 JetBrains 那样的产品?
公众号:方志朋
博客等级 码龄5年 307粉丝 · 94原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值