当前位置: 首页 > news >正文

嘿嘿,一个简单ElasticSearch小实现

周五临近下班,原本打算摸摸鱼,结果产品经理来个新需求。领导觉得 AI 服务器报价太贵,想先做个“低成本替代方案”来演示一下分析效果。于是,需求会议就开了。其中有一块功能是 “检索内容高亮显示并展示匹配度”,产品经理说这可以考虑用 Elasticsearch 实现。行吧,需求是他提的,代码自然就得咱来写了。那就开干吧 💪

一、启动 Elasticsearch 服务(Docker 简单搞定)

这里用的是 Elasticsearch 8.xx,主要是考虑我们项目还在用 JDK 8

1. docker

docker run \-d \--privileged=true \--name elasticsearch \-p 9200:9200 \-p 9300:9300 \-e "ES_JAVA_OPTS=-Xms1024m -Xmx2048m" \-e "discovery.type=single-node" \-e "ELASTIC_PASSWORD=elastic" \-e "xpack.security.enabled=true" \-e TZ=Asia/Shanghai \-v /etc/localtime:/etc/localtime:ro \-v /home/bugshare/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro \-v /home/bugshare/elasticsearch/data:/usr/share/elasticsearch/data \-v /home/bugshare/elasticsearch/plugins:/usr/share/elasticsearch/plugins \elasticsearch:8.19.6

2. 配置文件

# elasticsearch.yml
cluster.name: "docker-cluster"
network.host: 0.0.0.0http.cors.enabled: true
http.cors.allow-origin: "*"http.cors.allow-headers: Authorization

验证下是否启动成功:浏览器访问 http://127.0.0.1:9200,用户名密码:elastic / elastic,推荐装个浏览器插件 es-client 来操作更方便。

PixPin_2025-11-11_16-57-26.png

二、Java 集成 Elasticsearch

官方提供的 Java API 用起来有点繁琐,于是我去找了两个现成的封装框架:

  • Easy-ES
  • BBoss-Elasticsearch

下面是我整理的一份对比(AI 协助分析 👇):

详细对比表格

维度 Easy-ES BBoss-Elasticsearch
核心定位 极简 ORM,对标 MyBatis-Plus 企业级 ES 客户端 & 数据同步框架
设计理念 用对象操作 ES,屏蔽复杂性 简化但不屏蔽,保留灵活控制
学习曲线 非常平缓(MyBatis-Plus 用户零上手成本) 中等,需要理解 DSL 构建
查询 DSL 自动生成 可手写,灵活度高
ORM 支持 基础支持
数据同步 内置高性能数据同步
代码侵入性 较高(依赖注解) 较低(注解可选)
性能 简单查询快,复杂查询略逊 高性能,生产验证完善
文档 & 社区 中文文档完善 文档详尽,维护积极
适用场景 快速原型、轻量搜索 企业级复杂查询、数据同步

我个人更偏爱能写 DSL 的方案,于是选择了 BBoss

三、Spring Boot 整合 BBoss

1. 引入依赖

// build.gradle
implementation 'com.bbossgroups.plugins:bboss-elasticsearch-spring-boot-starter:7.5.3'

2. 配置文件

spring:elasticsearch:bboss:elasticUser: elasticelasticPassword: elasticelasticsearch:rest:hostNames: 127.0.0.1:9200

3. 定义映射文件(resources/esmapper/demo.xml)

// resources/esmapper/demo.xml
<properties><!-- 创建Indice --><property name="createDemoIndice"><![CDATA[{"settings": {"number_of_shards": 6,"index.refresh_interval": "5s"},"mappings": {"properties": {"demoId":{"type": "text"},"contentBody": {"type": "text"}}}}]]></property><!-- 高亮查询 --><property name="testHighlightSearch" cacheDsl="false"><![CDATA[{"query": {"bool": {"must": [{"match" : {"contentBody" : {"query" : #[condition]}}}]}},"size":1000,"highlight": {"pre_tags": ["<mark class='mark'>"],"post_tags": ["</mark>"],"fields": {"*": {}},"fragment_size": 2147483647}}]]></property>
</properties>

四、代码部分

1. 实体类

// Demo.java
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Demo extends ESBaseData {// Set the document identity field@ESId(readSet = true, persistent = false)private String demoId;private String contentBody;
}

2. 控制器

// DemoController.java
@Slf4j
@RestController
@RequestMapping("/es")
public class ElasticSearchController {@Autowiredprivate BBossESStarter bbossESStarter;private static final String MAP_PATH = "esmapper/elasticsearch.xml";@GetMapping("/init")public ResponseWrapper<Boolean, ?> init() {this.dropAndCreateAndGetIndice();this.addDocuments();return new ResponseWrapper<>().success().setMessage("初始化成功!");}@GetMapping("/dropAndCreateAndGetIndice")public void dropAndCreateAndGetIndice() {ClientInterface clientUtil = this.bbossESStarter.getConfigRestClient(MAP_PATH);boolean exist = clientUtil.existIndice("demo");log.info("exist: {}", exist);if (exist) {String r = clientUtil.dropIndice("demo");log.debug("r: {}", r);}// Create index democlientUtil.createIndiceMapping("demo", "createDemoIndice");String demoIndice = clientUtil.getIndice("demo");log.debug("demoIndice: {}", demoIndice);}@GetMapping("/addDocuments")public void addDocuments() {ClientInterface clientUtil = this.bbossESStarter.getRestClient();List<String> contents = ListUtil.of("在本系列文章中,我们将从一个新的角度来了解 Elasticsearch。","本系列文章的动机是让您更好地了解 Elasticsearch、Lucene 以及搜索引擎的底层工作原理。","我们先从基础索引结构开始,也就是倒排索引……","倒排索引将 term 映射到包含相应项的文档……","通过查找所有项及其出现次数……","Elasticsearch 索引由一个或多个分片组成……","“分片”是 Elasticsearch 的基本扩展单位……","Elasticsearch 有一个“事务日志”,其中附加了要编制索引的文档……");for (int i = 0; i < contents.size(); i++) {Demo demo = new Demo();demo.setDemoId(Convert.toStr(i + 1));demo.setContentBody(contents.get(i));String response = clientUtil.addDocument("demo", demo, "refresh=true");log.debug("response: {}", response);}}@GetMapping("/highlightSearch")public List<Map<String, Object>> highlightSearch(@RequestParam String content) {List<Map<String, Object>> list = new ArrayList<>();ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil(MAP_PATH);Map<String, Object> params = new HashMap<>();params.put("condition", content);ESDatas<Demo> esDatas = clientUtil.searchList("demo/_search","testHighlightSearch",params,Demo.class);log.debug("esDatas: {}", esDatas);// 获取总记录数long totalSize = esDatas.getTotalSize();log.debug("totalSize: {}", totalSize);// 获取结果对象列表,最多返回1000条记录List<Demo> demos = esDatas.getDatas();log.debug("demos: {}", demos);// maxScoreRestResponse restResponse = (RestResponse) esDatas.getRestResponse();Double maxScore = restResponse.getSearchHits().getMaxScore();log.debug("maxScore: {}", maxScore);for (int i = 0; demos != null && i < demos.size(); i++) {Demo demo = demos.get(i);Double score = demo.getScore();// 记录中匹配上检索条件的所有字段的高亮内容Map<String, List<Object>> highLights = demo.getHighlight();log.debug("highLights: {}", highLights);Iterator<Map.Entry<String, List<Object>>> entries = highLights.entrySet().iterator();while (entries.hasNext()) {Map.Entry<String, List<Object>> entry = entries.next();String fieldName = entry.getKey();List<Object> fieldHighLightSegments = entry.getValue();for (Object highLightSegment : fieldHighLightSegments) {list.add(MapUtil.builder(new HashMap<String, Object>()).put("highlight", highLightSegment).put("score", NumberUtil.formatPercent(NumberUtil.div(score, maxScore), 2)).build());}}}return list;}
}

前端部分就略过了,主要看效果:

匹配度 = 当前得分 / 最大得分

PixPin_2025-11-11_17-02-24.png

五、中文分词支持(IK Analyzer)

发现中文没分词,默认是按单个字匹配。验证下:

POST /demo/_analyze
{"field": "contentbody","text": "搜索引擎"
}

果然,默认没有中文分词。

1. 安装 analysis-ik 插件

# 进入docker容器
docker exec -it elasticsearch bash
# 注意跟es版本一致,不要高于es版本
elasticsearch-plugin install https://get.infini.cloud/elasticsearch/analysis-ik/8.19.6
# 重启
exit
docker restart elasticsearch
# 验证
docker exec -it elasticsearch bash
elasticsearch-plugin list

2. 修改索引映射:

// resources/esmapper/demo.xml
<properties><property name="createDemoIndice">..."contentBody": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_max_word"}...</property><property name="testHighlightSearch" cacheDsl="false">..."match" : {"contentBody" : {"query" : #[condition],"analyzer": "ik_max_word"}}...        </property><!-- 分词查询 --><property name="analyzeQuery" cacheDsl="false"><![CDATA[{##"analyzer": "standard","analyzer": "ik_max_word","text": #[condition]}]]></property>
</properties>

重启项目、重新初始化数据,再搜索一下,完美分词 ✅

六、效果展示

至此,一个小巧的 Elasticsearch 高亮搜索 + 匹配度演示 Demo 就完成了。
下周领导要看效果?没问题,稳妥得很 😎

PixPin_2025-11-11_17-29-33.png

http://www.gsyq.cn/news/48217.html

相关文章:

  • 2025年有实力的文旅景区策划专业服务单位推荐榜
  • 2025年重型深孔钻镗床加工厂口碑排行榜
  • 2025 年 11 月码垛机厂家推荐排行榜,龙门码垛机,立柱码垛机,全自动码垛机,机器人码垛设备,立柱式码垛机公司推荐
  • 部分 DP 问题小记
  • sqlserver日志收缩
  • 2025年玻璃鼠标垫定制厂家推荐排行榜
  • 2025年质量好的澳洲海外仓中转一件代发优质服务榜
  • 2025折叠伸缩雨棚定制厂家推荐排行
  • 2025年知名的智慧工厂目视化规划最新品牌推荐榜服务商
  • 基于MATLAB实现空间计量模型及检验方法
  • 2025农用遮阳网销售厂家排行
  • 2025年评价高的赣州装修公司定制
  • 2025背调最好的排行榜单
  • 2025年靠谱的河南公司注册代理记账机构品牌实力推荐排行榜
  • 2025年权威的慧商青少年少儿训练平台口碑排行榜
  • 2025年质量好的欧洲品牌三段力缓冲铰链厂家推荐及选择指南
  • 2025 年 11 月仿石漆厂家推荐排行榜,外墙仿石漆,内墙仿石漆,防霉仿石漆,水包水仿石漆,水包砂仿石漆,耐污仿石漆,自洁仿石漆公司推荐
  • CF1797F Li Hua and Path
  • 2025年口碑好的烤漆龙骨厂家推荐及选择指南
  • 2025年11月动态血糖仪品牌榜:五强性能参数与口碑排行一览
  • 2025年比较好的新疆棉花手工棉被厂家推荐及选购指南
  • 2025年质量好的磨砂布牛津布行业内口碑厂家排行榜
  • 2025年大型的继承律师事务所精英榜
  • 400电话解决方案有哪些优势?
  • 2025年比较好的不锈钢厨房拉篮厂家最新热销排行
  • 2025年数显电流仪表供货商排行榜单
  • 2025年性价比高的小白瓶前置过滤器排名
  • 如何选择适合你企业的外呼系统?实用指南!
  • 2025年青少年无人机培训位置推荐
  • Web前端入门第 90 问:JavaScript 也能无中生有的创建音频