前言
在网上购物时,首先是需要输入关键字检索商品,当进入搜索页时,一般夺会有一个筛选,方便用户进一步缩小商品范围,例如某宝、某东,其上面的商品都是亿万级别的体量,从下图可以得出,筛选条件中包括价格、品牌、商品规格属性(功效、净含量...)等,并且不同的搜索条件展示出来的筛选内容也是截然不同的,在这里介绍如何基于 Elasticsearch 的聚合搜索实现此功能
筛选条件
实现筛选过滤功能,首先得对 dsl 语句有一定的了解(小伙伴自行补课~)
- 创建索引
在这里有一点要注意的是其中 attributes 是用了 nested 类型
不了解的小伙伴请看这位大佬的文章 干货 | Elasticsearch Nested类型深入详解
# 创建索引
PUT product_index
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text"
},
"brandId": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"price": {
"type": "double"
},
"attributes": {
"type": "nested",
"properties": {
"attrKeyId": {
"type": "keyword"
},
"attrKeyName": {
"type": "keyword"
},
"attrValueName": {
"type": "keyword"
}
}
}
}
}
}
- 创建数据
# 创建数据
PUT /product_index/_bulk
{"index": {}}
{"id ":1,"name":"日系短袖","brandId":"10","brand":"优衣库","price":99.99,"attributes":[{"attrKeyId":"1","attrKeyName":"适用季节","attrValueName":"春季"},{"attrKeyId":"2","attrKeyName":"风格","attrValueName":"日系"},{"attrKeyId":"3","attrKeyName":"人群","attrValueName":"青年"}]}
{"index": {}}
{"id ":2,"name":"韩版短袖","brandId":"20","brand":"Kirsh","price":199.99,"attributes":[{"attrKeyId":"1","attrKeyName":"适用季节","attrValueName":"夏季"},{"attrKeyId":"2","attrKeyName":"风格","attrValueName":"韩系"},{"attrKeyId":"3","attrKeyName":"人群","attrValueName":"少年"}]}
{"index": {}}
{"id ":3,"name":"美系短袖","brandId":"30","brand":"Nike","price":299.99,"attributes":[{"attrKeyId":"1","attrKeyName":"适用季节","attrValueName":"秋季"},{"attrKeyId":"2","attrKeyName":"风格","attrValueName":"美系"},{"attrKeyId":"3","attrKeyName":"人群","attrValueName":"中年"}]}
{"index": {}}
{"id ":4,"name":"国产短袖","brandId":"40","brand":"安踏","price":399.99,"attributes":[{"attrKeyId":"1","attrKeyName":"适用季节","attrValueName":"冬季"},{"attrKeyId":"2","attrKeyName":"风格","attrValueName":"国潮"},{"attrKeyId":"3","attrKeyName":"人群","attrValueName":"青少年"}]}
{"index": {}}
{"id ":5,"name":"中国短袖","brandId":"40","brand":"安踏","price":499.99,"attributes":[{"attrKeyId":"1","attrKeyName":"适用季节","attrValueName":"夏季"},{"attrKeyId":"2","attrKeyName":"风格","attrValueName":"国潮"},{"attrKeyId":"3","attrKeyName":"人群","attrValueName":"青少年"}]}
- 根据搜索内容找出全部可筛选条件
模拟场景:输入“短袖”进行搜索,找到可筛选的 品牌(brand)&商品规格属性(attributes) 条件
# 根据搜索内容找出全部可筛选条件
GET /product_index/_search
{
"query": {
"match": {
"name": "短袖"
}
},
"size": 0,
"aggs": {
"brandId": {
"terms": {
"field": "brandId"
},
"aggs": {
"brand": {
"terms": {
"field": "brand"
}
}
}
},
"attra": {
"nested": {
"path": "attributes"
},
"aggs": {
"attrKeyId": {
"terms": {
"field": "attributes.attrKeyId"
},
"aggs": {
"attrKeyName": {
"terms": {
"field": "attributes.attrKeyName"
}
},
"attrValueName": {
"terms": {
"field": "attributes.attrValueName"
}
}
}
}
}
}
}
}
品牌聚合结果:
"brandId" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "40",
"doc_count" : 2,
"brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "安踏",
"doc_count" : 2
}
]
}
},
{
"key" : "10",
"doc_count" : 1,
"brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "优衣库",
"doc_count" : 1
}
]
}
},
{
"key" : "20",
"doc_count" : 1,
"brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Kirsh",
"doc_count" : 1
}
]
}
},
{
"key" : "30",
"doc_count" : 1,
"brand" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Nike",
"doc_count" : 1
}
]
}
}
]
}
商品规格属性聚合结果:
"attr" : {
"doc_count" : 15,
"attrKeyId" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "1",
"doc_count" : 5,
"attrKeyName" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "适用季节",
"doc_count" : 5
}
]
},
"attrValueName" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "夏季",
"doc_count" : 2
},
{
"key" : "冬季",
"doc_count" : 1
},
{
"key" : "春季",
"doc_count" : 1
},
{
"key" : "秋季",
"doc_count" : 1
}
]
}
},
{
"key" : "2",
"doc_count" : 5,
"attrKeyName" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "风格",
"doc_count" : 5
}
]
},
"attrValueName" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "国潮",
"doc_count" : 2
},
{
"key" : "日系",
"doc_count" : 1
},
{
"key" : "美系",
"doc_count" : 1
},
{
"key" : "韩系",
"doc_count" : 1
}
]
}
},
{
"key" : "3",
"doc_count" : 5,
"attrKeyName" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "人群",
"doc_count" : 5
}
]
},
"attrValueName" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "青少年",
"doc_count" : 2
},
{
"key" : "中年",
"doc_count" : 1
},
{
"key" : "少年",
"doc_count" : 1
},
{
"key" : "青年",
"doc_count" : 1
}
]
}
}
]
}
}
得到了以上结果即可通过服务端组装数据(这里就不在深入了~)
筛选查询
经过以上步骤可以得到搜索时的全部筛选条件,那么下一步就是带入筛选条件进行查询商品了,话不多说咱们开始
- 条件搜索
模拟场景:搜索关键词:“短袖”,品牌为“优衣库”,适用季节为“春季”的商品
根据模拟场景可得出以下参数
- keyword (关键词)
- brandId (品牌id)
- attrKeyId (属性id)
- attrValueName (属性值)
其中 attr 可能会选择很多,若是业务场景中每个属性是单选的情况下,推荐使用 k,v 的形式接收规格属性参数
# 根据筛选条件查询商品
GET /product_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "短袖"
}
}
],
"filter": [
{
"bool": {
"must": [
{
"term": {
"brandId": {
"value": "10"
}
}
},
{
"nested": {
"path": "attributes",
"query": {
"bool": {
"must": [
{
"term": {
"attributes.attrKeyId": {
"value": "1"
}
}
},
{
"term": {
"attributes.attrValueName": {
"value": "春季"
}
}
}
]
}
}
}
}
]
}
}
]
}
}
}
搜索结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.17402273,
"hits" : [
{
"_index" : "product_index",
"_type" : "_doc",
"_id" : "_GtUzXoBSEC8FU4FyZf0",
"_score" : 0.17402273,
"_source" : {
"id " : 1,
"name" : "日系短袖",
"brandId" : "10",
"brand" : "优衣库",
"price" : 99.99,
"attributes" : [
{
"attrKeyId" : "1",
"attrKeyName" : "适用季节",
"attrValueName" : "春季"
},
{
"attrKeyId" : "2",
"attrKeyName" : "风格",
"attrValueName" : "日系"
},
{
"attrKeyId" : "3",
"attrKeyName" : "人群",
"attrValueName" : "青年"
}
]
}
}
]
}
}
若将brandId替换成 20(或是替换任意参数),结果为:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
由此可得出筛选过滤查询以实现啦~
总结
这是基于Es聚合实现电商筛选搜索的思路,如果老哥们有好的建议或是意见的话欢迎在评论区留言哦~