首页 > IT > it

Elasticearch实现商品搜索与聚合分析

2023-02-15
字号:
Elasticearch实现商品搜索与聚合分析

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 searchs = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Content.class);
        for (SearchHit search:searchs
             ) {
            System.out.println(search.getContent());
        }
        //查看聚合分析结果,有哪些商铺在卖这种产品
        Aggregations aggregations = searchs.getAggregations();
        Aggregation aggregation = aggregations.get("店铺");
        List buckets = ((ParsedStringTerms) aggregation).getBuckets();
        for (Terms.Bucket bucket : buckets) {
            // 获取商铺的集合
            String key = (String) bucket.getKey();
            System.out.println(key);
        }
    }
总结

以上就是使用ElasticSearch进行查询的案例,核心是理解DSL语句的逻辑结构,然后就能很轻松地进行各种复杂的查询

今日热点

大家都在看