如何使用elasticsearch创建自己的搜索系统
发布时间:2022-01-17 13:45:42 所属栏目:MySql教程 来源:互联网
导读:这期内容当中小编将会给大家带来有关如何使用elasticsearch搭建自己的搜索系统,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。 什么是elasticsearch# Elasticsearch 是一个开源的高度可扩展的全文搜索和分析引擎,拥
这期内容当中小编将会给大家带来有关如何使用elasticsearch搭建自己的搜索系统,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。 什么是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! 如何使用elasticsearch搭建自己的搜索系统 此图是引入的最新版本的依赖,我们可以看到它所使用的es-high-client也为6.8.7,而es7.x版本都已经更新很久了,这里许多新特性都无法使用,所以版本滞后是他最大的问题。而且它的底层也是highclient,我们操作highclient可以更灵活。我呆过的两个公司均未采用此客户端。 elasticsearch-rest-high-level-client# 这是官方推荐的客户端,支持最新的es,其实使用起来也很便利,因为是官方推荐所以在特性的操作上肯定优于前者。而且该客户端与TransportClient不同,不存在并发瓶颈的问题,官方首推,必为精品! 搭建自己的迷你搜索系统# 引入es相关依赖,除此之外需引入springboot-web依赖、jackson依赖以及lombok依赖等。 Copy <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 Copyes.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 Copy@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搜索系统封装源码 Copy@Servicepublic 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中文分词器)、采用什么分词方式。 Copy @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": 64n" + " },n" + " "urlId" : {n" + " "type": "keyword",n" + " "ignore_above": 64n" + " },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": "textn" + " },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": 64n" + " },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" + &nbs (编辑:大连站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
站长推荐