Gitee地址:https://gitee.com/yuyuuyuy/micro-mall
文章目录
- Elasticearch实现商品搜索与聚合分析
- 前言
- 一、Elasticsearch使用介绍
- 二、效果展示
- 三、代码实现
- 总结
前言
搜索是互联网各个项目中的常见场景,而Elasticsearch就是搜索领域最重要的工具之一,它基于倒排索引,天然支持全文搜索,且搜索效率极高。而且支持分布式,可横向拓展。具有存储,搜索,分析功能。总的来说,you know,for search.
一、Elasticsearch使用介绍根据官网,ES具有存储,搜索,分析三大功能,本文也按这三大功能展开。
1.存储
ES版本跟新较快,且变化较大,新的版本里已经没有类型这一概念了。
新建产品索引实例如下,其中type指的是字段类型,keyword不支持分词,text支持分词,analyzer可设置分词的粒度,index表示是否建立该字段的索引,doc_values表明是否支持对该字段的聚合分析。该例子中图片就是一长串的url,没有搜索价值,也没有分析价值,所以这两个值都设为false节省空间。
PUT product
{
"mappings": {
"properties": {
"skuId":{
"type": "long"
},
"spuId":{
"type": "keyword"
},
"skuTitle":{
"type": "text",
"analyzer": "standard"
},
"brandImg":{
"type": "keyword",
"index": false,
"doc_values": false
}
}
}
}
}
}
}
如果要插入数据,只用以json格式插入数据即可
比如
PUT product/1
{
"name":"手机",
"price":"3000"
}
有些情况下ES的数据是从mysql中同步的,这种情况一般使用canal伪装成mysql的从机,监听binlog数据然后同步到ES,但是这种架构在并发量高的情况下可能会丢失数据,因此,cannel和ES之间还要加个消息队列
2.搜索
最简单的就是match语句,比如
"match": {
"title": "小米手机"
}
这里要注意的是,match是默认分词查询,比如小米手机,会分词成小米和手机两个词进行查询。如果不想分词查询,就想搜小米手机,那么就要用match_phrase,但是结果可以分词,比如你可能会搜到小米手机Pro11这样的结果。如果连搜索结果都不想分词,就要用term查询,这样搜小米手机,结果只能是小米手机 。总结:不想全文搜索的字段用term查询,想全文搜索的字段用match查询。
在基本查询的基础上,还有布尔查询,就是所有的查询条件都满足才返回结果。布尔查询有4种子句,分别是must,should,must_not,filter。must:必须满足must子句的条件,并且参与计算分值。should:满足任意一个即可,参与计算分值。must_not:必须不满足查询条件。filter:返回的文档必须满足filter子句的条件。但是不会像Must一样,参与计算分值。
3.聚合分析
就是在查询结果的基础上,对查询结果进行聚合分析。比如统计有哪些商家在卖搜索的商品,某价格区间内的商品有多少种等等。语法大概是聚合名称(自定义),聚合规则(大于小于,求平均值,统计聚合等),聚合字段(对哪个字段进行分析),并且在聚合的基础上,还可以嵌套聚合。
这是在kibana上使用DSL来构建查询语句
这里通过match全文搜索华为产品,并通过filter过滤出价格3000到4500的产品,最后通过aggs聚合分析出有两家商铺在卖华为产品。
使用elasticsearchRestTemplate进行查询
ES查询可用DSL查询,也可用ES给java提供的API:ElasticsearchRestTemplate来查询,而ElasticsearchRestTemplate只是DSL的映射而已。
创建索引:
PUT goods
{
"mappings": {
"properties": {
"id":{
"type": "long"
},
"title":{
"type": "text"
},
"img":{
"type": "keyword",
"index":"false",
"doc_values": false
},
"shop":{
"type": "keyword"
},
"price":{
"type": "keyword"
}
}
}
}
添加数据:
使用Jsoup爬虫自行添加,略
查询:
GET goods/_search
{
"query": {
"bool": {
"must": [
{"match": {
"title": "华为"
}}
],
"filter": [
{"range": {
"price": {
"gte": 3000,
"lte": 4500
}
}}
]
}
},
"aggs": {
"店铺": {
"terms": {
"field": "shop",
"size": 10
}
}
}
}
查询:全文搜索商品名称,指定产品价格范围,分析出有哪些店铺在卖该商品
GET goods/_search
{
"query": {
"bool": {
"must": [
{"match": {
"title": "华为"
}}
],
"filter": [
{"range": {
"price": {
"gte": 3000,
"lte": 4500
}
}}
]
}
},
"aggs": {
"店铺": {
"terms": {
"field": "shop",
"size": 10
}
}
}
}
使用elasticsearchRestTemplate进行查询
public void search(String keyword, Integer pageNum, Integer pageSize) {
//构建原生查询
NativeSearchQueryBuilder nativeSearchQueryBuilder=new NativeSearchQueryBuilder();
// 构建布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//构建普通查询,根据产品名称全文搜索
MatchQueryBuilder titleQuery = QueryBuilders.matchQuery("title", keyword);
//构建普通查询,过滤出在指定价格区间内的产品
RangeQueryBuilder price = QueryBuilders.rangeQuery("price");
//这里暂时先定义价格范围的默认值
price.gte(3000);
price.lte(5000);
//把普通查询放入布尔查询
boolQueryBuilder.must(titleQuery);
//布尔查询,把原生查询放入布尔查询种
boolQueryBuilder.filter(price);
//聚合分析出卖该产品的所有商铺
TermsAggregationBuilder shop= AggregationBuilders.terms("店铺").field("shop").size(10);
//将布尔查询放入原生查询
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
//将聚合查询放入原生查询
nativeSearchQueryBuilder.addAggregation(shop);
//设置分页参数
nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum, pageSize));
//执行原生查询,查看搜索结果
SearchHits<Content> searchs = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Content.class);
for (SearchHit<Content> search:searchs
) {
System.out.println(search.getContent());
}
//查看聚合分析结果,有哪些商铺在卖这种产品
Aggregations aggregations = searchs.getAggregations();
Aggregation aggregation = aggregations.get("店铺");
List<? extends Terms.Bucket> buckets = ((ParsedStringTerms) aggregation).getBuckets();
for (Terms.Bucket bucket : buckets) {
// 获取商铺的集合
String key = (String) bucket.getKey();
System.out.println(key);
}
}
总结
以上就是使用ElasticSearch进行查询的案例,核心是理解DSL语句的逻辑结构,然后就能很轻松地进行各种复杂的查询
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)