# ElasticSearchJingDongAndAPIStudy **Repository Path**: zwtgit/elastic-search-jing-dong-and-apistudy ## Basic Information - **Project Name**: ElasticSearchJingDongAndAPIStudy - **Description**: SpringBoot集成ElasticSearch,索引以及文档的API操作和模仿京东做的搜索。 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-10-09 - **Last Updated**: 2022-06-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ElasticSearchJingDongAndAPIStudy #### 介绍 详情请见我的博客:https://www.cnblogs.com/zwtblog/p/15387306.html#%E4%BA%AC%E4%B8%9C%E6%90%9C%E7%B4%A2%E5%AE%9E%E6%88%98 SpringBoot集成ElasticSearch,索引以及文档的API操作和模仿京东做的搜索。 #### 软件架构 ElasticSearch+Vue+SpringBoot # 集成SpringBoot 项目地址:https://gitee.com/zwtgit/elastic-search-jing-dong-and-apistudy 找到原生的依赖 ```xml es-snapshots elasticsearch snapshot repo https://snapshots.elastic.co/maven/ ``` 然后就是要注意导入SpringBoot项目后ES的版本不一致的问题。 例如我导入后: 所以要相应的修改一下 快速开始,使用 源码中提供的对象 ```java package com.zwt.esapi.config; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //Spring俩个步骤 1.找对象 2.放到Spring中 @Configuration public class ESConfig { @Bean public RestHighLevelClient restHighLevelClient(){ RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("127.0.0.1",9200,"http") ) ); return client; } } ``` ------ **具体的API测试** 1. 创建索引 2. 判断索引是否存在 3. 删除索引 4. 创建文档 5. CRUD文档 **关于索引的API操作** ```java @Autowired @Qualifier("restHighLevelClient") //不加也行 但定义的名字要是这个名字 private RestHighLevelClient client; //测试索引的创建 Request PUT lijiatu_index @Test void testCreateIndex() throws IOException { //1. 创建索引请求 CreateIndexRequest request = new CreateIndexRequest("lijiatu_index"); //2. 客户端执行请求 CreateIndexResponse createIndexRequest = client.indices().create(request, RequestOptions.DEFAULT); System.out.println(createIndexRequest); } //测试获取索引,判断其是否存在 @Test void testExistIndex() throws IOException { GetIndexRequest request = new GetIndexRequest("lijiatu_index"); boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); System.out.println(exists); } //测试删除索引 @Test void testDeleteIndex() throws IOException { DeleteIndexRequest request = new DeleteIndexRequest("lijiatu_index"); //删除 AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT); System.out.println(delete.isAcknowledged()); } ``` **关于文档的API操作** ```java //测试添加文档 @Test void testAddDocument() throws IOException { //创建对象 User user = new User("李嘉图", 12); //创建请求 IndexRequest request = new IndexRequest("ahui_index"); //规则 put lijiatu_index/_doc/1 request.id("1"); request.timeout(TimeValue.timeValueSeconds(1)); request.timeout("1s"); //将我们的数据放入请求 json (使用fastjson进行转换) IndexRequest source = request.source(JSON.toJSONString(user), XContentType.JSON); //客户端发送请求, 获取响应的结果 IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); //返回具体的json信息 System.out.println(indexResponse.toString()); //对应我们命令返回的状态 CREATED System.out.println(indexResponse.status()); } //获取文档 @Test void testIsExistes() throws IOException { GetRequest getRequest = new GetRequest("lijiatu_index", "1"); //不获取返回的_source 的上下文了 getRequest.fetchSourceContext(new FetchSourceContext(false)); getRequest.storedFields("_none_"); boolean exists = client.exists(getRequest, RequestOptions.DEFAULT); System.out.println(exists); } //获取文档的信息 @Test void testGetDocument() throws IOException { GetRequest getRequest = new GetRequest("lijiatu_index", "1"); GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT); //打印文档的内容 System.out.println(getResponse.getSourceAsString()); //返回的全部内容和命令是一样的 System.out.println(getResponse); } //获取文档的信息 @Test void testUpdateRequest() throws IOException { UpdateRequest updateRequest = new UpdateRequest("lijiatu_index", "1"); updateRequest.timeout("1s"); User user = new User("李嘉图学Java", 22); updateRequest.doc(JSON.toJSONString(user), XContentType.JSON); UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT); System.out.println(updateResponse.status()); } //删除文档记录 @Test void testDeleteRequest() throws IOException { DeleteRequest deleteRequest = new DeleteRequest("lijiatu_index", "1"); deleteRequest.timeout("1s"); DeleteResponse delete = client.delete(deleteRequest, RequestOptions.DEFAULT); System.out.println(delete.status()); } //特殊的,实际项目中一般都会批量插入数据 @Test void testBulkRequest() throws IOException { BulkRequest bulkRequest = new BulkRequest(); bulkRequest.timeout("10s"); ArrayList userList = new ArrayList<>(); userList.add(new User("lijiatu1", 18)); userList.add(new User("lijiatu1", 18)); userList.add(new User("lijiatu1", 18)); userList.add(new User("lijiatu1", 18)); userList.add(new User("lijiatu1", 18)); userList.add(new User("lijiatu1", 18)); //批量处理请求 for (int i = 0; i < userList.size(); i++) { //批量更新和批量修改等, 就在这里修改对应的请求就可以了 bulkRequest.add(new IndexRequest("ahui_index") .id("" + (i + 1)) //不加id的话会默认生成随机id .source(JSON.toJSONString(userList.get(i)), XContentType.JSON)); } BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT); //是够失败, 返回false代表成功 System.out.println(bulkResponse.hasFailures()); } //查询 //SearchRequest 搜索请求 //SearchSourceBuilder 条件构造 //HighLightBuilder 构建高亮 //xxx QueryBuilder 对应我们刚才看到的所有命令 @Test void testSearch() throws IOException { SearchRequest searchRequest = new SearchRequest("lijiatu_index"); //构建搜索条件 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); //查询条件,我们可以使用QueryBuilders 工具类来实现 //QueryBuilders.termQuery 精确匹配 //QueryBuilders.matchAllQuery 匹配所有 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "lijiatu"); // MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery(); sourceBuilder.query(termQueryBuilder); sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); searchRequest.source(sourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); System.out.println(JSON.toJSONString(searchResponse.getHits())); System.out.println("=========================================="); for (SearchHit documentFileds : searchResponse.getHits().getHits()) { System.out.println(documentFileds.getSourceAsMap()); } } ``` ------ **源码** ```java @Import({ RestClientConfigurations.RestClientBuilderConfiguration.class, RestClientConfigurations.RestHighLevelClientConfiguration.class, RestClientConfigurations.RestClientFallbackConfiguration.class }) /* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.autoconfigure.elasticsearch.rest; import java.time.Duration; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Elasticsearch rest client infrastructure configurations. * * @author Brian Clozel * @author Stephane Nicoll */ class RestClientConfigurations { @Configuration(proxyBeanMethods = false) static class RestClientBuilderConfiguration { @Bean @ConditionalOnMissingBean RestClientBuilder elasticsearchRestClientBuilder(RestClientProperties properties, ObjectProvider builderCustomizers) { HttpHost[] hosts = properties.getUris().stream().map(HttpHost::create).toArray(HttpHost[]::new); RestClientBuilder builder = RestClient.builder(hosts); PropertyMapper map = PropertyMapper.get(); map.from(properties::getUsername).whenHasText().to((username) -> { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(), properties.getPassword()); credentialsProvider.setCredentials(AuthScope.ANY, credentials); builder.setHttpClientConfigCallback( (httpClientBuilder) -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); }); builder.setRequestConfigCallback((requestConfigBuilder) -> { map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis) .to(requestConfigBuilder::setConnectTimeout); map.from(properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis) .to(requestConfigBuilder::setSocketTimeout); return requestConfigBuilder; }); builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } } @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RestHighLevelClient.class) static class RestHighLevelClientConfiguration { @Bean @ConditionalOnMissingBean RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) { return new RestHighLevelClient(restClientBuilder); } @Bean @ConditionalOnMissingBean RestClient elasticsearchRestClient(RestClientBuilder builder, ObjectProvider restHighLevelClient) { RestHighLevelClient client = restHighLevelClient.getIfUnique(); if (client != null) { return client.getLowLevelClient(); } return builder.build(); } } @Configuration(proxyBeanMethods = false) static class RestClientFallbackConfiguration { @Bean @ConditionalOnMissingBean RestClient elasticsearchRestClient(RestClientBuilder builder) { return builder.build(); } } } ``` # 京东搜索实战 项目地址:https://gitee.com/zwtgit/elastic-search-jing-dong-and-apistudy 项目导入静态资源后: 数据爬取京东的首页,可以看之前的爬虫小项目爬取数据 爬虫项目地址:https://www.cnblogs.com/zwtblog/p/15216808.html **具体的业务编写** ```java package com.zwt.service; import com.alibaba.fastjson.JSON; import com.zwt.pojo.Content; import com.zwt.utils.HtmlParseUtil; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @Service public class ContentService { @Autowired private RestHighLevelClient restHighLevelClient; //1.解析数据 放入 ES 索引中 public Boolean parseContent(String ketwords) throws IOException { List contents = new HtmlParseUtil().parseJD(ketwords); //把查询出来的数据放入 ES中 BulkRequest bulkRequest = new BulkRequest(); bulkRequest.timeout("2m"); for (int i = 0; i < contents.size(); i++) { bulkRequest.add( new IndexRequest("jd_goods") .source(JSON.toJSONString(contents.get(i)), XContentType.JSON) ); } BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); return !bulk.hasFailures(); } //2.获取这些数据 实现搜索功能 public List> searchPage(String keyword, int pageNo, int pageSize) throws IOException { if (pageNo <= 1) { pageNo = 1; } //条件搜索 SearchRequest searchRequest = new SearchRequest("jd_goods"); SearchSourceBuilder searcheBuilder = new SearchSourceBuilder(); //分页 searcheBuilder.from(pageNo); searcheBuilder.size(pageSize); //精准匹配 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword); searcheBuilder.query(termQueryBuilder); searcheBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); //执行搜索 searchRequest.source(searcheBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); //解析结果 ArrayList> list = new ArrayList<>(); for (SearchHit documentFields : searchResponse.getHits().getHits()) { list.add(documentFields.getSourceAsMap()); } return list; } } ``` ```java package com.zwt.controller; import com.zwt.service.ContentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.List; import java.util.Map; //请求编写 @RestController public class ContentController { @Autowired private ContentService contentService; @GetMapping("/parse/{keyword}") public Boolean parse(@PathVariable("keyword") String keyword) throws Exception { return contentService.parseContent(keyword); } @GetMapping("/search/{keyword}/{pageNo}/{pageSize}") public List> search(@PathVariable("keyword") String keyword, @PathVariable("pageNo") int pageNo, @PathVariable("pageSize") int pageSize) throws IOException { return contentService.searchPage(keyword, pageNo, pageSize); } } ``` 业务基本编写完成,准备进行前后端交换 最后实现“高亮” 效果图