[ 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查询 |
---|---|---|
And | findByNameAndPrice | {“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}} |
Or | findByNameOrPrice | {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}} |
Is | findByName | {“bool” : {“must” : {“field” : {“name” : “?”}}}} |
Not | findByNameNot | {“bool” : {“must_not” : {“field” : {“name” : “?”}}}} |
Between | findByPriceBetween | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : ?,”include_lower” : true,”include_upper” : true}}}}} |
LessThanEqual | findByPriceLessThan | {“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}} |
GreaterThanEqual | findByPriceGreaterThan | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”include_lower” : true,”include_upper” : true}}}}} |
Before | findByPriceBefore | {“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}} |
After | findByPriceAfter | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”include_lower” : true,”include_upper” : true}}}}} |
Like | findByNameLike | {“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,”analyze_wildcard” : true}}}}} |
StartingWith | findByNameStartingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “? *”,”analyze_wildcard” : true}}}}} |
EndingWith | findByNameEndingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,”analyze_wildcard” : true}}}}} |
Contains/Containing | findByNameContaining | {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,”analyze_wildcard” : true}}}}} |
In | findByNameIn(Collectionnames) | {“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}} |
NotIn | findByNameNotIn(Collectionnames) | {“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}} |
True | findByAvailableTrue | {“bool” : {“must” : {“field” : {“available” : true}}}} |
False | findByAvailableFalse | {“bool” : {“must” : {“field” : {“available” : false}}}} |
OrderBy | findByAvailableTrueOrderByNameDesc | {“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 ] 各种常规操作方法整理的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch学习之ES节点类型以及各种节点的分工