【Java 实战】通过ElasticSearch实现全局搜索功能

news2024/11/30 0:37:30

前言

在电商项目中,我们经常会使用到全局搜索来查询自己想要购买的商品,而商品的数量非常多,而且分类繁杂。

面对这样复杂的搜索业务和数据量,使用传统数据库搜索就显得力不从心,一般我们都会使用全文检索技术,比如Solr,Elasticsearch


一、环境搭建

Windows环境:
参考前文:Elasticsearch 安装及启动【Windows】、RabbitMQ安装和使用
Linux环境:
参考前文:Elasticsearch 安装及启动【Linux】、Linux安装RabbitMq

这里为了方便演示,我们统一在windows环境下安装

二、使用步骤

1.引入依赖

<!-- 引入ES依赖 -->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.12.0</version>
</dependency>

2.环境配置

修改配置文件application.yml如下:

创建ElasticSearchConfig配置类

package com.local.springboot.springbootcommon.config.es;

import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
    private String host;
    private int port;

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

3.商品同步至es

全局搜索是在Elasticsearch中搜索商品信息,所以我们需要将商品信息同步至Elasticsearch中,在商品修改、新增、删除时通过RabbitMQ异步处理Elasticsearch中的商品信息

创建商品索引

这里直接在测试类中添加goods索引,mapping创建前端商品列表需要展示的字段

@Test
void createIndex() throws IOException {
    // 创建一个索引请求
    CreateIndexRequest indexRequest = new CreateIndexRequest("goods");
    // 创建一个Settings
    Settings.Builder settings = Settings.builder();
    settings.put("number_of_shards", "3");  // 设置三个分片
    settings.put("number_of_replicas", "1");  // 设置一个备份
    // 把settings设置给request对象
    indexRequest.settings(settings);
    // 创建一个mappings
    XContentBuilder mappings = JsonXContent.contentBuilder();
    mappings
            .startObject()
                .startObject("properties")
                    .startObject("skuId")
                        .field("type", "text")
                    .endObject()
                    .startObject("skuName")
                        .field("type", "text")
                        .field("analyzer", "ik_max_word")
                    .endObject()
                    .startObject("productName")
                        .field("type", "text")
                        .field("analyzer", "ik_max_word")
                    .endObject()
                    .startObject("num")
                        .field("type", "integer")
                    .endObject()
                    .startObject("sellPrice")
                        .field("type", "double")
                    .endObject()
                    .startObject("coverUrl")
                        .field("type", "keyword")
                    .endObject()
                    .startObject("creatTime")
                        .field("type", "date")
                        .field("format", "yyyy-MM-dd")
                    .endObject()
                .endObject()
            .endObject();
    // 把mappings设置给index
    indexRequest.mapping(mappings);

    // 客户端开始发送请求
    CreateIndexResponse response = client.indices().create(indexRequest, RequestOptions.DEFAULT);
    System.out.println(response.toString());
}

执行完进行查询,索引创建成功

es添加商品

将商品信息添加到Elasticsearch中,方法IndexRequest.source()

ElasticSearchServiceImpl.java

@Override
public void addGoods(ItemEntity itemEntity) throws IOException {
    // 获取操作索引的对象
    IndexRequest request = new IndexRequest(ElasticConstant.GOODS)
            .id(itemEntity.getSkuId())
            .source(JSON.toJSONString(itemEntity), XContentType.JSON);
    client.index(request, RequestOptions.DEFAULT);
}

商品同步

商品做修改操作时,将商品信息同步至Elasticsearch中

商品添加处理

ItemInfoServiceImpl.java

@Override
public ApiResponse saveItem(ItemEntity itemEntity) {
    if (itemEntity != null) {
        String id = itemEntity.getSkuId();
        if (StringUtils.isNotBlank(id)) {
            ItemEntity entity = getById(id);
            if (entity != null) {
                BeanUtils.copyProperties(itemEntity, entity);
                updateById(entity);
            }
        } else {
            EntityUtil.initEntity(itemEntity);
            itemEntity.setSkuId(IdWorker.get32UUID());
            save(itemEntity);
        }
    }
    // 同步商品信息
    rabbitTemplate.convertAndSend(RabbitMQConstant.EXCHANGE_GOODS_EXCHANGE, RabbitMQConstant.ROUTING_KEY_GOODS_EVENT, itemEntity);
    return ApiResponse.ok();
}

RabbitMQ 处理

参考前文:SpringBoot —— 整合RabbitMQ常见问题及解决方案
RabbitMQ配置

监听队列

测试

添加商品接口

package com.local.springboot.springbootapi.api.item;

import com.local.springboot.springbootcommon.reponse.ApiResponse;
import com.local.springboot.springbootdao.entity.ItemEntity;
import com.local.springboot.springbootservice.service.item.ItemInfoService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RequestMapping("api/item/")
@RestController
public class ItemApiController {

    @Resource
    private ItemInfoService itemInfoService;

    @RequestMapping(value = "/addItem", produces = "application/json;charset=UTF-8")
    public ApiResponse addItem(@RequestBody ItemEntity itemEntity) {
        return itemInfoService.saveItem(itemEntity);
    }
}

调用接口,然后查询es数据,同步成功


ps:如果Elasticsearch是中途搭建的,可以写个脚本方法查询所有商品添加到Elasticsearch中

商品查询

ElasticSearchServiceImpl.java

@Override
public ApiResponse selectItems(String keyword, Integer sort) {
    log.info("es查询商品信息参数:{},{}", keyword, sort);
    List<ItemEntity> entities = new ArrayList<>();
    SearchRequest request = new SearchRequest(ElasticConstant.GOODS);
    // 设置查询条件 productName、skuName 匹配keyword
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    // 设置排序
    if (sort ==1) {
        searchSourceBuilder.sort("sellPrice", SortOrder.ASC);
    }

    // keyword为空,查询全部
    if (StringUtils.isBlank(keyword)) {
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
    } else {
        searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword, "skuName", "productName"));
        // 设置高亮属性
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("skuName", 30);
        highlightBuilder.preTags("<font color = 'red'>");
        highlightBuilder.postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);
    }
    request.source(searchSourceBuilder);
    try {
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        for (SearchHit hit : response.getHits().getHits()) {
            ItemEntity item = new ItemEntity();
            Map<String, Object> sourceMap = hit.getSourceAsMap();
            BeanUtils.populate(item, sourceMap);
            // 获取高亮字段的信息, 但是有些数据是没有高亮信息的
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField skuNameHigh = highlightFields.get("skuName");
            if (skuNameHigh != null) {
                item.setSkuName(skuNameHigh.getFragments()[0].toString());
            }
            entities.add(item);
        }
    } catch (Exception e) {
        log.info("es查询商品信息异常:{}", e.getMessage());
        return ApiResponse.error("es查询商品信息异常:" + e.getMessage());
    }
    return ApiResponse.ok(entities);
}

ElasticSearch为搜索结果提供高亮、排序、分页等功能

比如搜索手机,查询匹配字段有手机两个字,查询结果手机就会出行高亮效果;排序我们可以通过传入字段的值进行相应匹配排序;分页这里就不说了,后续会单独写一篇文章总结ElasticSearch的分页查询

查询测试

package com.local.springboot.springbootapi.api.search;

import com.local.springboot.springbootcommon.reponse.ApiResponse;
import com.local.springboot.springbootservice.service.search.ElasticSearchService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RequestMapping("api/elastic/")
@RestController
public class ElasticSearchController {

    @Resource
    private ElasticSearchService elasticSearchService;

    @RequestMapping(value = "/selectItems", produces = "application/json;charset=UTF-8")
    public ApiResponse selectItems(String keyword, Integer sort) {
        return elasticSearchService.selectItems(keyword, sort);
    }
}

接口测试

查询成功

商品删除

单个商品删除

@Test
void delete() throws IOException {
    DeleteByQueryRequest request = new DeleteByQueryRequest("goods");
    request.setQuery(QueryBuilders.multiMatchQuery("d15e00ad1be60272d81ec79dfc01d4f1","skuId"));
    BulkByScrollResponse response = client.deleteByQuery(request, RequestOptions.DEFAULT);
    // 返回结果 true
    System.out.println(response);
}

清除es中的所有商品数据

@Test
void clear() throws IOException {
    DeleteByQueryRequest request = new DeleteByQueryRequest("goods");
    request.setQuery(QueryBuilders.matchAllQuery());
    BulkByScrollResponse response = client.deleteByQuery(request, RequestOptions.DEFAULT);
    // 返回结果 true
    System.out.println(response);
}

总结

以上就是今天要讲的内容,本文仅仅简单介绍了通过ElasticSearch在java中实现入门级全局搜索功能,后续会深入ElasticSearch搜索的其他功能

创作不易,关注💖、点赞👍、收藏🎉就是对作者最大的鼓励👏,欢迎在下方评论留言🧐

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85013.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vite的原理

背景 这里的背景介绍会从与Vite紧密相关的两个概念的发展史说起&#xff0c;一个是JavaScript的模块化标准&#xff0c;另一个是前端构建工具。 共存的模块化标准 为什么JavaScript会有多种共存的模块化标准&#xff1f;因为js在设计之初并没有模块化的概念&#xff0c;随着前…

js将后端返回的文件流导出为excel,并自定义下载文件名

一. 需求 后台管理系统&#xff0c;常会出现导出excel表格功能&#xff1b;点击导出按钮&#xff0c;请求后端接口&#xff0c;需要将接口返回的文件流导出为excel&#xff0c;实现前端下载excel文件。 但是&#xff0c;返回的数据获取 response 时出现乱码&#xff0c;如图&a…

CSS复合样式id选择器class选择器

1、CSS复合样式 单一样式&#xff1a;一个CSS属性只控制一种样式&#xff0c;叫做单一样式。复合样式&#xff1a; 一个CSS属性控制多种样式&#xff0c;叫做复合样式。 background、border、font 无序&#xff1a;background、border有序&#xff1a;font&#xff08;先size、…

用JS实现:图片压缩、图片加密

本文将用JavaScript实现两个颇有技术含量的功能&#xff1a;图片压缩、图片加密。 最终效果&#xff1a;可实现将任意图片加密、压缩&#xff0c;并保存到一个独立的html页面中&#xff0c;输入正确的密码&#xff0c;才能看到原图。 第一步、压缩图片 技术原理 将图片读入c…

SDL 软件安全开发周期 Security Development Lifecycle

本文参考多篇文章写作而成&#xff0c;出处在文末注明。 SDL的是安全开发生命周期&#xff0c;Security Development Lifecycle。由微软最早提出&#xff0c;是一种专注于软件开发的安全保障流程。为实现保护最终用户为目标&#xff0c;它在软件开发流程的各个阶段引入安全和隐…

C语言中如何逆序任意输出的字符串

C语言中如何逆序任意输出的字符串的三种方法&#xff1a; 一 设置两个指针&#xff0c;分别指向字符串的头部和尾部&#xff0c;然后交换两个指针所指的字符&#xff0c;并向中间移动指针直到交叉。 char *Reverse(char *s){ // p指向字符串头部 char *p s ; // q指向字符串尾…

扩展函数工具类篇(Kotlin)

引言&#xff1a;本文仅记录自己项目中使用到的扩展类、则需使用&#xff01;&#xff08;均为kt文件&#xff09; 一、ActivityExpand /*** 启动Activity*/ fun AppCompatActivity.startPager(actClass: Class<*>) {startActivity(Intent(this, actClass)) }/*** 启动…

微软推出适用于 Linux 的 Windows 子系统

导读微软宣布正式推出适用于 Linux 的 Windows 子系统 (WSL)&#xff0c;现在可以在 Microsoft Store 中以完整形式购买。据微软称&#xff0c;WSL 以前以预览形式提供&#xff0c;允许开发人员使用他们最喜欢的笔记本电脑进行编程&#xff0c;以运行 GNU/Linux 环境&#xff0…

拓扑排序(Topological Sorting)

题目描述 题目链接&#xff1a;剑指 Offer II 113. 课程顺序 - 力扣&#xff08;Leetcode&#xff09; 现在总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses-1。 给定一个数组 prerequisites &#xff0c;它的每一个元素 prerequisites[i] 表示两门课程之间的…

前端给后端发送数据,后端接收

在具体的前后端分离项目中&#xff0c;我们经常会遇到点击表格中的某一行的按钮&#xff0c;获取这一行的某一项值的需求&#xff0c;前端点击拿到这个值&#xff0c;再传给后端使用 例如&#xff1a;在此例中点击同步的按钮&#xff0c;需要获取到表格中的ip地址的内容&#…

GD32F303固件库开发(17)----内部Flash读写

概述 本例程主要讲解如何对芯片自带Flash进行读写&#xff0c;用芯片内部Flash可以对一些需要断电保存的数据进行保存&#xff0c;无需加外部得存储芯片&#xff0c;本例程采用的是GD32F303ZET6主控&#xff0c;512K大小的Flash。 最近在弄ST和GD的课程&#xff0c;需要GD样片…

web前端期末大作业:基于HTML+CSS+JS外卖服务平台10页 带购物车 (web课程设计与实现)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Yolo v5源码解析笔记

一、Yolo v5文件目录树结构 二、train.py文件介绍 从main()方法&#xff0c;首先读取参数&#xff1b;其次建立YOLONet&#xff1b;然后读取训练数据&#xff1b;最后进行训练。 2.1 读取参数 部分截图 2.2 建立YOLONet YOLONet的建立是通过 yolo文件夹中的yolo_net.py文件…

游戏开发58课 性能优化

6. 卡顿优化 相信很多研发者或玩家&#xff0c;都遇到这种情况&#xff1a;游戏大部时间运行都很流畅&#xff0c;但在战斗的某些时刻或者打开某些界面会卡一下&#xff0c;甚至卡很久。这个现象就是卡顿。引发卡顿的原因有很多&#xff0c;但主要有&#xff1a; 突发大量IO。…

银河麒麟桌面V10SP1版本系统休眠唤醒鼠标键盘失效解决方法

使用银河麒麟桌面系统有时会出现系统休眠后再唤醒&#xff0c;鼠标键盘失效的问题&#xff0c;可尝试以下方法解决。 1.检查是否安装 laptop-mode-tools 工具 执行命令: dpkg -l | grep laptop-mode-tools如果执行命令无结果输出&#xff0c;表示未安装(如果已安装&#xff0…

【从零开始学习深度学习】21. 卷积神经网络(CNN)之二维卷积层原理介绍、如何用卷积层检测物体边缘

目录1. 二维互相关运算2. 自定义二维卷积层3. 卷积层的应用----图像中物体边缘检测4. 通过数据学习核数组5. 互相关运算和卷积运算6. 特征图和感受野总结卷积神经网络&#xff08;convolutional neural network&#xff09;是含有卷积层&#xff08;convolutional layer&#x…

欧克科技在深交所上市:客户集中度较高,胡坚晟为控股股东

12月12日&#xff0c;欧克科技股份有限公司&#xff08;下称“欧克科技”&#xff0c;SZ:001223&#xff09;在深圳证券交易所主板上市。本次上市&#xff0c;欧克科技的发行价格为65.58元/股&#xff0c;发行数量为1668万股&#xff0c;募资总额约为10.94亿元&#xff0c;募资…

印制电路板(PCB)设计原则和抗干扰措施

印制电路板(PCB)是电子产品中电路元件和器件的支撑件&#xff0e;它提供电路元件和器件之间的电气连接。随着电于技术的飞速发展&#xff0c;PCB的密度越来越高。PCB 设计的好坏对抗干扰能力影响很大&#xff0c;因此&#xff0c;在进行PCB 设计时&#xff0e;必须遵守PCB设计的…

Nginx 防盗链

Nginx 防盗链 本篇主要介绍一下 nginx 中 防盗链的具体配置 , 以及http 的 referer 头 概述 防盗链其实就是 防止别的站点来引用你的 资源, 占用你的流量 在了解nginx 防盗链之前 我们先了解一下 什么是 HTTP 的头信息 Referer,当浏览器访问网站的时候,一般会带上Referer,告…

【TS】TypeScript声明文件(.d.ts)的使用

前言 当我们在TS文件中需要引入外部库时&#xff0c;编译时是无法判断传入参数的类型的&#xff0c;所以我们需要在引入前加入一个声明文件来帮助ts判断类型。 当然现在大部分库都自带有自己的声明文件&#xff0c;一般在types目录下。 使用场景 在ts文件中对引用的外部库做…