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总结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 extends Terms.Bucket> buckets = ((ParsedStringTerms) aggregation).getBuckets(); for (Terms.Bucket bucket : buckets) { // 获取商铺的集合 String key = (String) bucket.getKey(); System.out.println(key); } }
以上就是使用ElasticSearch进行查询的案例,核心是理解DSL语句的逻辑结构,然后就能很轻松地进行各种复杂的查询