es搜索系统封装源码,走过路过,请帮我点个star哦!
原文地址为https://www.cnblogs.com/haixiang/p/12451703.html,转载请注明出处!

什么是elasticsearch

Elasticsearch 是一个开源的高度可扩展的全文搜索和分析引擎,拥有查询近实时的超强性能。

大名鼎鼎的Lucene 搜索引擎被广泛用于搜索领域,但是操作复杂繁琐,总是让开发者敬而远之。而 Elasticsearch将 Lucene 作为其核心来实现所有索引和搜索的功能,通过简单的 RESTful 语法来隐藏掉 Lucene 的复杂性,从而让全文搜索变得简单

ES在Lucene基础上,提供了一些分布式的实现:集群,分片,复制等。

搜索为什么不用MySQL而用es

我们本文案例是一个迷你商品搜索系统,为什么不考虑使用MySQL来实现搜索功能呢?原因如下:

  • MySQL默认使用innodb引擎,底层采用b+树的方式来实现,而Es底层使用倒排索引的方式实现,使用倒排索引支持各种维度的分词,可以掌控不同粒度的搜索需求。(MYSQL8版本也支持了全文检索,使用倒排索引实现,有兴趣可以去看看两者的差别)
  • 如果使用MySQL的%key%的模糊匹配来与es的搜索进行比较,在8万数据量时他们的耗时已经达到40:1左右,毫无疑问在速度方面es完胜。

es在大厂中的应用情况

  • es运用最广泛的是elk组合来对日志进行搜索分析
  • 58安全部门、京东订单中心几乎全采用es来完成相关信息的存储与检索
  • es在tob的项目中也用于各种检索与分析
  • 在c端产品中,企业通常自己基于Lucene封装自己的搜索系统,为了适配公司营销战略、推荐系统等会有更多定制化的搜索需求

es客户端选型

spring-boot-starter-data-elasticsearch

我相信你看到的网上各类公开课视频或者小项目均推荐使用这款springboot整合过的es客户端,但是我们要say no!

此图是引入的最新版本的依赖,我们可以看到它所使用的es-high-client也为6.8.7,而es7.x版本都已经更新很久了,这里许多新特性都无法使用,所以版本滞后是他最大的问题。而且它的底层也是highclient,我们操作highclient可以更灵活。我呆过的两个公司均未采用此客户端。

elasticsearch-rest-high-level-client

这是官方推荐的客户端,支持最新的es,其实使用起来也很便利,因为是官方推荐所以在特性的操作上肯定优于前者。而且该客户端与TransportClient不同,不存在并发瓶颈的问题,官方首推,必为精品!

搭建自己的迷你搜索系统

引入es相关依赖,除此之外需引入springboot-web依赖、jackson依赖以及lombok依赖等。

    <properties>
<es.version>7.3.2</es.version>
</properties>
<!-- high client-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${es.version}</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${es.version}</version>
</dependency> <!--rest low client high client以来低版本client所以需要引入-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>${es.version}</version>
</dependency>

es配置文件es-config.properties

es.host=localhost
es.port=9200
es.token=es-token
es.charset=UTF-8
es.scheme=http es.client.connectTimeOut=5000
es.client.socketTimeout=15000

封装RestHighLevelClient

@Configuration
@PropertySource("classpath:es-config.properties")
public class RestHighLevelClientConfig { @Value("${es.host}")
private String host;
@Value("${es.port}")
private int port;
@Value("${es.scheme}")
private String scheme;
@Value("${es.token}")
private String token;
@Value("${es.charset}")
private String charSet;
@Value("${es.client.connectTimeOut}")
private int connectTimeOut;
@Value("${es.client.socketTimeout}")
private int socketTimeout; @Bean
public RestClientBuilder restClientBuilder() {
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost(host, port, scheme)
); Header[] defaultHeaders = new Header[]{
new BasicHeader("Accept", "*/*"),
new BasicHeader("Charset", charSet),
//设置token 是为了安全 网关可以验证token来决定是否发起请求 我们这里只做象征性配置
new BasicHeader("E_TOKEN", token)
};
restClientBuilder.setDefaultHeaders(defaultHeaders);
restClientBuilder.setFailureListener(new RestClient.FailureListener(){
@Override
public void onFailure(Node node) {
System.out.println("监听某个es节点失败");
}
});
restClientBuilder.setRequestConfigCallback(builder ->
builder.setConnectTimeout(connectTimeOut).setSocketTimeout(socketTimeout));
return restClientBuilder;
} @Bean
public RestHighLevelClient restHighLevelClient(RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}
}

封装es常用操作es搜索系统封装源码

@Service
public class RestHighLevelClientService { @Autowired
private RestHighLevelClient client; @Autowired
private ObjectMapper mapper; /**
* 创建索引
* @param indexName
* @param settings
* @param mapping
* @return
* @throws IOException
*/
public CreateIndexResponse createIndex(String indexName, String settings, String mapping) throws IOException {
CreateIndexRequest request = new CreateIndexRequest(indexName);
if (null != settings && !"".equals(settings)) {
request.settings(settings, XContentType.JSON);
}
if (null != mapping && !"".equals(mapping)) {
request.mapping(mapping, XContentType.JSON);
}
return client.indices().create(request, RequestOptions.DEFAULT);
} /**
* 判断 index 是否存在
*/
public boolean indexExists(String indexName) throws IOException {
GetIndexRequest request = new GetIndexRequest(indexName);
return client.indices().exists(request, RequestOptions.DEFAULT);
} /**
* 搜索
*/
public SearchResponse search(String field, String key, String rangeField, String
from, String to,String termField, String termVal,
String ... indexNames) throws IOException{
SearchRequest request = new SearchRequest(indexNames); SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new MatchQueryBuilder(field, key)).must(new RangeQueryBuilder(rangeField).from(from).to(to)).must(new TermQueryBuilder(termField, termVal));
builder.query(boolQueryBuilder);
request.source(builder);
log.info("[搜索语句为:{}]",request.source().toString());
return client.search(request, RequestOptions.DEFAULT);
} /**
* 批量导入
* @param indexName
* @param isAutoId 使用自动id 还是使用传入对象的id
* @param source
* @return
* @throws IOException
*/
public BulkResponse importAll(String indexName, boolean isAutoId, String source) throws IOException{
if (0 == source.length()){
//todo 抛出异常 导入数据为空
}
BulkRequest request = new BulkRequest();
JsonNode jsonNode = mapper.readTree(source); if (jsonNode.isArray()) {
for (JsonNode node : jsonNode) {
if (isAutoId) {
request.add(new IndexRequest(indexName).source(node.asText(), XContentType.JSON));
} else {
request.add(new IndexRequest(indexName)
.id(node.get("id").asText())
.source(node.asText(), XContentType.JSON));
}
}
}
return client.bulk(request, RequestOptions.DEFAULT);
}

创建索引,这里的settings是设置索引是否设置复制节点、设置分片个数,mappings就和数据库中的表结构一样,用来指定各个字段的类型,同时也可以设置字段是否分词(我们这里使用ik中文分词器)、采用什么分词方式。

   @Test
public void createIdx() throws IOException {
String settings = "" +
" {\n" +
" \"number_of_shards\" : \"2\",\n" +
" \"number_of_replicas\" : \"0\"\n" +
" }";
String mappings = "" +
"{\n" +
" \"properties\": {\n" +
" \"itemId\" : {\n" +
" \"type\": \"keyword\",\n" +
" \"ignore_above\": 64\n" +
" },\n" +
" \"urlId\" : {\n" +
" \"type\": \"keyword\",\n" +
" \"ignore_above\": 64\n" +
" },\n" +
" \"sellAddress\" : {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\", \n" +
" \"search_analyzer\": \"ik_smart\",\n" +
" \"fields\": {\n" +
" \"keyword\" : {\"ignore_above\" : 256, \"type\" : \"keyword\"}\n" +
" }\n" +
" },\n" +
" \"courierFee\" : {\n" +
" \"type\": \"text\n" +
" },\n" +
" \"promotions\" : {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\", \n" +
" \"search_analyzer\": \"ik_smart\",\n" +
" \"fields\": {\n" +
" \"keyword\" : {\"ignore_above\" : 256, \"type\" : \"keyword\"}\n" +
" }\n" +
" },\n" +
" \"originalPrice\" : {\n" +
" \"type\": \"keyword\",\n" +
" \"ignore_above\": 64\n" +
" },\n" +
" \"startTime\" : {\n" +
" \"type\": \"date\",\n" +
" \"format\": \"yyyy-MM-dd HH:mm:ss\"\n" +
" },\n" +
" \"endTime\" : {\n" +
" \"type\": \"date\",\n" +
" \"format\": \"yyyy-MM-dd HH:mm:ss\"\n" +
" },\n" +
" \"title\" : {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\", \n" +
" \"search_analyzer\": \"ik_smart\",\n" +
" \"fields\": {\n" +
" \"keyword\" : {\"ignore_above\" : 256, \"type\" : \"keyword\"}\n" +
" }\n" +
" },\n" +
" \"serviceGuarantee\" : {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\", \n" +
" \"search_analyzer\": \"ik_smart\",\n" +
" \"fields\": {\n" +
" \"keyword\" : {\"ignore_above\" : 256, \"type\" : \"keyword\"}\n" +
" }\n" +
" },\n" +
" \"venue\" : {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\", \n" +
" \"search_analyzer\": \"ik_smart\",\n" +
" \"fields\": {\n" +
" \"keyword\" : {\"ignore_above\" : 256, \"type\" : \"keyword\"}\n" +
" }\n" +
" },\n" +
" \"currentPrice\" : {\n" +
" \"type\": \"keyword\",\n" +
" \"ignore_above\": 64\n" +
" }\n" +
" }\n" +
"}";
clientService.createIndex("idx_item", settings, mappings);
}

分词技巧

  • 索引时最小分词,搜索时最大分词,例如"Java知音"索引时分词包含Java、知音、音、知等,最小粒度分词可以让我们匹配更多的检索需求,但是我们搜索时应该设置最大分词,用“Java”和“知音”去匹配索引库,得到的结果更贴近我们的目的,
  • 对分词字段同时也设置keyword,便于后续排查错误时可以精确匹配搜索,快速定位。

我们向es导入十万条淘宝双11活动数据作为我们的样本数据,数据结构如下所示

{
"_id": "https://detail.tmall.com/item.htm?id=538528948719\u0026skuId=3216546934499",
"卖家地址": "上海",
"快递费": "运费: 0.00元",
"优惠活动": "满199减10,满299减30,满499减60,可跨店",
"商品ID": "538528948719",
"原价": "2290.00",
"活动开始时间": "2016-11-11 00:00:00",
"活动结束时间": "2016-11-11 23:59:59",
"标题": "【天猫海外直营】 ReFa CARAT RAY 黎珐 双球滚轮波光美容仪",
"服务保障": "正品保证;赠运费险;极速退款;七天退换",
"会场": "进口尖货",
"现价": "1950.00"
}

调用上面封装的批量导入方法进行导入

    @Test
public void importAll() throws IOException {
clientService.importAll("idx_item", true, itemService.getItemsJson());
}

我们调用封装的搜索方法进行搜索,搜索产地为武汉、价格在11-149之间的相关酒产品,这与我们淘宝中设置筛选条件搜索商品操作一致。

    @Test
public void search() throws IOException {
SearchResponse search = clientService.search("title", "酒", "currentPrice",
"11", "149", "sellAddress", "武汉");
SearchHits hits = search.getHits();
SearchHit[] hits1 = hits.getHits();
for (SearchHit documentFields : hits1) {
System.out.println( documentFields.getSourceAsString());
}
}

我们得到以下搜索结果,其中_score为某一项的得分,商品就是按照它来排序。

    {
"_index": "idx_item",
"_type": "_doc",
"_id": "Rw3G7HEBDGgXwwHKFPCb",
"_score": 10.995819,
"_source": {
"itemId": "525033055044",
"urlId": "https://detail.tmall.com/item.htm?id=525033055044&skuId=def",
"sellAddress": "湖北武汉",
"courierFee": "快递: 0.00",
"promotions": "满199减10,满299减30,满499减60,可跨店",
"originalPrice": "3768.00",
"startTime": "2016-11-01 00:00:00",
"endTime": "2016-11-11 23:59:59",
"title": "酒嗨酒 西班牙原瓶原装进口红酒蒙德干红葡萄酒6只装整箱送酒具",
"serviceGuarantee": "破损包退;正品保证;公益宝贝;不支持7天退换;极速退款",
"venue": "食品主会场",
"currentPrice": "151.00"
}
}

扩展性思考

  • 商品搜索权重扩展,我们可以利用多种收费方式智能为不同店家提供增加权重,增加曝光度适应自身的营销策略。同时我们经常发现淘宝搜索前列的商品许多为我们之前查看过的商品,这是通过记录用户行为,跑模型等方式智能为这些商品增加权重。
  • 分词扩展,也许因为某些商品的特殊性,我们可以自定义扩展分词字典,更精准、人性化的搜索。
  • 高亮功能,es提供highlight高亮功能,我们在淘宝上看到的商品展示中对搜索关键字高亮,就是通过这种方式来实现。高亮使用方式

使用elasticsearch搭建自己的搜索系统的更多相关文章

  1. Elasticsearch-搭建自己的搜索系统

    参考链接: https://blog.csdn.net/weixin_42730079/article/details/81113806 https://www.cnblogs.com/dreamro ...

  2. 用ElasticSearch搭建自己的搜索和分析引擎

    作者:robben,腾讯高级工程师 商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 导语:互联网产品中的检索功能随处可见.当你的项目规模是百度大搜|商搜或者微信公众号搜索这种体量的时候 ...

  3. 用ElasticSearch搭建自己的搜索和分析引擎【转自腾讯Wetest】

    本文大概地介绍了ES的原理,以及Wetest在使用ES中的一些经验总结.因为ES本身涉及的功能和知识点非常广泛,所以这里重点挑出了实际项目中可能会用到,也可能会踩坑的一些关键点进行了阐述. 一 重要概 ...

  4. TableStore最佳实践:GEO索引打造店铺搜索系统

    摘要: 如何使用TableStore打造店铺搜索系统 一.方案背景 对于一套GEO管理系统,其核心点与瓶颈在于数据库的存储性能与查询能力:一方面,存储服务需要应对海量数据的低延迟存.读,另一方面,存储 ...

  5. 快速搭建应用服务日志收集系统(Filebeat + ElasticSearch + kibana)

    快速搭建应用服务日志收集系统(Filebeat + ElasticSearch + kibana) 概要说明 需求场景,系统环境是CentOS,多个应用部署在多台服务器上,平时查看应用日志及排查问题十 ...

  6. ElasticSearch + Canal 开发千万级的实时搜索系统

    公司是做社交相关产品的,社交类产品对搜索功能需求要求就比较高,需要根据用户城市.用户ID昵称等进行搜索. 项目原先的搜索接口采用SQL查询的方式实现,数据库表采用了按城市分表的方式.但随着业务的发展, ...

  7. ElasticSearch + Canal 开发千万级的实时搜索系统【转】

    公司是做社交相关产品的,社交类产品对搜索功能需求要求就比较高,需要根据用户城市.用户ID昵称等进行搜索. 项目原先的搜索接口采用SQL查询的方式实现,数据库表采用了按城市分表的方式.但随着业务的发展, ...

  8. 用ElasticSearch,LogStash,Kibana搭建实时日志收集系统

    用ElasticSearch,LogStash,Kibana搭建实时日志收集系统 介绍 这套系统,logstash负责收集处理日志文件内容存储到elasticsearch搜索引擎数据库中.kibana ...

  9. Elasticsearch构建全文搜索系统

    目录 前言 一.安装 1.安装elasticsearch 2.启动集群cluster 3.安装管理界面elasticsearch-head 4.安装分词插件elasticsearch-analysis ...

随机推荐

  1. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十)之Inner Classes

    The inner class is a valuable feature because it allows you to group classes that logically belong t ...

  2. stand up meeting 1-6

    今日更新: 1.修复初始最佳战绩显示bug:  初始为击败全国0% 用户 2.挑战结果界面显示“哎,今天的饭又白吃了,回去多吃两碗###”, 去除API返回string中的“###”. 3.分享模块初 ...

  3. 杭电 How far away ?

    There are n houses in the village and some bidirectional roads connecting them. Every day peole alwa ...

  4. 数据结构与算法--树(tree)结构

    树 二叉树 遍历原则:前序遍历是根左右, 中序遍历是左根右,后序遍历是左右根. 二叉搜索树 特点:对于树中的每个节点X,它的左子树中所有节点的值都小于X,右子树中所有节点的值都大于X. 遍历:采取二叉 ...

  5. 面试官:兄弟,说说Java到底是值传递还是引用传递

    二哥,好久没更新面试官系列的文章了啊,真的是把我等着急了,所以特意过来催催.我最近一段时间在找工作,能从二哥的文章中学到一点就多一点信心啊! 说句实在话,离读者 trust you 发给我这段信息已经 ...

  6. Upload-Labs 实验操作记录

    0x01 安装 下载:https://github.com/c0ny1/upload-labs 环境:简单搭建phpstudy环境即可,记得在upload-labs根目录下创建该文件夹 0x02 文件 ...

  7. Lesson0423

    ABS计算几何科普讲座 讲个笑话,ABS又来科普简单知识了 %%% STO szO ABS Orz OTZ %%% 之前完全没有接触过计算几何啊...ABS聚聚给我们从最简单的部分讲起,听得还是很爽的 ...

  8. Scrapy中的crawlspider

    crawlspider 能自动的获取url并提交请求 命令:scrapy genspider -t crawl spidername 'example.cn' 所导入的模块 # -*- coding: ...

  9. KVM 一键批量创建虚拟机

    目录 一.原理 二.基础镜像 2.1.创建基础镜像 2.2. 完善基础镜像 2.3.基础镜像设置权限 3.4 设置 title 3.5.基础镜像XML 三.批量创建机器脚本 四.挂载磁盘多种方式 4. ...

  10. WebRTC 及点对点网络通信机制

    原文请查阅这里,略有删减,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland. 这是 JavaScript 工作原理第十八章. 概述 何为 WebRTC ?首先,字面上已经给出了关于 ...