This article describes how to implement the product search function in Elasticsearch
Chinese word divider
-
Elasticsearch has a default word splitter, the default word splitter just separates Chinese word by word, which is not what we want.
get hanzo/_analyze { "text": "Xiaomi Phone"."tokenizer": "standard" } Copy the code
-
You need to install the same IK word splitter as Elasticsearch, ik word splitter divides mi phones into Mi and phone, which meets our requirements.
get hanzo/_analyze
{
"text": "Xiaomi Phone"."tokenizer": "ik_max_word"
}
Copy the code
Used in SpringBoot
Annotate @document, @field and so on in commodity information entity class. For fields that require Chinese word segmentation, we directly use the @field attribute to set it to ik_max_word.
/ * * *@AuthorHao yu QAQ *@Date2020/6/4 then *@Description: Product information in search */
@Document(indexName = "hanzo", type = "product",shards = 1,replicas = 0)
@Data
public class EsProduct implements Serializable {
private static final long serialVersionUID = -1L;
@Id
private Long goodsId;
@Field(analyzer = "ik_max_word",type = FieldType.Text)
private String goodsName;
@Field(analyzer = "ik_max_word",type = FieldType.Text)
private String goodsIntro;
private Long goodsCategoryId;
private String goodsCoverImg;
private String goodsCarousel;
private Integer originalPrice;
private Integer sellingPrice;
private Integer stockNum;
private String tag;
private Byte goodsSellStatus;
private Integer createUser;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
private Integer updateUser;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
@Field(type = FieldType.Keyword)
private String goodsDetailContent;
}
Copy the code
Simple Product Search
First to achieve a simple commodity search function, search including commodity name, commodity introduction, commodity label contains the specified keywords of the commodity and a direct according to the classification of commodity query commodity function
-
Implementation in SpringBoot, using Elasticsearch Repositories derived query to search;
public interface EsProductRepository extends ElasticsearchRepository<EsProduct.Long> { /** * derivative search keyword query **@paramGoodsName Product name *@paramGoodsIntro Product Profile *@paramTag Indicates the product tag@paramPage page information *@return* / Page<EsProduct> findByGoodsNameOrGoodsIntroOrTag(String goodsName, String goodsIntro, String tag,Pageable page); /** * derivative search product classification query **@paramGoodsCategoryId Indicates the product category *@paramPage page information *@return* / Page<EsProduct> findByGoodsCategoryId(Long goodsCategoryId, Pageable page); } Copy the code
-
A derived Query is a simple way to convert a method with a specified method name into a Elasticsearch Query DSL statement.
Integrated product search
Next, let’s implement a complex product search that involves filtering, matching weights for different fields, and sorting.
- First of all, let’s talk about our requirements. Search the product name, product introduction and product label according to the input keyword, and sort by relevance by default.
- Here we have some special requirements, such as the product name matching the keyword of the product we think better match the search criteria, followed by the product profile and product label, then need to use
function_score
The query; - Elasticsearch finds document relevance by
_score
Field, document_score
The higher the field value is, the more it matches the search criteriafunction_score
Queries can be influenced by setting weights_score
Field value, using it we can achieve the above requirements; - In SpringBoot implementation, using Elasticsearch Repositories search method to achieve, but need to customize query conditions QueryBuilder;
@Override
public Page<EsProduct> sortSearch(String keyword, Integer pageNum, Integer pageSize, Integer sort) {
Pageable pageable = PageRequest.of(pageNum, pageSize);
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
/ / paging
nativeSearchQueryBuilder.withPageable(pageable);
/ / filter
/ / search
if (StringUtils.isEmpty(keyword)) {
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
} else {
List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("goodsName", keyword),
ScoreFunctionBuilders.weightFactorFunction(10)));
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("goodsIntro", keyword),
ScoreFunctionBuilders.weightFactorFunction(5)));
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("tag", 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);
}
/ / sorting
if(sort==1) {// From new to old
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("goodsId").order(SortOrder.DESC));
}else if(sort==2) {// From high to low by inventory
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("stockNum").order(SortOrder.DESC));
}else if(sort==3) {// From low to high by price
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sellingPrice").order(SortOrder.ASC));
}else if(sort==4) {// From high to low by price
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sellingPrice").order(SortOrder.DESC));
}else{
// By relevance
nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
}
nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
log.info("DSL:{}", searchQuery.getQuery().toString());
return productRepository.search(searchQuery);
}
Copy the code
Related products recommendation
When we look at the related items, there will be some product recommendations at the bottom.
-
First of all, let’s talk about our requirements. We can find related goods according to the ID of the specified goods.
-
Here our implementation principle is as follows: first, obtain the specified product information according to the ID, and then search the product by the name of the specified product, product introduction, product label and classification, and filter out the current product, adjust the weight of the search conditions to obtain the best matching degree;
-
In SpringBoot implementation, using Elasticsearch Repositories search method to achieve, but need to customize query conditions QueryBuilder;
@Override public Page<EsProduct> recommend(Long id, Integer pageNum, Integer pageSize) { Pageable pageable = PageRequest.of(pageNum, pageSize); List<EsProduct> esProductList = productDao.getAllEsProductList(id); if (esProductList.size() > 0) { EsProduct esProduct = esProductList.get(0); String keyword = esProduct.getGoodsName(); Long goodsCategoryId = esProduct.getGoodsCategoryId(); // Search by product name, product description, product label and classification List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>(); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("goodsName", keyword), ScoreFunctionBuilders.weightFactorFunction(8))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("goodsIntro", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("tag", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("goodsCategoryId", goodsCategoryId), ScoreFunctionBuilders.weightFactorFunction(6))); FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; filterFunctionBuilders.toArray(builders); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2); NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder(); builder.withQuery(functionScoreQueryBuilder); builder.withPageable(pageable); NativeSearchQuery searchQuery = builder.build(); log.info("DSL:{}", searchQuery.getQuery().toString()); return productRepository.search(searchQuery); } return new PageImpl<>(null); } Copy the code
Reference code and interface documentation
Github
Swagger UI document
conclusion
Knowledge is valuable only if it is shared. If you have any questions, please feel free to contact me through my email on my page.