基于 Elasticsearch 聚合搜索实现电商筛选查询功能

前言

在网上购物时,首先是需要输入关键字检索商品,当进入搜索页时,一般夺会有一个筛选,方便用户进一步缩小商品范围,例如某宝、某东,其上面的商品都是亿万级别的体量,从下图可以得出,筛选条件中包括价格、品牌、商品规格属性(功效、净含量...)等,并且不同的搜索条件展示出来的筛选内容也是截然不同的,在这里介绍如何基于 Elasticsearch 的聚合搜索实现此功能

某东

某宝

筛选条件

实现筛选过滤功能,首先得对 dsl 语句有一定的了解(小伙伴自行补课~)

  1. 创建索引

在这里有一点要注意的是其中 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"
                    }
                }
            }
        }
    }
}
  1. 创建数据
# 创建数据
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":"青少年"}]}
  1. 根据搜索内容找出全部可筛选条件

模拟场景:输入“短袖”进行搜索,找到可筛选的 品牌(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
                }
              ]
            }
          }
        ]
      }
    }

得到了以上结果即可通过服务端组装数据(这里就不在深入了~)

筛选查询

经过以上步骤可以得到搜索时的全部筛选条件,那么下一步就是带入筛选条件进行查询商品了,话不多说咱们开始

  1. 条件搜索

模拟场景:搜索关键词:“短袖”,品牌为“优衣库”,适用季节为“春季”的商品
根据模拟场景可得出以下参数

  1. keyword (关键词)
  2. brandId (品牌id)
  3. attrKeyId (属性id)
  4. 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聚合实现电商筛选搜索的思路,如果老哥们有好的建议或是意见的话欢迎在评论区留言哦~

原文地址:https://www.cnblogs.com/z-coding/p/15045724.html