[ ElasticSearch ] 各种常规操作方法整理

Posted 削尖的螺丝刀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ ElasticSearch ] 各种常规操作方法整理相关的知识,希望对你有一定的参考价值。

本文为SpringBoot整合ElasticSearch后,对一些基本的操作方法做的整理,话不多说,直接上Code…
 
🎏 ps: SpringBoot整合ElasticSearch

1.首先我们新建一个测试实体类Product
package com.example.demo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Setting;

/**
 * @Description: 这里面的信息会在Boot项目启动时,自动加载到ES服务器里面
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
// 属于的索引群
@Document(indexName = "shopping")
// 分三片,没指定replicas的话默认副本为1
@Setting(shards = 3)
public class Product {
    //必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @Id
    private Long id;//商品唯一标识

    /**
     * type : 字段数据类型
     * analyzer : 分词器类型
     * index : 是否索引(默认:true)
     * Keyword : 短语,不进行分词
     */
    // @Field注解意思很明显了,指定Field类型,且使用ik分词器
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String title;//商品名称

    @Field(type = FieldType.Keyword)
    private String category;//分类名称

    @Field(type = FieldType.Double)
    private Double price;//商品价格

    @Field(type = FieldType.Keyword, index = false)
    private String images;//图片地址
}

 
 

2.建立一个dao接口继承ElasticsearchRepository来操作ES
package com.example.demo;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;


/**
 * @Description: ElasticsearchRepository 是ES的专属dao类,所有使用ES的dao都要继承这个类,然后泛型中申明对象,类似通用Mapper一样的玩法
 */
@Repository
public interface ProductDao extends ElasticsearchRepository<Product, Long> {
   
}

 
 
 

3.各种常规操作方法如下(写在Test类中)
package com.example.demo;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@SpringBootTest
class ElasticSearchApplicationTests {

	//注入 ElasticsearchRestTemplate
	@Autowired
	private ElasticsearchRestTemplate elasticsearchRestTemplate;

	@Autowired
	private ProductDao productDao;


	// 注意我们的项目启动后,Product的索引信息会被自动加载到ES里
	// 这里做一个删除索引测试
	@Test
	public void deleteIndex(){
		//创建索引,系统初始化会自动创建索引
		boolean flg = elasticsearchRestTemplate.indexOps(Product.class).delete();
		System.out.println("删除索引 = " + flg);
	}


	/**
	 * 新增
	 */
	@Test
	public void save(){
		Product product = new Product();
		product.setId(2L);
		product.setTitle("华为手机");
		product.setCategory("手机");
		product.setPrice(2999.0);
		product.setImages("http://www.TestByKyle/hw.jpg");
		productDao.save(product);
	}
	//POSTMAN, GET http://localhost:9200/product/_doc/2

	//修改
	@Test
	public void update(){
		Product product = new Product();
		product.setId(2L);
		product.setTitle("小米 2 手机");
		product.setCategory("手机");
		product.setPrice(9999.0);
		product.setImages("http://www.TestByKyle/xm.jpg");
		productDao.save(product);
	}
	//POSTMAN, GET http://localhost:9200/product/_doc/2


	//根据 id 查询
	@Test
	public void findById(){
		Product product = productDao.findById(2L).get();
		System.out.println(product);
	}

	@Test
	public void findAll(){
		Iterable<Product> products = productDao.findAll();
		for (Product product : products) {
			System.out.println(product);
		}
	}

	//删除
	@Test
	public void delete(){
		Product product = new Product();
		product.setId(2L);
		productDao.delete(product);
	}
	//POSTMAN, GET http://localhost:9200/product/_doc/2

	//批量新增
	@Test
	public void saveAll(){
		/*elasticsearchRestTemplate.indexOps(Product.class).putMapping();*/
		List<Product> productList = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			Product product = new Product();
			product.setId(Long.valueOf(i));
			product.setTitle("["+i+"]小米手机");
			product.setCategory("手机");
			product.setPrice(1999.0 + i);
			product.setImages("http://www.TestByKyle/xm.jpg");
			productList.add(product);
		}
		for (int i = 10; i < 20; i++) {
			Product product = new Product();
			product.setId(Long.valueOf(i));
			product.setTitle("["+i+"]华为电脑");
			product.setCategory("电脑");
			product.setPrice(1999.0 + i);
			product.setImages("http://www.TestByKyle/hw.jpg");
			productList.add(product);
		}
		for (int i = 20; i < 30; i++) {
			Product product = new Product();
			product.setId(Long.valueOf(i));
			product.setTitle("["+i+"]红米电脑");
			product.setCategory("电脑");
			product.setPrice(1999.0 + i);
			product.setImages("http://www.TestByKyle/hm.jpg");
			productList.add(product);
		}
		productDao.saveAll(productList);
	}

	//分页查询
	@Test
	public void findByPageable(){
		//设置排序(排序方式,正序还是倒序,排序的 id)
		Sort sort = Sort.by(Sort.Direction.DESC,"id");
		int currentPage=0;//当前页,第一页从 0 开始, 1 表示第二页
		int pageSize = 30;//每页显示多少条
		//设置查询分页
		PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
		//分页查询
		Page<Product> productPage = productDao.findAll(pageRequest);
		for (Product Product : productPage.getContent()) {
			System.out.println(Product);
		}
	}



	//使用Template分页查询
	@Test
	public void findByTemplatePageable(){
		NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().
				withQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("category", "电脑")))
				.withPageable(PageRequest.of(0, 20)) //加上分页参数
				.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC))
				.build();
		SearchHits<Product> search = elasticsearchRestTemplate.search(searchQuery, Product.class);

		for (SearchHit<Product> Product : search.getSearchHits()) {
			System.out.println(Product);
		}
	}


	//查询高亮信息
	@Test
	public void findByHighlightField(){
		//根据一个值查询多个字段  并高亮显示  这里的查询是取并集,即多个字段只需要有一个字段满足即可
		//需要查询的字段
		BoolQueryBuilder boolQueryBuilder= QueryBuilders.boolQuery()
				.should(QueryBuilders.matchQuery("title","红米"))
				.should(QueryBuilders.matchQuery("category","电脑"));
		//构建高亮查询
		NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(boolQueryBuilder)
				.withHighlightFields(
						new HighlightBuilder.Field("title")
						,new HighlightBuilder.Field("category"))
				.withHighlightBuilder(new HighlightBuilder().preTags("<span style='color:red'>").postTags("</span>"))
				.build();
		//查询
		SearchHits<Product> search = elasticsearchRestTemplate.search(searchQuery, Product.class);
		//得到查询返回的内容
		List<SearchHit<Product>> searchHits = search.getSearchHits();
		//设置一个最后需要返回的实体类集合
		List<Product> products = new ArrayList<>();
		//遍历返回的内容进行处理
		for(SearchHit<Product> searchHit:searchHits){
			//高亮的内容
			Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
			//将高亮的内容填充到content中
			searchHit.getContent().setTitle(highlightFields.get("title")==null ? searchHit.getContent().getTitle():highlightFields.get("title").get(0));
			searchHit.getContent().setCategory(highlightFields.get("category")==null ? searchHit.getContent().getCategory():highlightFields.get("category").get(0));
			System.out.println(searchHit.getContent().toString());
			//放到实体类中
			products.add(searchHit.getContent());
		}
		System.out.println("________________________________________________________________");
		products.stream().forEach(System.out::println);
	}

}

 
 
 

在Dao中自定义操作方法


这里拓展讲一个东西,就是继承了ElasticsearchRepository的Dao可根据命名规则来自定义查询方法,不需要具体实现即可使用,这是SpringDataJPA的玩法。

具体规则如下:

键字使用示例等同于的ES查询
AndfindByNameAndPrice{“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
OrfindByNameOrPrice{“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
IsfindByName{“bool” : {“must” : {“field” : {“name” : “?”}}}}
NotfindByNameNot{“bool” : {“must_not” : {“field” : {“name” : “?”}}}}
BetweenfindByPriceBetween{“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : ?,”include_lower” : true,”include_upper” : true}}}}}
LessThanEqualfindByPriceLessThan{“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}}
GreaterThanEqualfindByPriceGreaterThan{“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”include_lower” : true,”include_upper” : true}}}}}
BeforefindByPriceBefore{“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}}
AfterfindByPriceAfter{“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”include_lower” : true,”include_upper” : true}}}}}
LikefindByNameLike{“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,”analyze_wildcard” : true}}}}}
StartingWithfindByNameStartingWith{“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,”analyze_wildcard” : true}}}}}
EndingWithfindByNameEndingWith{“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,”analyze_wildcard” : true}}}}}
Contains/ContainingfindByNameContaining{“bool” : {“must” : {“field” : {“name” : {“query” : “?”,”analyze_wildcard” : true}}}}}
InfindByNameIn(Collectionnames){“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}}
NotInfindByNameNotIn(Collectionnames){“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}}
TruefindByAvailableTrue{“bool” : {“must” : {“field” : {“available” : true}}}}
FalsefindByAvailableFalse{“bool” : {“must” : {“field” : {“available” : false}}}}
OrderByfindByAvailableTrueOrderByNameDesc{“sort” : [{ “name” : {“order” : “desc”} }],”bool” : {“must” : {“field” : {“available” : true}}}}
比如我根据当前实体类,定义一个 findByTitle 方法(不需要具体实现,直接就可以使用)
package com.example.demo;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Description: ElasticsearchRepository 是ES的专属dao类,所有使用ES的dao都要继承这个类,然后泛型中申明对象,类似通用Mapper一样的玩法
 */
@Repository
public interface ProductDao extends ElasticsearchRepository<Product, Long> {
    List<Product> findByTitle(<

以上是关于[ ElasticSearch ] 各种常规操作方法整理的主要内容,如果未能解决你的问题,请参考以下文章

ELK学习004:Elasticsearch常规操作

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

Elasticsearch学习之ES节点类型以及各种节点的分工

龙芯平台快速搭建Elasticsearch:开源的搜索和大数据分析引擎

ElasticSearch高级操作

JavaApi操作ElasticSearch(强烈推荐)