Elasticsearch基本操作

文章目录

    • [1.1 索引库操作](#1.1 索引库操作)
      • [1.1.1 创建索引库 :](#1.1.1 创建索引库 :)
      • [1.1.2 删除索引库 :](#1.1.2 删除索引库 :)
      • [1.1.3 判断索引库是否存在](#1.1.3 判断索引库是否存在)
    • [1.2 文档操作](#1.2 文档操作)
      • [1.2.1 新增文档](#1.2.1 新增文档)
      • [1.2.2 查询文档](#1.2.2 查询文档)
      • [1.2.3 删除文档](#1.2.3 删除文档)
      • [1.2.4 修改文档](#1.2.4 修改文档)
      • [1.2.5 批量导入文档](#1.2.5 批量导入文档)
    • [1.3 RestClient查询](#1.3 RestClient查询)
      • [1.3.1 普通查询](#1.3.1 普通查询)
      • [1.3.2 复合条件查询](#1.3.2 复合条件查询)
      • [1.3.3 分页排序查询](#1.3.3 分页排序查询)
      • [1.3.4 高亮分页查询](#1.3.4 高亮分页查询)
      • [1.3.5 分页过滤复合查询](#1.3.5 分页过滤复合查询)
      • [1.3.6 处理响应结果](#1.3.6 处理响应结果)
    • [1.4 Mysql和ES数据同步](#1.4 Mysql和ES数据同步)
      • [1.4.1 引入依赖和配置yml](#1.4.1 引入依赖和配置yml)
      • [1.4.2 定义交换机队列名称( 常量 )](#1.4.2 定义交换机队列名称( 常量 ))
      • [1.4.3 声明和绑定交换机与队列( 使用注解不需要声明 )](#1.4.3 声明和绑定交换机与队列( 使用注解不需要声明 ))
      • [1.4.4 编写业务逻辑](#1.4.4 编写业务逻辑)

1.1 索引库操作

引入依赖 :

yaml 复制代码
  <dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>elasticsearch-rest-high-level-client</artifactId>
  </dependency>

因为SpringBoot默认的ES版本是7.17.10,所以我们需要覆盖默认的ES版本:

yaml 复制代码
<properties>
      <maven.compiler.source>11</maven.compiler.source>
      <maven.compiler.target>11</maven.compiler.target>
      <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

初始化RestClient :

java 复制代码
@Bean
public RestHighLevelClient client(){
    return  new RestHighLevelClient(RestClient.builder(
            HttpHost.create("http://192.168.164.128:9200")
    ));
}

在启动类加上如上的代码初始化client.

结合数据表结构创建索引库结构 :

json 复制代码
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 创建索引库 :

java 复制代码
@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 删除索引库 :

java 复制代码
@Test
void testDeleteIndex() throws IOException {
    // 1.创建Request对象
    DeleteIndexRequest request = new DeleteIndexRequest("items");
    // 2.发送请求
    client.indices().delete(request, RequestOptions.DEFAULT);
}

1.1.3 判断索引库是否存在

java 复制代码
@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 新增文档

索引库结构与数据库结构还存在一些差异,因此我们要定义一个索引库结构对应的实体

java 复制代码
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;
}

操作代码 :

java 复制代码
@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 查询文档

java 复制代码
@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 删除文档

java 复制代码
@Test
void testDeleteDocument() throws IOException {
    // 1.准备Request,两个参数,第一个是索引库名,第二个是文档id
    DeleteRequest request = new DeleteRequest("item", "100002644680");
    // 2.发送请求
    client.delete(request, RequestOptions.DEFAULT);
}

1.2.4 修改文档

java 复制代码
@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 批量导入文档

java 复制代码
@Test
void testBulkRequest() throws IOException {
	// 批量查询酒店数据
	List<Hotel> 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 普通查询

java 复制代码
@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 复合条件查询

java 复制代码
@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 分页排序查询

java 复制代码
@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 高亮分页查询

java 复制代码
    @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("<em>")
//						.postTags("</em>")
//		);
		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 分页过滤复合查询

java 复制代码
/**
 * 搜索
 * @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 处理响应结果

java 复制代码
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<String, HighlightField> 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

java 复制代码
<!--amqp-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
yaml 复制代码
  rabbitmq:
    host: 192.168.164.128
    port: 5672
    username: itheima
    password: 123321
    virtual-host: /

1.4.2 定义交换机队列名称( 常量 )

java 复制代码
/**
 * @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 声明和绑定交换机与队列( 使用注解不需要声明 )

java 复制代码
/**
 * @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);
    }
java 复制代码
/**
 * 监听酒店新增或修改的业务
 * @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 编写业务逻辑

java 复制代码
/**
 * 删除数据同步到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);
	}
}
相关推荐
小小工匠2 小时前
ElasticSearch - 深入解析 Elasticsearch Composite Aggregation 的分页与去重机制
elasticsearch·composite·after_key·桶聚合分页
风_流沙2 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch
szxinmai主板定制专家2 小时前
【国产NI替代】基于FPGA的32通道(24bits)高精度终端采集核心板卡
大数据·人工智能·fpga开发
TGB-Earnest3 小时前
【py脚本+logstash+es实现自动化检测工具】
大数据·elasticsearch·自动化
大圣数据星球5 小时前
Fluss 写入数据湖实战
大数据·设计模式·flink
suweijie7685 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
Data跳动11 小时前
Spark内存都消耗在哪里了?
大数据·分布式·spark
woshiabc11112 小时前
windows安装Elasticsearch及增删改查操作
大数据·elasticsearch·搜索引擎
lucky_syq12 小时前
Saprk和Flink的区别
大数据·flink