Elasticsearch基本操作
文章目录
- 1.1 索引库操作
- 1.1.1 创建索引库 :
- 1.1.2 删除索引库 :
- 1.1.3 判断索引库是否存在
- 1.2 文档操作
- 1.2.1 新增文档
- 1.2.2 查询文档
- 1.2.3 删除文档
- 1.2.4 修改文档
- 1.2.5 批量导入文档
- 1.3 RestClient查询
- 1.3.1 普通查询
- 1.3.2 复合条件查询
- 1.3.3 分页排序查询
- 1.3.4 高亮分页查询
- 1.3.5 分页过滤复合查询
- 1.3.6 处理响应结果
- 1.4 Mysql和ES数据同步
- 1.4.1 引入依赖和配置yml
- 1.4.2 定义交换机队列名称( 常量 )
- 1.4.3 声明和绑定交换机与队列( 使用注解不需要声明 )
- 1.4.4 编写业务逻辑
1.1 索引库操作
引入依赖 :
(图片来源网络,侵删)org.elasticsearch.client >elasticsearch-rest-high-level-client
因为SpringBoot默认的ES版本是7.17.10,所以我们需要覆盖默认的ES版本:
11 11 7.12.1
初始化RestClient :
@Bean public RestHighLevelClient client(){ return new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.164.128:9200") )); }
在启动类加上如上的代码初始化client.
结合数据表结构创建索引库结构 :
PUT /items { "mappings": { "properties": { "id": { "type": "keyword" }, "name":{ "type": "text", "analyzer": "ik_max_word", "copy_to": "all" }, "price":{ "type": "integer" }, "stock":{ "type": "integer" }, "image":{ "type": "keyword", "index": false }, "category":{ "type": "keyword", "copy_to": "all" }, "brand":{ "type": "keyword", "copy_to": "all" }, "sold":{ "type": "integer" }, "commentCount":{ "type": "integer" }, "isAD":{ "type": "boolean" }, "updateTime":{ "type": "date" }, "all":{ "type": "text", "analyzer": "ik_max_word" } } } }
1.1.1 创建索引库 :
@Test void testCreateIndex() throws IOException { // 1.创建Request对象 CreateIndexRequest request = new CreateIndexRequest("items"); // 2.准备请求参数 request.source(MAPPING_TEMPLATE, XContentType.JSON); // 3.发送请求 client.indices().create(request, RequestOptions.DEFAULT); } // 这个可以放到constants里面 static final String MAPPING_TEMPLATE = "{\n" + " \"mappings\": {\n" + " \"properties\": {\n" + " \"id\": {\n" + " \"type\": \"keyword\"\n" + " },\n" + " \"name\":{\n" + " \"type\": \"text\",\n" + " \"analyzer\": \"ik_max_word\"\n" + " },\n" + " \"price\":{\n" + " \"type\": \"integer\"\n" + " },\n" + " \"stock\":{\n" + " \"type\": \"integer\"\n" + " },\n" + " \"image\":{\n" + " \"type\": \"keyword\",\n" + " \"index\": false\n" + " },\n" + " \"category\":{\n" + " \"type\": \"keyword\"\n" + " },\n" + " \"brand\":{\n" + " \"type\": \"keyword\"\n" + " },\n" + " \"sold\":{\n" + " \"type\": \"integer\"\n" + " },\n" + " \"commentCount\":{\n" + " \"type\": \"integer\"\n" + " },\n" + " \"isAD\":{\n" + " \"type\": \"boolean\"\n" + " },\n" + " \"updateTime\":{\n" + " \"type\": \"date\"\n" + " }\n" + " }\n" + " }\n" + "}";
1.1.2 删除索引库 :
@Test void testDeleteIndex() throws IOException { // 1.创建Request对象 DeleteIndexRequest request = new DeleteIndexRequest("items"); // 2.发送请求 client.indices().delete(request, RequestOptions.DEFAULT); }
1.1.3 判断索引库是否存在
@Test void testExistsIndex() throws IOException { // 1.创建Request对象 GetIndexRequest request = new GetIndexRequest("items"); // 2.发送请求 boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); // 3.输出 System.err.println(exists ? "索引库已经存在!" : "索引库不存在!"); }
1.2 文档操作
1.2.1 新增文档
索引库结构与数据库结构还存在一些差异,因此我们要定义一个索引库结构对应的实体
package com.hmall.item.domain.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.time.LocalDateTime; @Data @ApiModel(description = "索引库实体") public class ItemDTO{ @ApiModelProperty("商品id") private String id; @ApiModelProperty("商品名称") private String name; @ApiModelProperty("价格(分)") private Integer price; @ApiModelProperty("库存数量") private Integer stock; @ApiModelProperty("商品图片") private String image; @ApiModelProperty("类目名称") private String category; @ApiModelProperty("品牌名称") private String brand; @ApiModelProperty("销量") private Integer sold; @ApiModelProperty("评论数") private Integer commentCount; @ApiModelProperty("是否是推广广告,true/false") private Boolean isAD; @ApiModelProperty("更新时间") private LocalDateTime updateTime; }
操作代码 :
@Test void testAddDocument() throws IOException { // 1.根据id查询商品数据 Item item = itemService.getById(100002644680L); // 2.转换为文档类型 ItemDTO itemDTO = BeanUtil.copyProperties(item, ItemDTO.class); // 3.将ItemDTO转json String doc = JSONUtil.toJsonStr(itemDTO); // 1.准备Request对象 IndexRequest request = new IndexRequest("items").id(itemDTO.getId()); // 2.准备Json文档 request.source(doc, XContentType.JSON); // 3.发送请求 client.index(request, RequestOptions.DEFAULT); }
1.2.2 查询文档
@Test void testGetDocumentById() throws IOException { // 1.准备Request对象 GetRequest request = new GetRequest("items").id("100002644680"); // 2.发送请求 GetResponse response = client.get(request, RequestOptions.DEFAULT); // 3.获取响应结果中的source String json = response.getSourceAsString(); ItemDTO itemDTO = JSONUtil.toBean(json, ItemDTO.class); System.out.println("itemDTO = " + itemDTO); }
1.2.3 删除文档
@Test void testDeleteDocument() throws IOException { // 1.准备Request,两个参数,第一个是索引库名,第二个是文档id DeleteRequest request = new DeleteRequest("item", "100002644680"); // 2.发送请求 client.delete(request, RequestOptions.DEFAULT); }
1.2.4 修改文档
@Test void testUpdateDocument() throws IOException { // 1.准备Request UpdateRequest request = new UpdateRequest("items", "100002644680"); // 2.准备请求参数 request.doc( "price", 58800, "commentCount", 1 ); // 3.发送请求 client.update(request, RequestOptions.DEFAULT); }
1.2.5 批量导入文档
@Test void testBulkRequest() throws IOException { // 批量查询酒店数据 List hotels = hotelService.list(); // 1.创建Request BulkRequest request = new BulkRequest(); // 2.准备参数,添加多个新增的Request for (Hotel hotel : hotels) { // 2.1.转换为文档类型HotelDoc HotelDoc hotelDoc = new HotelDoc(hotel); // 2.2.创建新增文档的Request对象 request.add(new IndexRequest("hotel") .id(hotelDoc.getId().toString()) .source(JSONUtil.toJsonStr(hotelDoc), XContentType.JSON)); } // 3.发送请求 client.bulk(request, RequestOptions.DEFAULT); }
1.3 RestClient查询
1.3.1 普通查询
@Test void testMatch() throws IOException { // 1. 创建Request对象 SearchRequest request = new SearchRequest("hotel"); // 2. 组织请求参数 request.source().query(QueryBuilders.matchQuery("all", "如家")); // 3. 发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); handleResponse(response); }
1.3.2 复合条件查询
@Test void testBool() throws IOException { // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.准备DSL // 2.1.准备BooleanQuery BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 2.2.添加term boolQuery.must(QueryBuilders.termQuery("city", "杭州")); // 2.3.添加range boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250)); request.source().query(boolQuery); // 3.发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.解析响应 handleResponse(response); }
1.3.3 分页排序查询
@Test void testPageAndSort() throws IOException { int pageNo = 1, pageSize = 5; // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.1.搜索条件参数 //request.source().query(QueryBuilders.matchAllQuery()); request.source().query(QueryBuilders.matchQuery("all", "如家")); // 2.2.排序参数 request.source().sort("price", SortOrder.ASC); // 2.3.分页参数 request.source().from((pageNo - 1) * pageSize).size(pageSize); // 3.发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.解析响应 handleResponse(response); }
1.3.4 高亮分页查询
@Test void testHighLight() throws IOException { int pageNo = 1, pageSize = 3; // 1.准备Request SearchRequest request = new SearchRequest("hotel"); // 2.1.搜索条件参数 request.source().query(QueryBuilders.matchQuery("all", "如家")); // 2.2 高亮条件 // request.source().highlighter( // new HighlightBuilder() // .field("name") // .preTags("") // .postTags("") // ); request.source().highlighter( new HighlightBuilder() .field("name") .field("brand") .requireFieldMatch(false)); // 2.3.排序参数 request.source().sort("price", SortOrder.ASC); // 2.4.分页参数 request.source().from((pageNo - 1) * pageSize).size(pageSize); // 3.发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 4.解析响应 handleResponse(response); }
1.3.5 分页过滤复合查询
/** * 搜索 * @param params 请求参数 * @return 分页结果 */ @Override public PageResult search(RequestParams params) { try { // 1. 准备Request SearchRequest request = new SearchRequest("hotel"); // 2.1 query boolBasicQuery(params, request); // 2.2 分页 int pageNo = params.getPage(); int pageSize = params.getSize(); request.source().from((pageNo - 1) * pageSize).size(pageSize); // 3. 发送请求 SearchResponse response = client.search(request, RequestOptions.DEFAULT); return handleResponse(response); } catch (IOException e) { throw new RuntimeException(e); } } /** * 构建基本的bool查询 * @param params 请求参数 * @param request 请求对象 */ private void boolBasicQuery(RequestParams params, SearchRequest request) { // 1.构建BooleanQuery BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); // 关键字搜索 String key = params.getKey(); if (StrUtil.isEmpty(key)) { boolQuery.must(QueryBuilders.matchAllQuery()); } else { boolQuery.must(QueryBuilders.matchQuery("all", key)); } // 城市条件 String city = params.getCity(); if(StrUtil.isNotEmpty(city)){ boolQuery.filter(QueryBuilders.termQuery("city", city)); } // 品牌条件 String brand = params.getBrand(); if(StrUtil.isNotEmpty(brand)){ boolQuery.filter(QueryBuilders.termQuery("brand", brand)); } // 星级条件 String starName = params.getStarName(); if(StrUtil.isNotEmpty(starName)){ boolQuery.filter(QueryBuilders.termQuery("starName", starName)); } // 价格条件 Integer minPrice = params.getMinPrice(); Integer maxPrice = params.getMaxPrice(); if(minPrice != null && maxPrice != null){ boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice)); } request.source().query(boolQuery); // // 2.算分控制 // FunctionScoreQueryBuilder functionScoreQuery = // QueryBuilders.functionScoreQuery( // // 原始查询,相关性算分的查询 // boolQuery, // // function score的数组 // new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // // 其中的一个function score 元素 // new FunctionScoreQueryBuilder.FilterFunctionBuilder( // // 过滤条件 // QueryBuilders.termQuery("isAD", true), // // 算分函数 // ScoreFunctionBuilders.weightFactorFunction(10) // ) // }); // request.source().query(functionScoreQuery); }
1.3.6 处理响应结果
private void handleResponse(SearchResponse response) { SearchHits searchHits = response.getHits(); // 1. 获取总条数 long total = searchHits.getTotalHits().value; log.info("总条数:{}", total); // 2. 遍历结果数组 SearchHit[] hits = searchHits.getHits(); for(SearchHit hit : hits) { // 3. 获取JSON字符串 String json = hit.getSourceAsString(); // 4. 转换为Java对象 HotelDoc hotelDoc = JSONUtil.toBean(json, HotelDoc.class); // 5. 获取高亮结果 Map highlightFields = hit.getHighlightFields(); if(CollUtil.isNotEmpty(highlightFields)){ // 5.1 有高亮结果 获取name的高亮结果 HighlightField field1 = highlightFields.get("name"); HighlightField field2 = highlightFields.get("brand"); if(field1 != null && field2 != null){ String name = field1.getFragments()[0].string(); String brand = field2.getFragments()[0].string(); hotelDoc.setName(name); hotelDoc.setBrand(brand); } } log.info("HotelDoc:{}", hotelDoc); } }
1.4 Mysql和ES数据同步
这里我使用的是rbmq做异步通知es更新数据
1.4.1 引入依赖和配置yml
org.springframework.boot spring-boot-starter-amqp
rabbitmq: host: 192.168.164.128 port: 5672 username: itheima password: 123321 virtual-host: /
1.4.2 定义交换机队列名称( 常量 )
/** * @author Ccoo * 2024/2/12 */ public class MqConstants { /** * 交换机 */ public final static String HOTEL_EXCHANGE = "hotel.topic"; /** * 监听新增和修改的队列 */ public final static String HOTEL_INSERT_QUEUE = "hotel.insert.queue"; /** * 监听删除的队列 */ public final static String HOTEL_DELETE_QUEUE = "hotel.delete.queue"; /** * 新增或修改的RoutingKey */ public final static String HOTEL_INSERT_KEY = "hotel.insert"; /** * 删除的RoutingKey */ public final static String HOTEL_DELETE_KEY = "hotel.delete"; }
1.4.3 声明和绑定交换机与队列( 使用注解不需要声明 )
/** * @author Ccoo * 2024/2/12 */ @Configuration public class MqConfig { @Bean public TopicExchange topicExchange(){ return new TopicExchange(MqConstants.HOTEL_EXCHANGE, true, false); } @Bean public Queue insertQueue(){ return new Queue(MqConstants.HOTEL_INSERT_QUEUE, true); } @Bean public Queue deleteQueue(){ return new Queue(MqConstants.HOTEL_DELETE_QUEUE, true); } @Bean public Binding insertQueueBinding(){ return BindingBuilder.bind(insertQueue()).to(topicExchange()).with(MqConstants.HOTEL_INSERT_KEY) } @Bean public Binding deleteQueueBinding(){ return BindingBuilder.bind(deleteQueue()).to(topicExchange()).with(MqConstants.HOTEL_DELETE_KEY) } } /** * 监听酒店新增或修改的业务 * @param id 酒店id */ @RabbitListener(queues = MqConstants.HOTEL_INSERT_QUEUE) public void listenHotelInsertOrUpdate(Long id){ hotelService.insertById(id); } /** * 监听酒店删除的业务 * @param id 酒店id */ @RabbitListener(queues = MqConstants.HOTEL_DELETE_QUEUE) public void listenHotelDelete(Long id){ hotelService.deleteById(id); }
/** * 监听酒店新增或修改的业务 * @param id 酒店id */ @RabbitListener(bindings = @QueueBinding( value = @Queue(name = MqConstants.HOTEL_INSERT_QUEUE, durable = "true"), exchange = @Exchange(name = MqConstants.HOTEL_EXCHANGE, type = ExchangeTypes.TOPIC), key = MqConstants.HOTEL_INSERT_KEY )) public void listenHotelInsertOrUpdate(Long id){ hotelService.insertById(id); } /** * 监听酒店删除的业务 * @param id 酒店id */ @RabbitListener(bindings = @QueueBinding( value = @Queue(name = MqConstants.HOTEL_DELETE_QUEUE, durable = "true"), exchange = @Exchange(name = MqConstants.HOTEL_EXCHANGE, type = ExchangeTypes.TOPIC), key = MqConstants.HOTEL_DELETE_KEY )) public void listenHotelDelete(Long id){ hotelService.deleteById(id); }
1.4.4 编写业务逻辑
/** * 删除数据同步到ES * @param id */ @Override public void deleteById(Long id) { try { // 1.准备Request DeleteRequest request = new DeleteRequest("hotel", id.toString()); // 2.发送请求 client.delete(request, RequestOptions.DEFAULT); } catch (IOException e) { throw new RuntimeException(e); } } /** * 新增或修改数据同步到ES * @param id */ @Override public void insertById(Long id) { try { // 0.根据id查询酒店数据 Hotel hotel = getById(id); // 转换为文档类型 HotelDoc hotelDoc = new HotelDoc(hotel); // 1.准备Request对象 IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString()); // 2.准备Json文档 request.source(JSONUtil.toJsonStr(hotelDoc), XContentType.JSON); // 3.发送请求 client.index(request, RequestOptions.DEFAULT); } catch (IOException e) { throw new RuntimeException(e); } }
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。