网易首页 > 网易号 > 正文 申请入驻

基于 ElasticSearch 实现站内全文搜索

0
分享至

摘要

对于一家公司而言,数据量越来越多,如果快速去查找这些信息是一个很难的问题,在计算机领域有一个专门的领域IR(Information Retrival)研究如果获取信息,做信息检索。

在国内的如百度这样的搜索引擎也属于这个领域,要自己实现一个搜索引擎是非常难的,不过信息查找对每一个公司都非常重要,对于开发人员也可以选则一些市场上的开源项目来构建自己的站内搜索引擎,本文将通过ElasticSearch来构建一个这样的信息检索项目。

1 技术选型

  • 搜索引擎服务使用 ElasticSearch

  • 提供的对外 web 服务选则 Springboot web

1.1 ElasticSearch

1.2 Spring Boot

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

现在 Spring Boot 在做 web 开发上是绝对的主流,其不仅仅是开发上的优势,在布署,运维各个方面都有着非常不错的表现,并且 Spring 生态圈的影响力太大了,可以找到各种成熟的解决方案。

1.3 ik分词器

ElasticSearch 本身不支持中文的分词,需要安装中文分词插件,如果需要做中文的信息检索,中文分词是基础,此处选则了ik,下载好后放入 elasticSearch 的安装位置的 plugin 目录即可。

2 环境准备

需要安装好elastiSearch以及kibana(可选),并且需要lk分词插件。

  • 安装elasticSearch elasticsearch官网. 笔者使用的是7.5.1。

  • ik插件下载 ik插件github地址. 注意下载和你下载elasticsearch版本一样的ik插件。

  • 将ik插件放入elasticsearch安装目录下的plugins包下,新建报名ik,将下载好的插件解压到该目录下即可,启动es的时候会自动加载该插件。

搭建 Spring Boot 项目 idea ->new project ->spring initializer

3 项目架构

  • 获取数据使用ik分词插件

  • 将数据存储在es引擎中

  • 通过es检索方式对存储的数据进行检索

  • 使用es的java客户端提供外部服务

4 实现效果

4.1 搜索页面

简单实现一个类似百度的搜索框即可。

4.2 搜索结果页面

点击第一个搜索结果是我个人的某一篇博文,为了避免数据版权问题,笔者在es引擎中存放的全是个人的博客数据。

5 具体代码实现

5.1 全文检索的实现对象

按照博文的基本信息定义了如下实体类,主要需要知道每一个博文的url,通过检索出来的文章具体查看要跳转到该url。


package com.lbh.es.entity;
import com.fasterxml.jackson.annotation.JsonIgnore; import javax.persistence.*; /** * PUT articles * { * "mappings": * {"properties":{ * "author":{"type":"text"}, * "content":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"}, * "title":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"}, * "createDate":{"type":"date","format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"}, * "url":{"type":"text"} * } }, * "settings":{ * "index":{ * "number_of_shards":1, * "number_of_replicas":2 * } * } * } * --------------------------------------------------------------------------------------------------------------------- * Copyright(c)lbhbinhao@163.com * @author liubinhao * @date 2021/3/3 */ @Entity @Table(name = "es_article") public class ArticleEntity { @Id @JsonIgnore @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "author") private String author; @Column(name = "content",columnDefinition="TEXT") private String content; @Column(name = "title") private String title; @Column(name = "createDate") private String createDate; @Column(name = "url") private String url; public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getCreateDate() { return createDate; } public void setCreateDate(String createDate) { this.createDate = createDate; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }

5.2 客户端配置

通过java配置es的客户端。


/** * Copyright(c)lbhbinhao@163.com * @author liubinhao * @date 2021/3/3 */ @Configuration public class EsConfig { @Value("${elasticsearch.schema}") private String schema; @Value("${elasticsearch.address}") private String address; @Value("${elasticsearch.connectTimeout}") private int connectTimeout; @Value("${elasticsearch.socketTimeout}") private int socketTimeout; @Value("${elasticsearch.connectionRequestTimeout}") private int tryConnTimeout; @Value("${elasticsearch.maxConnectNum}") private int maxConnNum; @Value("${elasticsearch.maxConnectPerRoute}") private int maxConnectPerRoute;
@Bean public RestHighLevelClient restHighLevelClient() { // 拆分地址 List hostLists = new ArrayList<>(); String[] hostList = address.split(","); for (String addr : hostList) { String host = addr.split(":")[0]; String port = addr.split(":")[1]; hostLists.add(new HttpHost(host, Integer.parseInt(port), schema)); } // 转换成 HttpHost 数组 HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{}); // 构建连接对象 RestClientBuilder builder = RestClient.builder(httpHost); // 异步连接延时配置 builder.setRequestConfigCallback(requestConfigBuilder -> { requestConfigBuilder.setConnectTimeout(connectTimeout); requestConfigBuilder.setSocketTimeout(socketTimeout); requestConfigBuilder.setConnectionRequestTimeout(tryConnTimeout); return requestConfigBuilder; }); // 异步连接数配置 builder.setHttpClientConfigCallback(httpClientBuilder -> { httpClientBuilder.setMaxConnTotal(maxConnNum); httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute); return httpClientBuilder; }); return new RestHighLevelClient(builder); } }

5.3 业务代码编写

包括一些检索文章的信息,可以从文章标题,文章内容以及作者信息这些维度来查看相关信息。


/** * Copyright(c)lbhbinhao@163.com * @author liubinhao * @date 2021/3/3 */ @Service public class ArticleService { private static final String ARTICLE_INDEX = "article"; @Resource private RestHighLevelClient client; @Resource private ArticleRepository articleRepository; public boolean createIndexOfArticle(){ Settings settings = Settings.builder() .put("index.number_of_shards", 1) .put("index.number_of_replicas", 1) .build(); // {"properties":{"author":{"type":"text"}, // "content":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"} // ,"title":{"type":"text","analyzer":"ik_max_word","search_analyzer":"ik_smart"}, // ,"createDate":{"type":"date","format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"} // } String mapping = "{\"properties\":{\"author\":{\"type\":\"text\"},\n" + "\"content\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_smart\"}\n" + ",\"title\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",\"search_analyzer\":\"ik_smart\"}\n" + ",\"createDate\":{\"type\":\"date\",\"format\":\"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd\"}\n" + "},\"url\":{\"type\":\"text\"}\n" + "}"; CreateIndexRequest indexRequest = new CreateIndexRequest(ARTICLE_INDEX) .settings(settings).mapping(mapping,XContentType.JSON); CreateIndexResponse response = null; try { response = client.indices().create(indexRequest, RequestOptions.DEFAULT); } catch (IOException e) { e.printStackTrace(); } if (response!=null) { System.err.println(response.isAcknowledged() ? "success" : "default"); return response.isAcknowledged(); } else { return false; } }
public boolean deleteArticle(){ DeleteIndexRequest request = new DeleteIndexRequest(ARTICLE_INDEX); try { AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT); return response.isAcknowledged(); } catch (IOException e) { e.printStackTrace(); } return false; }
public IndexResponse addArticle(ArticleEntity article){ Gson gson = new Gson(); String s = gson.toJson(article); //创建索引创建对象 IndexRequest indexRequest = new IndexRequest(ARTICLE_INDEX); //文档内容 indexRequest.source(s,XContentType.JSON); //通过client进行http的请求 IndexResponse re = null; try { re = client.index(indexRequest, RequestOptions.DEFAULT); } catch (IOException e) { e.printStackTrace(); } return re; }
public void transferFromMysql(){ articleRepository.findAll().forEach(this::addArticle); }
public List queryByKey(String keyword){ SearchRequest request = new SearchRequest(); /* * 创建 搜索内容参数设置对象:SearchSourceBuilder * 相对于matchQuery,multiMatchQuery针对的是多个fi eld,也就是说,当multiMatchQuery中,fieldNames参数只有一个时,其作用与matchQuery相当; * 而当fieldNames有多个参数时,如field1和field2,那查询的结果中,要么field1中包含text,要么field2中包含text。 */ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders .multiMatchQuery(keyword, "author","content","title")); request.source(searchSourceBuilder); List result = new ArrayList<>(); try { SearchResponse search = client.search(request, RequestOptions.DEFAULT); for (SearchHit hit:search.getHits()){ Map map = hit.getSourceAsMap(); ArticleEntity item = new ArticleEntity(); item.setAuthor((String) map.get("author")); item.setContent((String) map.get("content")); item.setTitle((String) map.get("title")); item.setUrl((String) map.get("url")); result.add(item); } return result; } catch (IOException e) { e.printStackTrace(); } return null; } public ArticleEntity queryById(String indexId){ GetRequest request = new GetRequest(ARTICLE_INDEX, indexId); GetResponse response = null; try { response = client.get(request, RequestOptions.DEFAULT); } catch (IOException e) { e.printStackTrace(); } if (response!=null&&response.isExists()){ Gson gson = new Gson(); return gson.fromJson(response.getSourceAsString(),ArticleEntity.class); } return null; } }

5.4 对外接口

和使用springboot开发web程序相同。


/** * Copyright(c)lbhbinhao@163.com * @author liubinhao * @date 2021/3/3 */ @RestController @RequestMapping("article") public class ArticleController { @Resource private ArticleService articleService;
@GetMapping("/create") public boolean create(){ return articleService.createIndexOfArticle(); }
@GetMapping("/delete") public boolean delete() { return articleService.deleteArticle(); }
@PostMapping("/add") public IndexResponse add(@RequestBody ArticleEntity article){ return articleService.addArticle(article); }
@GetMapping("/fransfer") public String transfer(){ articleService.transferFromMysql(); return "successful"; } @GetMapping("/query") public List query(String keyword){ return articleService.queryByKey(keyword); } }

5.5 页面

此处页面使用thymeleaf,主要原因是笔者真滴不会前端,只懂一丢丢简单的h5,就随便做了一个可以展示的页面。

搜索页面


YiyiDu

搜索结果页面


xx-manager

来源:blog.csdn.net/weixin_44671737/article/details/114456257

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
问界M7碰撞起火3人遇难,这时我才看懂了华为智选模式

问界M7碰撞起火3人遇难,这时我才看懂了华为智选模式

闲醉山人
2024-04-28 16:43:23
闹大了,雷军发文称:累了就去小米展台,定制配文矿泉水免费领

闹大了,雷军发文称:累了就去小米展台,定制配文矿泉水免费领

钱多多多多
2024-04-28 20:18:48
美国阴谋曝光!美打算在蒙古境内设立军事基地,蒙古回应很清醒

美国阴谋曝光!美打算在蒙古境内设立军事基地,蒙古回应很清醒

布衣的呼喊
2024-04-27 09:46:58
将中国银行踢出SWIFT,美终极金融战打响!

将中国银行踢出SWIFT,美终极金融战打响!

华山穹剑
2024-04-28 18:54:29
局势反转说来就来!菲应声倒地,不是中国干的,却被打个措手不及

局势反转说来就来!菲应声倒地,不是中国干的,却被打个措手不及

康海河说
2024-04-29 09:36:57
问界M9底盘用料太吓人了,可以比肩合资50-100万间任意车型!

问界M9底盘用料太吓人了,可以比肩合资50-100万间任意车型!

热爱娱乐等
2024-03-15 15:05:48
乌克兰被忽悠瘸了?有些人别把自己也给骗了

乌克兰被忽悠瘸了?有些人别把自己也给骗了

寰宇大观察
2024-04-27 21:30:09
狗头萝莉公布招工信息,却因工资太低被骂上热搜,哭着解释这件事

狗头萝莉公布招工信息,却因工资太低被骂上热搜,哭着解释这件事

新游戏大妹子
2024-04-27 12:26:23
英超战至最后1轮?五大联赛已出3冠:德意法均结束,皇马13分领跑

英超战至最后1轮?五大联赛已出3冠:德意法均结束,皇马13分领跑

直播吧
2024-04-29 08:48:19
掘金官方:穆雷左小腿拉伤 G5出战成疑

掘金官方:穆雷左小腿拉伤 G5出战成疑

直播吧
2024-04-29 07:44:26
快船5分险胜独行侠!不得不承认的8个现实:杜登欧分手是错误选择

快船5分险胜独行侠!不得不承认的8个现实:杜登欧分手是错误选择

毒舌NBA
2024-04-29 06:59:56
普京签令,俄宣布“国有化”!俄研制新版“桥梁杀手”,泽连斯基:至少还需7套“爱国者”

普京签令,俄宣布“国有化”!俄研制新版“桥梁杀手”,泽连斯基:至少还需7套“爱国者”

每日经济新闻
2024-04-28 13:15:19
五一彻底打消了去新加坡旅游的念头!网友分享太真实,替我省钱了

五一彻底打消了去新加坡旅游的念头!网友分享太真实,替我省钱了

美美谈情感
2024-04-28 17:39:18
为何以前五一放7天,现在却改成五天还得调休,原来都是他的建议

为何以前五一放7天,现在却改成五天还得调休,原来都是他的建议

动漫里的童话
2024-04-28 17:12:23
中国女飞人遭国际田联除名!因男性化特征明显,田协已低调处理

中国女飞人遭国际田联除名!因男性化特征明显,田协已低调处理

刺头体育
2024-04-29 07:35:08
将被抹除!哈马斯再度求饶:珍视和平,希望回到加沙帮助重建家园

将被抹除!哈马斯再度求饶:珍视和平,希望回到加沙帮助重建家园

娱宙观
2024-04-28 14:52:33
1998年印尼屠华事件:10万华人妇女当街被侵犯,50万华人被残杀

1998年印尼屠华事件:10万华人妇女当街被侵犯,50万华人被残杀

平安是福呀
2024-04-25 23:19:39
爆冷1-3!从总冠军球队到连续两年首轮游,不得不承认五个现实

爆冷1-3!从总冠军球队到连续两年首轮游,不得不承认五个现实

毒舌NBA
2024-04-29 09:47:00
周鸿祎990万卖车,奔驰迈巴赫恨死他了

周鸿祎990万卖车,奔驰迈巴赫恨死他了

一品扫地僧
2024-04-28 23:13:18
“新冠疫苗之父”的突然落马,让我回想起新冠时期的“顽强抗争”

“新冠疫苗之父”的突然落马,让我回想起新冠时期的“顽强抗争”

巴城的城
2024-04-28 19:05:46
2024-04-29 12:46:44
会呼吸的Coder
会呼吸的Coder
科技改变世界
466文章数 1813关注度
往期回顾 全部

科技要闻

马斯克想把特斯拉中国数据送出国 这事太难

头条要闻

牛弹琴:总理在钓鱼台会见马斯克 向美国释放明确信号

头条要闻

牛弹琴:总理在钓鱼台会见马斯克 向美国释放明确信号

体育要闻

湖人的G4,尽人事得到了回报

娱乐要闻

田馥甄遭抵制,蔡依林却能稳稳捞金?

财经要闻

问界M7追尾起火3人遇难 四大疑问待解

汽车要闻

配置更丰富 静态体验2024款欧拉好猫

态度原创

房产
手机
本地
数码
公开课

房产要闻

力度越来越大!落户两年享本地居民购房政策,海南第16城松绑限购!

手机要闻

华为Pura 70零件已实现90%本土制造 100%存在挑战

本地新闻

云游中国|苗族蜡染:九黎城的“潮”文化

数码要闻

古尔曼:Vision Pro继任者2026年底之前不会推出

公开课

父亲年龄越大孩子越不聪明?

无障碍浏览 进入关怀版