Elasticsearch-实战案例

news2025/4/2 7:34:22

一、没有使用Elasticsearch的查询速度698ms

 1.数据库模糊查询不走索引,在数据量较大的时候,查询性能很差。需要注意的是,数据库模糊查询随着表数据量的增多,查询性能的下降会非常明显,而搜索引擎的性能则不会随着数据增多而下降太多。目前仅10万不到的数据量差距就如此明显,如果数据量达到百万、千万、甚至上亿级别,这个性能差距会非常夸张。

2.功能单一

数据库的模糊搜索功能单一,匹配条件非常苛刻,必须恰好包含用户搜索的关键字。而在搜索引擎中,用户输入出现个别错字,或者用拼音搜索、同义词搜索都能正确匹配到数据。

综上,在面临海量数据的搜索,或者有一些复杂搜索需求的时候,推荐使用专门的搜索引擎来实现搜索功能。

二、 Elasticsearch使用

1.依赖

版本:因为SpringBoot默认的ES版本是7.17.10,所以我们需要覆盖默认的ES版本:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
  <properties>
      <maven.compiler.source>11</maven.compiler.source>
      <maven.compiler.target>11</maven.compiler.target>
      <elasticsearch.version>7.12.1</elasticsearch.version>
  </properties>

启动es服务---测试是否连接es成功:

package com.itfly;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class IndexTest {

    private RestHighLevelClient client;

    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://localhost:9200")
        ));
    }

    @Test
    void testConnect() {
        System.out.println(client);
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

成功连接

2、创建索引库 

实现搜索功能需要的字段包括三大部分:

  • 搜索过滤字段

    • 分类

    • 品牌

    • 价格

  • 排序字段

    • 默认:按照更新时间降序排序

    • 销量

    • 价格

  • 展示字段

    • 商品id:用于点击后跳转

    • 图片地址

    • 是否是广告推广商品

    • 名称

    • 价格

    • 评价数量

    • 销量

代码分为三步:

  • 1)创建Request对象。

    • 因为是创建索引库的操作,因此Request是CreateIndexRequest

  • 2)添加请求参数

    • 其实就是Json格式的Mapping映射参数。因为json字符串很长,这里是定义了静态字符串常量MAPPING_TEMPLATE,让代码看起来更加优雅。

  • 3)发送请求

    • client.indices()方法的返回值是IndicesClient类型,封装了所有与索引库操作有关的方法。例如创建索引、删除索引、判断索引是否存在等

 

    @Test
    void testCreateIndex() throws IOException {
        // 1.创建Request对象
        CreateIndexRequest request = new CreateIndexRequest("goods");
        // 2.准备请求参数
        request.source(MAPPING_TEMPLATE, XContentType.JSON);
        // 3.发送请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }

    static final String MAPPING_TEMPLATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"id\": { \"type\": \"long\" },\n" +
            "      \"name\": { \"type\": \"text\", \"analyzer\": \"ik_smart\" },\n" +
            "      \"description\": { \"type\": \"text\", \"analyzer\": \"ik_smart\" },\n" +
            "      \"price\": { \"type\": \"scaled_float\", \"scaling_factor\": 100 },\n" +
            "      \"stock\": { \"type\": \"integer\" },\n" +
            "      \"status\": { \"type\": \"integer\" },\n" +
            "      \"image\": { \"type\": \"keyword\", \"index\": false },\n" +
            "      \"created_at\": { \"type\": \"date\", \"format\": \"yyyy-MM-dd HH:mm:ss\" },\n" +
            "      \"updated_at\": { \"type\": \"date\", \"format\": \"yyyy-MM-dd HH:mm:ss\" }\n" +
            "    }\n" +
            "  }\n" +
            "}";

 判断索引库是否存在

@Test
void testExistsIndex() throws IOException {
    // 1.创建Request对象
    GetIndexRequest request = new GetIndexRequest("goods");
    // 2.发送请求
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    // 3.输出
    System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}

 

RestClient操作文档

索引库准备好以后,就可以操作文档了。为了与索引库操作分离,我们再次创建一个测试类,做两件事情:

package com.itfly;


import com.itfly.service.IProductsService;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

@SpringBootTest(properties = "spring.profiles.active=local")
public class DocumentTest {

    private RestHighLevelClient client;
    @Autowired
    private IProductsService productsService;

    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://localhost:9200")
        ));
    }
    
    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

新增文档

我们需要将数据库中的商品信息导入elasticsearch中,而不是造假数据了。

语法:
 

POST /{索引库名}/_doc/1
{
    "name": "Jack",
    "age": 21
}

可以看到与索引库操作的API非常类似,同样是三步走:

  • 1)创建Request对象,这里是IndexRequest,因为添加文档就是创建倒排索引的过程

  • 2)准备请求参数,本例中就是Json文档

  • 3)发送请求

变化的地方在于,这里直接使用client.xxx()的API,不再需要client.indices()了。

    @Test
    void testAddDocument() throws IOException {
        // 1.根据id查询商品数据,批量新增文档
        productsService.list().forEach(item -> {
            IndexRequest indexRequest = new IndexRequest("goods");
            indexRequest.id(item.getId().toString());
            indexRequest.source(JSONUtil.parseObj(item), XContentType.JSON);
            try {
                client.index(indexRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("添加成功");
        });
        //
    }

 把所有的数据加入索引库,并创建文档

RestClient查询 



  • 第一步,创建SearchRequest对象,指定索引库名

  • 第二步,利用request.source()构建DSL,DSL中可以包含查询、分页、排序、高亮等

    • query():代表查询条件,利用QueryBuilders.matchAllQuery()构建一个match_all查询的DSL

  • 第三步,利用client.search()发送请求,得到响应

@Test
void testMatchAll() throws IOException {
    // 1.创建Request
    SearchRequest request = new SearchRequest("goods");
    // 2.组织请求参数
    request.source().query(QueryBuilders.matchAllQuery());
    // 3.发送请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析响应
    handleResponse(response);
}

private void handleResponse(SearchResponse response) {
    SearchHits searchHits = response.getHits();
    // 1.获取总条数
    long total = searchHits.getTotalHits().value;
    System.out.println("共搜索到" + total + "条数据");
    // 2.遍历结果数组
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        // 3.得到_source,也就是原始json文档
        String source = hit.getSourceAsString();
        // 4.反序列化并打印
        ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
        System.out.println(item);
    }
}

 查询

package com.itfly.controller;

import com.itfly.DTO.ProductsSearchDTO;
import com.itfly.resp.ResultData;
import com.itfly.service.IProductsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Map;

/**
 * @author yyf
 * description
 * @date 2025/3/27 18:33
 */
@RestController
@RequestMapping("/es")
@Api(tags = "商品")
public class SearchController {
    @Autowired
    private IProductsService productsService;
    @PostMapping("/list")
    @ApiOperation(value = "查询所有商品")
    public ResultData list(@RequestBody ProductsSearchDTO productsSearchDTO) {
        // 创建客户端
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200))
        );
        try {
            // 构建查询请求
            SearchRequest searchRequest = new SearchRequest("goods");
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(QueryBuilders.matchQuery("name",productsSearchDTO.getName() ));
            sourceBuilder.size(10);
            searchRequest.source(sourceBuilder);

            // 执行查询
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

            // 处理结果
            for (SearchHit hit : response.getHits().getHits()) {
                // 获取文档内容,返回ResultData
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                return ResultData.success(sourceAsMap);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                client.close(); // 关闭客户端
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}

总结:

 数据库模糊查询不走索引,在数据量较大的时候,查询性能很差。需要注意的是,数据库模糊查询随着表数据量的增多,查询性能的下降会非常明显,而搜索引擎的性能则不会随着数据增多而下降太多。目前仅10万不到的数据量差距就如此明显,如果数据量达到百万、千万、甚至上亿级别,这个性能差距会非常夸张。

其次,功能单一

数据库的模糊搜索功能单一,匹配条件非常苛刻,必须恰好包含用户搜索的关键字。而在搜索引擎中,用户输入出现个别错字,或者用拼音搜索、同义词搜索都能正确匹配到数据。

综上,在面临海量数据的搜索,或者有一些复杂搜索需求的时候,推荐使用专门的搜索引擎来实现搜索功能。

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

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

相关文章

IP数据报报文格式

一 概述 IP数据报由两部分组成&#xff1a;首部数据部分。首部的前一部分是固定长度&#xff0c;一共20字节大小&#xff0c;是所有IP数据报文必须具有的&#xff1b;固定部分后面是一些可选字段&#xff0c;其长度是可变的。 二 首部固定部分各字段意义 &#xff08;1&…

openEuler24.03 LTS下安装Kafka集群

目录 前提条件 Kafka集群规划 下载Kafka 解压 设置环境变量 配置Kafka 分发到其他机器 分发安装文件 分发环境变量 启动Kafka 测试Kafka 关闭Kafka 集群启停脚本 问题及解决 前提条件 安装好ZooKeeper集群&#xff0c;可参考&#xff1a;openEuler24.03 LTS下安…

qt QQuaternion详解

1. 概述 QQuaternion 是 Qt 中用于表示三维空间中旋转的四元数类。它包含一个标量部分和一个三维向量部分&#xff0c;可以用来表示旋转操作。四元数在计算机图形学中广泛用于平滑的旋转和插值。 2. 重要方法 默认构造函数 QQuaternion::QQuaternion(); // 构造单位四元数 (1…

epoch、batch、batch size、step、iteration深度学习名词含义详细介绍

卷积神经网络训练中的三个核心概念&#xff1a;Epoch、Batch Size 和迭代次数 在深度学习中&#xff0c;理解一些基本的术语非常重要&#xff0c;这些术语对模型的训练过程、效率以及最终性能都有很大影响。以下是一些常见术语的含义介绍&#xff1a; 1. Epoch&#xff08;周…

TCP 协议算法解析 | RTT / 滑动窗口 / 拥塞控制

注&#xff1a;本文为 “TCP 协议算法解析” 相关文章合辑。 略作重排&#xff0c;未去重。 如有内容异常&#xff0c;请看原文。 TCP 的那些事儿&#xff08;上&#xff09; 2014 年 05 月 28 日 陈皓 TCP 是一个极为复杂的协议&#xff0c;因为它需要解决众多问题&#x…

卷积神经网络 - ResNet(残差网络)

残差网络(Residual Network&#xff0c;ResNet)通过给非线性的卷积层增加直连边 (Shortcut Connection)(也称为残差连接(Residual Connection))的方式来提高信息的传播效率。 这是一种特殊的深度神经网络结构&#xff0c;由 Kaiming He 等人在 2015 年提出&#xff0c;目的是解…

GreenPlum学习

简介 Greenplum是一个面向数据仓库应用的关系型数据库&#xff0c;因为有良好的体系结构&#xff0c;所以在数据存储、高并发、高可用、线性扩展、反应速度、易用性和性价比等方面有非常明显的优势。Greenplum是一种基于PostgreSQL的分布式数据库&#xff0c;其采用sharednothi…

传统神经网络、CNN与RNN

在网络上找了很多关于深度学习的资料&#xff0c;也总结了一点小心得&#xff0c;于是就有了下面这篇文章。这里内容较为简单&#xff0c;适合初学者查看&#xff0c;所以大佬看到这里就可以走了。 话不多说&#xff0c;上图 #mermaid-svg-Z3k5YhiQ2o5AnvZE {font-family:&quo…

无人机,雷达定点飞行时,位置发散,位置很飘,原因分析

参考&#xff1a; 无人车传感器 IMU与GPS数据融合进行定位机制_gps imu 组合定位原始数-CSDN博客 我的无人机使用雷达定位&#xff0c;位置模式很飘 雷达的更新频率也是10HZ&#xff0c; 而px飞控的频率是100HZ&#xff0c;没有对两者之间的频率差异做出处理 所以才导致无人…

【Linux探索学习】第二十九弹——线程概念:Linux线程的基本概念与线程控制详解

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 在现代操作系统中&#xff0c;线程是程序执行流的最小单元。与进程相比&#xff0c;线程更加轻量级&#xff0c;创建和销毁的开销更小&…

深入探索 iOS 卡顿优化

认识卡顿 一些概念 FPS&#xff1a;Frames Per Second&#xff0c;表示每秒渲染的帧数&#xff0c;通过用于衡量画面的流畅度&#xff0c;数值越高则表示画面越流畅。CPU&#xff1a;负责对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码…

# 基于 OpenCV 的选择题自动批改系统实现

在教育领域&#xff0c;选择题的批改工作通常较为繁琐且重复性高。为了提高批改效率&#xff0c;我们可以利用计算机视觉技术&#xff0c;通过 OpenCV 实现选择题的自动批改。本文将详细介绍如何使用 Python 和 OpenCV 实现一个简单的选择题自动批改系统。 1. 项目背景 选择题…

身份验证:区块链如何让用户掌控一切

在网上证明你自称的身份变得越来越复杂。由于日常生活的很多方面现在都在网上进行&#xff0c;保护你的数字身份比以往任何时候都更加重要。 我们可能都接受过安全培训&#xff0c;这些培训鼓励我们选择安全的密码、启用双因素身份验证或回答安全问题&#xff0c;例如“你祖母…

嵌入式硬件: GPIO与二极管基础知识详解

1. 前言 在嵌入式系统和硬件开发中&#xff0c;GPIO&#xff08;通用输入输出&#xff09;是至关重要的控制方式&#xff0c;而二极管作为基础电子元件&#xff0c;广泛应用于信号整流、保护电路等。本文将从基础原理出发&#xff0c;深入解析GPIO的输入输出模式&#xff0c;包…

游戏引擎学习第194天

为当天的活动做铺垫 正在进行游戏开发中的调试和视图功能开发。目标是增加一些新功能&#xff0c;使得在开发过程中能够有效地检查游戏行为。今天的重点是推进用户界面&#xff08;UI&#xff09;的开发&#xff0c;并且尝试在调试变量的管理上找到一个折中的解决方案。计划探…

js文字两端对齐

目录 一、问题 二、原因及解决方法 三、总结 一、问题 1.text-align: justify; 不就可以了吗&#xff1f;但是实际测试无效 二、原因及解决方法 1.原因&#xff1a;text-align只对非最后一行文字有效。只有一行文字时&#xff0c;text-align无效&#xff0c;要用text-alig…

HarmonyOS 介绍

HarmonyOS简介 随着万物互联时代的开启&#xff0c;应用的设备底座将从几十亿手机扩展到数百亿IoT设备。全新的全场景设备体验&#xff0c;正深入改变消费者的使用习惯。 同时应用开发者也面临设备底座从手机单设备到全场景多设备的转变&#xff0c;全场景多设备的全新底座&am…

每天一篇目标检测文献(六)——Part One

今天看的是《Object Detection with Deep Learning: A Review》 目录 一、摘要 1.1 原文 1.2 翻译 二、介绍 2.1 信息区域选择 2.2 特征提取 2.3 分类 三、深度学习的简要回顾 3.1 历史、诞生、衰落和繁荣 3.2 CNN架构和优势 一、摘要 1.1 原文 Due to object dete…

ESXI 安装及封装第三方驱动和在ESXI系统下安装驱动

ESXI 安装及封装第三方驱动和在ESXI系统下安装驱动 准备工作在线安装 Windows PowerShell离线安装 Windows PowerShell更新在线更新离线更新 下载 ESXi-Customizer-PS-v2.6.0.ps1安装Python安装pip安装相关插件 下载离线捆绑包下载对应的网卡驱动&#xff08;如果纯净版可以进去…

【12】Ajax的原理和解析

一、前言 二、什么是Ajax 三、Ajax的基本原理 3.1 发送请求 3.2 解析内容 3.3 渲染网页 3.4 总结 四、Ajax 分析 五、过滤请求-筛选所有Ajax请求 一、前言 当我们在用 requests 抓取页面的时候&#xff0c;得到的结果可能会和在浏览器中看到的不一样&a…