本文共 24373 字,大约阅读时间需要 81 分钟。
引入依赖
maven <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>温馨提示:springboot版本中内嵌了es版本 ,如果与服务器版本不一致,需强制改动其依赖版本
maven 更改方法
properties 中指定版本号<properties>
<elasticsearch.version>es服务版本</elasticsearch.version> </properties>可能有人问,为什么要这么定义es版本号前后缀?
因为 springboot父工程就是这样定义版本号的,所以我们平时在引入依赖时无需显示申明版本号
在我们学习ES的时候,就已经讲过 ,初学期间,我们直接把 indexs 当做数据库,
type当做表 document当做一行数据
在操作mysql数据库的时候啊,我们通常会定义pojo实体类,一个类呢,对应着一个表,然后使用jpa /mybatis 对这个类做操作,然后保存到我们的数据库即可。
那么ES是否可以定义一个实体类,然后操作到ES服务器上呢?ES 在springboot中如何使用呢?
咱们进入今天的正题:springboot整合es
springboot 整合es
配置链接信息 springboot中 连接es很简单uris 表明其需要连接的集群 (我这里只有一个,es ,一个节点也是集群)
spring:
elasticsearch: rest: uris: localhost:9200
如果是Linux服务器上安装的话 ,可能还需要配置连接es的账户和密码
spring: elasticsearch: rest: uris: localhost:9200 password: xxx username: xxx
定义实体类
es 在springboot中使用,同样可定义实体类 ,使其绑定到索引库以及type上,并能根据字段类型设置mapping映射.
@Document 注解注意引用的是import org.springframework.data.elasticsearch.annotations.Document下
在项目使用了Mongodb的时候,格外要注意这点!
indexName指定了 索引库名
我这里是没有写_type的, 虽然哈,一直说 将type作为表来理解,但是es 与mysql终究是不一样的,后续es 8.x会去除type操作 _type ,我这里没写,因为其默认了,
shards 指定分片
replicas 指定副本集 此二者无特殊场景,默认即可
@Document(indexName = "pms", type = "product",shards = 1,replicas = 0)public class EsProduct implements Serializable { private static final long serialVersionUID = -1L; @Id private Long id; @Field(type = FieldType.Keyword) private String productSn; private Long brandId; @Field(type = FieldType.Keyword) private String brandName; private Long productCategoryId; @Field(type = FieldType.Keyword) private String productCategoryName; private String pic; @Field(analyzer = "ik_max_word",type = FieldType.Text) private String name; @Field(analyzer = "ik_max_word",type = FieldType.Text) private String subTitle; @Field(analyzer = "ik_max_word",type = FieldType.Text) private String keywords; private BigDecimal price; private Integer sale; private Integer newStatus; private Integer recommandStatus; private Integer stock; private Integer promotionType; private Integer sort; @Field(type =FieldType.Nested) private ListattrValueList; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getProductSn() { return productSn; } public void setProductSn(String productSn) { this.productSn = productSn; } public Long getBrandId() { return brandId; } public void setBrandId(Long brandId) { this.brandId = brandId; } public String getBrandName() { return brandName; } public void setBrandName(String brandName) { this.brandName = brandName; } public Long getProductCategoryId() { return productCategoryId; } public void setProductCategoryId(Long productCategoryId) { this.productCategoryId = productCategoryId; } public String getProductCategoryName() { return productCategoryName; } public void setProductCategoryName(String productCategoryName) { this.productCategoryName = productCategoryName; } public String getPic() { return pic; } public void setPic(String pic) { this.pic = pic; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSubTitle() { return subTitle; } public void setSubTitle(String subTitle) { this.subTitle = subTitle; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } public Integer getSale() { return sale; } public void setSale(Integer sale) { this.sale = sale; } public Integer getNewStatus() { return newStatus; } public void setNewStatus(Integer newStatus) { this.newStatus = newStatus; } public Integer getRecommandStatus() { return recommandStatus; } public void setRecommandStatus(Integer recommandStatus) { this.recommandStatus = recommandStatus; } public Integer getStock() { return stock; } public void setStock(Integer stock) { this.stock = stock; } public Integer getPromotionType() { return promotionType; } public void setPromotionType(Integer promotionType) { this.promotionType = promotionType; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public List getAttrValueList() { return attrValueList; } public void setAttrValueList(List attrValueList) { this.attrValueList = attrValueList; } public String getKeywords() { return keywords; } public void setKeywords(String keywords) { this.keywords = keywords; }}
@Id 注意 引入的是 import org.springframework.data.annotation.Id包下的
@Field 可指定字段类型,与es中mapping设定需要规约的类型一致,并且可选择分词器(前面已经安装了IK)
import java.util.List;public class EsProductRelatedInfo { private ListbrandNames; private List productCategoryNames; private List productAttrs; public List getBrandNames() { return brandNames; } public void setBrandNames(List brandNames) { this.brandNames = brandNames; } public List getProductCategoryNames() { return productCategoryNames; } public void setProductCategoryNames(List productCategoryNames) { this.productCategoryNames = productCategoryNames; } public List getProductAttrs() { return productAttrs; } public void setProductAttrs(List productAttrs) { this.productAttrs = productAttrs; } public static class ProductAttr{ private Long attrId; private String attrName; private List attrValues; public Long getAttrId() { return attrId; } public void setAttrId(Long attrId) { this.attrId = attrId; } public List getAttrValues() { return attrValues; } public void setAttrValues(List attrValues) { this.attrValues = attrValues; } public String getAttrName() { return attrName; } public void setAttrName(String attrName) { this.attrName = attrName; } }}
public class EsProductAttributeValue implements Serializable { private static final long serialVersionUID = 1L; private Long id; private Long productAttributeId; //属性值 @Field(type = FieldType.Keyword) private String value; //属性参数:0->规格;1->参数 private Integer type; //属性名称 @Field(type=FieldType.Keyword) private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getProductAttributeId() { return productAttributeId; } public void setProductAttributeId(Long productAttributeId) { this.productAttributeId = productAttributeId; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; }}
定义dao 接口继承ElasticsearchRepository
public interface EsProductRepository extends ElasticsearchRepository{ /** * 搜索查询 * * @param name 商品名称 * @param subTitle 商品标题 * @param keywords 商品关键字 * @param page 分页信息 * @return */ Page findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);}
继承了此接口 ,基础文档CRUD 接口即做好了
定义一个controller控制器,操作
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import java.util.List;/** * 搜索商品管理Controller */@Controller@Api(tags = "EsProductController", description = "搜索商品管理")@RequestMapping("/esProduct")public class EsProductController { @Autowired private EsProductService esProductService; @ApiOperation(value = "导入所有数据库中商品到ES") @RequestMapping(value = "/importAll", method = RequestMethod.POST) @ResponseBody public CommonResultimportAllList() { int count = esProductService.importAll(); return CommonResult.success(count); } @ApiOperation(value = "根据id删除商品") @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET) @ResponseBody public CommonResult
service层
package com.macro.mall.tiny.service;import com.macro.mall.tiny.domain.EsProductRelatedInfo;import com.macro.mall.tiny.nosql.elasticsearch.document.EsProduct;import org.springframework.data.domain.Page;import java.util.List;/** * 商品搜索管理Service */public interface EsProductService { /** * 从数据库中导入所有商品到ES */ int importAll(); /** * 根据id删除商品 */ void delete(Long id); /** * 根据id创建商品 */ EsProduct create(Long id); /** * 批量删除商品 */ void delete(Listids); /** * 根据关键字搜索名称或者副标题 */ Page search(String keyword, Integer pageNum, Integer pageSize); /** * 根据关键字搜索名称或者副标题复合查询 */ Page search(String keyword, Long brandId, Long productCategoryId, Integer pageNum, Integer pageSize,Integer sort); /** * 根据商品id推荐相关商品 */ Page recommend(Long id, Integer pageNum, Integer pageSize); /** * 获取搜索词相关品牌、分类、属性 */ EsProductRelatedInfo searchRelatedInfo(String keyword);}
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;import org.elasticsearch.index.query.BoolQueryBuilder;import org.elasticsearch.index.query.QueryBuilders;import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;import org.elasticsearch.search.aggregations.Aggregation;import org.elasticsearch.search.aggregations.AggregationBuilders;import org.elasticsearch.search.aggregations.bucket.filter.InternalFilter;import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;import org.elasticsearch.search.aggregations.bucket.nested.InternalNested;import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;import org.elasticsearch.search.aggregations.bucket.terms.*;import org.elasticsearch.search.sort.SortBuilders;import org.elasticsearch.search.sort.SortOrder;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageImpl;import org.springframework.data.domain.PageRequest;import org.springframework.data.domain.Pageable;import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;import org.springframework.data.elasticsearch.core.SearchHits;import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import org.springframework.util.StringUtils;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Map;/** * 商品搜索管理Service实现类 */@Servicepublic class EsProductServiceImpl implements EsProductService { private static final Logger LOGGER = LoggerFactory.getLogger(EsProductServiceImpl.class); @Autowired private EsProductDao productDao; @Autowired private EsProductRepository productRepository; @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; @Override public int importAll() { ListesProductList = productDao.getAllEsProductList(null); Iterable esProductIterable = productRepository.saveAll(esProductList); Iterator iterator = esProductIterable.iterator(); int result = 0; while (iterator.hasNext()) { result++; iterator.next(); } return result; } @Override public void delete(Long id) { productRepository.deleteById(id); } @Override public EsProduct create(Long id) { EsProduct result = null; List esProductList = productDao.getAllEsProductList(id); if (esProductList.size() > 0) { EsProduct esProduct = esProductList.get(0); result = productRepository.save(esProduct); } return result; } @Override public void delete(List ids) { if (!CollectionUtils.isEmpty(ids)) { List esProductList = new ArrayList<>(); for (Long id : ids) { EsProduct esProduct = new EsProduct(); esProduct.setId(id); esProductList.add(esProduct); } productRepository.deleteAll(esProductList); } } @Override public Page search(String keyword, Integer pageNum, Integer pageSize) { Pageable pageable = PageRequest.of(pageNum, pageSize); return productRepository.findByNameOrSubTitleOrKeywords(keyword, keyword, keyword, pageable); } @Override public Page search(String keyword, Long brandId, Long productCategoryId, Integer pageNum, Integer pageSize,Integer sort) { Pageable pageable = PageRequest.of(pageNum, pageSize); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); //分页 nativeSearchQueryBuilder.withPageable(pageable); //过滤 if (brandId != null || productCategoryId != null) { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); if (brandId != null) { boolQueryBuilder.must(QueryBuilders.termQuery("brandId", brandId)); } if (productCategoryId != null) { boolQueryBuilder.must(QueryBuilders.termQuery("productCategoryId", productCategoryId)); } nativeSearchQueryBuilder.withFilter(boolQueryBuilder); } //搜索 if (StringUtils.isEmpty(keyword)) { nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery()); } else { List filterFunctionBuilders = new ArrayList<>(); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", keyword), ScoreFunctionBuilders.weightFactorFunction(10))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("subTitle", keyword), ScoreFunctionBuilders.weightFactorFunction(5))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keywords", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; filterFunctionBuilders.toArray(builders); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2); nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder); } //排序 if(sort==1){ //按新品从新到旧 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC)); }else if(sort==2){ //按销量从高到低 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sale").order(SortOrder.DESC)); }else if(sort==3){ //按价格从低到高 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC)); }else if(sort==4){ //按价格从高到低 nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC)); }else{ //按相关度 nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); } nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build(); LOGGER.info("DSL:{}", searchQuery.getQuery().toString()); return productRepository.search(searchQuery); } @Override public Page recommend(Long id, Integer pageNum, Integer pageSize) { Pageable pageable = PageRequest.of(pageNum, pageSize); List esProductList = productDao.getAllEsProductList(id); if (esProductList.size() > 0) { EsProduct esProduct = esProductList.get(0); String keyword = esProduct.getName(); Long brandId = esProduct.getBrandId(); Long productCategoryId = esProduct.getProductCategoryId(); //根据商品标题、品牌、分类进行搜索 List filterFunctionBuilders = new ArrayList<>(); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", keyword), ScoreFunctionBuilders.weightFactorFunction(8))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("subTitle", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keywords", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("brandId", brandId), ScoreFunctionBuilders.weightFactorFunction(5))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("productCategoryId", productCategoryId), ScoreFunctionBuilders.weightFactorFunction(3))); FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; filterFunctionBuilders.toArray(builders); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2); //用于过滤掉相同的商品 BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); boolQueryBuilder.mustNot(QueryBuilders.termQuery("id",id)); //构建查询条件 NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); builder.withQuery(functionScoreQueryBuilder); builder.withFilter(boolQueryBuilder); builder.withPageable(pageable); NativeSearchQuery searchQuery = builder.build(); LOGGER.info("DSL:{}", searchQuery.getQuery().toString()); return productRepository.search(searchQuery); } return new PageImpl<>(null); } @Override public EsProductRelatedInfo searchRelatedInfo(String keyword) { NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); //搜索条件 if(StringUtils.isEmpty(keyword)){ builder.withQuery(QueryBuilders.matchAllQuery()); }else{ builder.withQuery(QueryBuilders.multiMatchQuery(keyword,"name","subTitle","keywords")); } //聚合搜索品牌名称 builder.addAggregation(AggregationBuilders.terms("brandNames").field("brandName")); //集合搜索分类名称 builder.addAggregation(AggregationBuilders.terms("productCategoryNames").field("productCategoryName")); //聚合搜索商品属性,去除type=1的属性 AbstractAggregationBuilder aggregationBuilder = AggregationBuilders.nested("allAttrValues","attrValueList") .subAggregation(AggregationBuilders.filter("productAttrs",QueryBuilders.termQuery("attrValueList.type",1)) .subAggregation(AggregationBuilders.terms("attrIds") .field("attrValueList.productAttributeId") .subAggregation(AggregationBuilders.terms("attrValues") .field("attrValueList.value")) .subAggregation(AggregationBuilders.terms("attrNames") .field("attrValueList.name")))); builder.addAggregation(aggregationBuilder); NativeSearchQuery searchQuery = builder.build(); SearchHits searchHits = elasticsearchRestTemplate.search(searchQuery, EsProduct.class); return convertProductRelatedInfo(searchHits); } /** * 将返回结果转换为对象 */ private EsProductRelatedInfo convertProductRelatedInfo(SearchHits response) { EsProductRelatedInfo productRelatedInfo = new EsProductRelatedInfo(); Map aggregationMap = response.getAggregations().getAsMap(); //设置品牌 Aggregation brandNames = aggregationMap.get("brandNames"); List brandNameList = new ArrayList<>(); for(int i = 0; i<((Terms) brandNames).getBuckets().size(); i++){ brandNameList.add(((Terms) brandNames).getBuckets().get(i).getKeyAsString()); } productRelatedInfo.setBrandNames(brandNameList); //设置分类 Aggregation productCategoryNames = aggregationMap.get("productCategoryNames"); List productCategoryNameList = new ArrayList<>(); for(int i=0;i<((Terms) productCategoryNames).getBuckets().size();i++){ productCategoryNameList.add(((Terms) productCategoryNames).getBuckets().get(i).getKeyAsString()); } productRelatedInfo.setProductCategoryNames(productCategoryNameList); //设置参数 Aggregation productAttrs = aggregationMap.get("allAttrValues"); List attrIds = ((ParsedLongTerms) ((ParsedFilter) ((ParsedNested) productAttrs).getAggregations().get("productAttrs")).getAggregations().get("attrIds")).getBuckets(); List attrList = new ArrayList<>(); for (Terms.Bucket attrId : attrIds) { EsProductRelatedInfo.ProductAttr attr = new EsProductRelatedInfo.ProductAttr(); attr.setAttrId((Long) attrId.getKey()); List attrValueList = new ArrayList<>(); List attrValues = ((ParsedStringTerms) attrId.getAggregations().get("attrValues")).getBuckets(); List attrNames = ((ParsedStringTerms) attrId.getAggregations().get("attrNames")).getBuckets(); for (Terms.Bucket attrValue : attrValues) { attrValueList.add(attrValue.getKeyAsString()); } attr.setAttrValues(attrValueList); if(!CollectionUtils.isEmpty(attrNames)){ String attrName = attrNames.get(0).getKeyAsString(); attr.setAttrName(attrName); } attrList.add(attr); } productRelatedInfo.setProductAttrs(attrList); return productRelatedInfo; }}
mapper,dao类此处由于篇幅问题不再黏贴了,理解操作流程。
转载地址:http://bhxzb.baihongyu.com/