本篇是这个系列的最后一篇了,在这之前可以先看看前面的内容:
ES搜索引擎入门+最佳实践(一)_flame.liu的博客-CSDN博客
ES搜索引擎入门+最佳实践(二)_flame.liu的博客-CSDN博客
ES搜索引擎入门+最佳实践(三)_flame.liu的博客-CSDN博客
ES搜索引擎入门+最佳实践(四)_flame.liu的博客-CSDN博客
ES搜索引擎入门+最佳实践(五)_flame.liu的博客-CSDN博客
ES搜索引擎入门+最佳实践(六)_flame.liu的博客-CSDN博客
ES搜索引擎入门+最佳实践(七):聚合_flame.liu的博客-CSDN博客
这篇文章将介绍使用ES JAVA API对ES中的数据进行增删改查.
一.添加引用
ES高级客户端已经被放弃,所以这里使用的是elasticsearch-java
<!-- es java客户端-->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
</dependency>
<!-- jsom与java对象之间的转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
二. 创建ES客户端的配置类
package com.flamelp.productessearch.config;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ESClientConfig {
// @Value("${elasticsearch.client.hostname}")
private String hostname="localhost"; //ES主机地址
// @Value("${elasticsearch.client.port}")
private int port=9200; //ES端口号
/**
* 创建ES客户端
* @return ES客户端
*/
@Bean
public ElasticsearchClient restHighLevelClient() {
//创建一个低级客户端,ES JAVA Client API通过低级客户端连接,与ES的版本无关
RestClient restClient = RestClient.builder(new HttpHost(hostname, port)).build();
//创建JSON的映射器
ElasticsearchTransport elasticsearchTransport = new RestClientTransport(restClient,new JacksonJsonpMapper());
//创建API的客户端
return new ElasticsearchClient(elasticsearchTransport);
}
}
三. 在dao层添加对数据增删改查的方法
索引以及存储对象的格式可以查看前面几篇文章
3.1 编写一个查询需求实体对象
这个对象传递过来的是客户端需要搜索的信息
@Getter
@Setter
@Schema(name = "ProductDetail", description = "")
public class ProductESInfo implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键")
private Integer id;
@Schema(description = "UID")
private String uid;
@Schema(description = "商品名称")
private String productName;
@Schema(description = "商品图片")
private String img;
@Schema(description = "商品标签")
private String[] tag;
@Schema(description = "商品标签字符串形式")
@JsonIgnore
private String str_tag;//在导入文档时忽略这个属性
@Schema(description = "发货地经纬度")
private LocationPoint location;
@Schema(description = "发货城市")
private String city;
@Schema(description = "原价")
private BigDecimal originalPrice;
@Schema(description = "售价")
private BigDecimal sellPrice;
@Schema(description = "库存")
private Integer stockNum;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
@Schema(description = "销量")
private Integer salesVolume;
@Schema(description = "推广活动")
private String promotion;
@Schema(description = "商品类别")
private Integer category;
@Schema(description = "商品评分")
private BigDecimal score;
@Schema(description = "好评数量")
private Integer goodComment;
@Schema(description = "差评数量")
private Integer badComment;
@Schema(description = "店铺编号")
private Integer shopId;
@Schema(description = "店铺名称")
private String shopName;
/**
* 将tag转换为数组
* @return
*/
public String[] getTag(){
if(str_tag!=null){
return StringUtils.split(str_tag,',');
}else{
return tag;
}
}
}
@Getter
@Setter
@Schema(name = "LocationPoint", description = "发货地点的经纬度")
public class LocationPoint implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "发货地纬度")
private double lat;
@Schema(description = "发货地经度")
private double lon;
}
3.2 创建 ProductESDao 类
@Component
public class ProductESDao implements IProductESDao {
@Resource
ElasticsearchClient esClient; //ES api 客户端
private static final String indexName = "product"; //索引名称
}
接下来在这个类添加需要的方法.
3.3 删除记录
/**
* 根据商品编号在ES中删除产品信息
* @param productId
* @return ture:成功
*/
@Override
public boolean deleteProductESInfo(String productId) throws IOException {
DeleteResponse response = esClient.delete(builder ->
builder.index(indexName).id(productId));
if(response.shards().successful().intValue()>0){
return true;
}else{
return false;
}
}
3.4 添加单条记录
/**
* 根据产品编号在ES中修改产品信息
* @param productESInfo
* @return true:成功
*/
@Override
public boolean addProductESInfo(ProductESInfo productESInfo) throws IOException {
IndexResponse response = esClient.index(builder ->
builder.index(indexName) //指定索引
.id(productESInfo.getId().toString()) //用产品编号作为文档编号
.document(productESInfo)); //将对象加入文档
if(response.version()>0){ //判断文档编号
return true;
}else{
return false;
}
}
3.5 修改记录
/**
* 修改ES中产品信息
* @param productESInfo
* @return true:成功
*/
@Override
public boolean modifyProductESInfo(ProductESInfo productESInfo) throws IOException {
//修改文档(覆盖)
UpdateResponse<ProductESInfo> response = esClient.update(builder ->
builder.index(indexName) //指定索引
.id(productESInfo.getId().toString()) //指定id
.doc(productESInfo),ProductESInfo.class); //设置需要修改的值
if(response.shards().successful().intValue()>0){
return true;
}else{
return false;
}
}
3.6 批量添加记录
/**
* 将MySQL中的数据加载到ES索引中
* * @param list 需要加载到ES中的产品数据
* @return true表示没有出现错误
*/
@Override
public boolean contextLoads(List<ProductESInfo> list) {
boolean result = true;
BulkRequest.Builder builder = new BulkRequest.Builder().index(indexName); // 指定索引
for (ProductESInfo product : list) {
builder.operations(op -> op.index(in -> in.id(product.getId().toString()).document(product)));
}
// 运行批量操作
try {
BulkResponse bulk = esClient.bulk(builder.build());
result = !bulk.errors();
} catch (IOException e) {
System.out.println(e.toString());
}
return result;
}
3.7 根据搜索条件查询多条记录
/**
* 根据搜索条件在ES中搜索产品数据
* @param qc
* @return 检索到的产品信息
*/
@Override
public List<ProductESInfo> searchProductESInfo(ProductQueryCriteria qc) throws IOException {
/**
* 从ES中检索数据一共分四步
* 1. 创建BoolQuery.Builder(多条件查询构建器)
* 2. 根据搜索条件往BoolQuery.Builder中添加查询条件
* 3. 通过es客户端的search发送查询构建器,并设置分页,排序,最终返回搜索响应结果(SearchResponse)
* 4. 从搜索响应结果(SearchResponse)获得hits并转移到list数据,最后返回结果.
*/
//创建多条件查询构建起
BoolQuery.Builder qb = QueryBuilders.bool();
//添加产品查询条件,并且设置查询权重为2
qb.must(m->m.match(ma->ma.field("productName").query(qc.getTitle()).boost(2F)));
//判断商品类型,并将商品类型作为条件添加到查询构建起
if(qc.getCategory()!=0){
qb.must(m-> m.term(t->t.field("category").value(qc.getCategory().toString())));
}
//判断城市并将城市搜索信息添加到查询构建起
if(qc.getCity()!=""){
qb.must(m-> m.term(t->t.field("city").value(qc.getCity())));
}
//判断价格范围,约定都为0时,不作为查询条件
if(qc.getMinPrice()==BigDecimal.valueOf(0)&&qc.getMinPrice()==BigDecimal.valueOf(0)){
qb.must(m->m.range(r->r.field("sellPrice")
.gte(JsonData.of(qc.getMinPrice()))
.lte(JsonData.of(qc.getMaxPrice()))));
}
//判断是否需要根据标签查询,这里指定至少需要匹配一个标签
if(qc.getTag()!=null){
qb.should(s->s.termsSet(ts->ts.field("tag")
.terms(qc.getTag())
.minimumShouldMatchScript(ss->ss.inline(il->il.source("1")))
.boost(1.1F)));
}
//查询店铺名字中是否包含有商品名称
if(qc.getTitle()!=""){
qb.should(s-> s.match(ma->ma.field("shopName").query(qc.getTitle()).boost(1.1F)));
}
//按照地理位置进行查询,50km范围内的商品发货地
if(qc.getPoint()!=null){
qb.should(s->s.geoDistance(g->g.field("location")
.location(loc->loc.latlon(lat->lat.lat(qc.getPoint().getLat()).lon(qc.getPoint().getLon())))
.distance("50km").boost(1.2F)));
}
//向ES客户端发送搜索命令,并返回搜索响应(SearchResponse)
SearchResponse<ProductESInfo> response= esClient.search(builder -> builder
.query(q->q.bool(b->qb)) //设置查询
.from(qc.getFrom()) //分页:记录开始的序号
.size(qc.getSize()) //分页:每页显示的数据量
.sort(s->s.field(f->f.field(qc.getOrderByCode().toString()).order(SortOrder.Desc))) //按照相关度降序排列
,ProductESInfo.class);//SortOrder.Desc
//从搜索响应SearchResponse中获得查询的数据结果
List<Hit<ProductESInfo>> hits = response.hits().hits();
List<ProductESInfo> productESInfos = new ArrayList<>();
for(Hit<ProductESInfo> hit: hits){
ProductESInfo productESInfo = hit.source();
productESInfos.add(productESInfo);
}
return productESInfos;
}
上面这个方法是ES操作的核心, 毕竟ES就是为了检索数据而出现的.上面代码的注释比较全,只对部分内容做些解释.
1. BoolQuery.Builder 是创建一个多条件查询构造器,我们可以在这个构造器里面添加查询的方法,最终发送给ES的时候将转换为JSON代码;
2. 多条件查询时需要判断是否需要添加条件;
3.must表示这个查询条件是必须的,这里有多个must拼接起来,在最终会拼接位一个must的jsom语句,should也是一样.
4. match表示分词查询,term表示完全匹配,range用户范围查询,需要指定最大值和最小值,geoDistance是进行地理位置查询.
5. 这里有意思的是terms,在es中可以用来匹配一个数组里面的值,但是在这个api中terms好像不是这样的,所以这里引用了termsSet这个操作,可以起到相同的作用,需要注意的是,这个操作需要指定minimumShouldMatchScript或者minimumShouldMatchField.
minimumShouldMatchScript:指定一个脚本告诉ES,在匹配集合数据时需要匹配几条,后面 il->il.source("1") 表示脚本返回数据为1,只需要匹配一条.
minimumShouldMatchField:指定索引中的一个字段,这个字段必须返回一个数字,然后用这个数字规定需要在集合里面匹配的记录数.(不知道为什么有个这么奇怪的规则).
6.boots这里是设置每一项查询的得分权重.文章来源:https://www.toymoban.com/news/detail-652598.html
好了,就到这里了.有问题就留言吧,看到就回.文章来源地址https://www.toymoban.com/news/detail-652598.html
到了这里,关于ES搜索引擎入门+最佳实践(九):项目实战(二)--elasticsearch java api 进行数据增删改查的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!