草庐IT

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

weixin_44909963 2023-11-26 原文

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

Gitee地址:https://gitee.com/yuyuuyuy/micro-mall


文章目录


前言

搜索是互联网各个项目中的常见场景,而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语句的逻辑结构,然后就能很轻松地进行各种复杂的查询

有关ElasticSearch实现商品搜索与聚合分析的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby-on-rails - Nokogiri:使用 XPath 搜索 <div> - 2

    我使用Nokogiri(Rubygem)css搜索寻找某些在我的html里面。看起来Nokogiri的css搜索不喜欢正则表达式。我想切换到Nokogiri的xpath搜索,因为这似乎支持搜索字符串中的正则表达式。如何在xpath搜索中实现下面提到的(伪)css搜索?require'rubygems'require'nokogiri'value=Nokogiri::HTML.parse(ABBlaCD3"HTML_END#my_blockisgivenmy_bl="1"#my_eqcorrespondstothisregexmy_eq="\/[0-9]+\/"#FIXMEThefoll

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  6. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  7. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  8. ruby - 如何搜索有用的 ruby - 2

    寻找有用的ruby的好网站是什么? 最佳答案 AgileWebDevelopment列出插件(虽然不是ruby​​gems,我不确定为什么),并允许人们对它们进行评级。RubyToolbox按类别列出gem并比较它们的受欢迎程度。Rubygems有一个搜索框。StackOverflow对最有用的rails插件和ruby​​gems有疑问。 关于ruby-如何搜索有用的ruby,我们在StackOverflow上找到一个类似的问题: https://stacko

  9. ruby - 如何搜索、递增和替换 Ruby 字符串中的整数子字符串? - 2

    我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/

  10. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

随机推荐