You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多属性多选项Elasticsearch筛选器优化:保留可选值

多属性商品筛选可选值优化问题

问题背景

已基于Elasticsearch实现多属性多选项的商品筛选功能,核心筛选逻辑正常,但返回前端的后续可选筛选值仅保留用户已选中的选项(比如选了2020年后,Year属性的可选值只剩2020,丢失其他符合当前筛选条件的年份),需要优化以返回所有符合当前筛选结果集的可选属性值。

数据示例

单商品属性结构

{"attributes": [{"id": "bafa62bf-cc24-4434-943e-6931bb31091f","value": "Black","attributeName": "Color","attributeId": "1c5aae51-a58b-44bc-9073-2c0e4e97bebe"},{"id": "71dd6aeb-03ab-4fae-9ebd-6bf41ab821fa","value": "Brand","attributeName": "Tesla","attributeId": "4c1c9939-6348-42e4-9c07-293587645300"},{"id": "f5fc9a7f-cc11-46fe-aa04-aa0f21ef4f14","value": "Sedan","attributeName": "Type","attributeId": "8cdf4684-3306-4924-92dd-36efc874d416"},{"id": "c666cb4a-4442-4979-aed3-4156a6d92fec","value": "KM","attributeName": "0","attributeId": "975461d4-65a6-47fa-8dc9-595d6def34c5"},{"id": "b3c66548-2a56-47f2-bdfd-2fc043d9d330","value": "2020","attributeName": "Year","attributeId": "f546d055-8c87-45cf-8700-b3d6ca114dbc"}]}

前端筛选请求Payload

{"attributesForFilter": [{"id": "4c1c9939-6348-42e4-9c07-293587645300","values": ["Tesla"]},{"id": "1c5aae51-a58b-44bc-9073-2c0e4e97bebe","values": ["Gray","Black"]},{"id": "f546d055-8c87-45cf-8700-b3d6ca114dbc","values": ["2020"]}]}

当前错误返回结果

{"attributes": [{"id": "1c5aae51-a58b-44bc-9073-2c0e4e97bebe","name": "Color","source": {"options": [{"id": "6e73fac6-95b3-4665-97ef-8590f9702ddd","value": "Black"}]}},{"id": "71dd6aeb-03ab-4fae-9ebd-6bf41ab821fa","name": "Brand","source": {"options": [{"id": "6e73fac6-95b3-4665-97ef-8590f9702ddd","value": "Tesla"}]}},{"id": "b3c66548-2a56-47f2-bdfd-2fc043d9d330","name": "Year","source": {"options": [{"id": "6e73fac6-95b3-4665-97ef-8590f9702ddd","value": "2020"}]}}]}

当前Elasticsearch查询语句

{"post_filter": {"bool": {"filter": [{"term": {"status": "active"}},{"bool": {"must_not": {"exists": {"field": "officeId"}}}},{"bool": {"must": {"exists": {"field": "asset"}}}},{"term": {"categories.id": "6cd9fef8-5c19-4b36-8297-e0fa229df687"}},{"bool": {"must": [{"bool": {"should": [{"bool": {"should": [{"nested": {"path": "attributes","query": {"bool": {"must": [{"multi_match": {"analyzer": "custom_analyzer","fields": ["attributes.value^3"],"query": "Tesla","type": "phrase_prefix"}},{"term": {"attributes.attributeId": "71dd6aeb-03ab-4fae-9ebd-6bf41ab821fa"}}]}}}}]}}]}},{"bool": {"should": [{"bool": {"should": [{"nested": {"path": "attributes","query": {"bool": {"must": [{"multi_match": {"analyzer": "custom_analyzer","fields": ["attributes.value^3"],"query": "Black","type": "phrase_prefix"}},{"term": {"attributes.attributeId": "1c5aae51-a58b-44bc-9073-2c0e4e97bebe"}}]}}}}]}},{"bool": {"should": [{"nested": {"path": "attributes","query": {"bool": {"must": [{"multi_match": {"analyzer": "custom_analyzer","fields": ["attributes.value^3"],"query": "Gray","type": "phrase_prefix"}},{"term": {"attributes.attributeId": "1c5aae51-a58b-44bc-9073-2c0e4e97bebe"}}]}}}}]}}]}},{"bool": {"should": [{"bool": {"should": [{"nested": {"path": "attributes","query": {"bool": {"must": [{"multi_match": {"analyzer": "custom_analyzer","fields": ["attributes.value^3"],"query": "2020","type": "phrase_prefix"}},{"term": {"attributes.attributeId": "f546d055-8c87-45cf-8700-b3d6ca114dbc"}}]}}}}]}}]}}]}}},"aggs": {"attributes": {"nested": {"path": "attributes"},"aggs": {"attributeId": {"terms": {"field": "attributes.attributeId"},"aggs": {"top_attribute_hits": {"top_hits": {"_source": {"includes": ["attributes"]},"size": 10}}}}}}}

优化方案

1. 调整Elasticsearch聚合逻辑

当前聚合使用top_hits只能返回匹配到的属性条目,无法获取当前筛选结果集中所有可选的属性值。需要修改聚合结构,针对每个attributeId下的value做分桶聚合,同时保留属性名称等元数据:

{
  "post_filter": {...}, // 保留原有的post_filter筛选逻辑不变
  "aggs": {
    "attributes": {
      "nested": {"path": "attributes"},
      "aggs": {
        "attributeId": {
          "terms": {"field": "attributes.attributeId"},
          "aggs": {
            "attributeName": {
              "terms": {"field": "attributes.attributeName", "size": 1}
            },
            "values": {
              "terms": {"field": "attributes.value", "size": 100},
              "aggs": {
                "valueId": {
                  "terms": {"field": "attributes.id", "size": 1}
                }
              }
            }
          }
        }
      }
    }
  }
}

聚合说明:

  • attributeId分桶:按属性ID分组,确保同一属性的选项归为一类
  • attributeName聚合:获取该属性的名称(同一attributeId对应唯一attributeName,size设为1即可)
  • values分桶:获取当前筛选结果集中该属性下所有存在的可选值
  • valueId聚合:获取每个属性值对应的唯一ID

2. Golang代码逻辑调整

在解析ES聚合结果时,需要遍历聚合桶,把每个属性的所有可选值收集起来,而不是只保留用户选中的条目:

// 定义ES聚合结果的结构体
type AggregationResult struct {
    Attributes struct {
        AttributeId struct {
            Buckets []struct {
                Key          string `json:"key"` // attributeId
                AttributeName struct {
                    Buckets []struct {
                        Key string `json:"key"` // attributeName
                    } `json:"buckets"`
                } `json:"attributeName"`
                Values struct {
                    Buckets []struct {
                        Key     string `json:"key"` // 属性值
                        ValueId struct {
                            Buckets []struct {
                                Key string `json:"key"` // 属性值ID
                            } `json:"buckets"`
                        } `json:"valueId"`
                    } `json:"buckets"`
                } `json:"values"`
            } `json:"buckets"`
        } `json:"attributeId"`
    } `json:"attributes"`
}

// 转换为前端需要的响应结构
type AttributeOption struct {
    Id     string `json:"id"`
    Name   string `json:"name"`
    Source struct {
        Options []Option `json:"options"`
    } `json:"source"`
}

type Option struct {
    Id    string `json:"id"`
    Value string `json:"value"`
}

func transformAggToResponse(agg AggregationResult) []AttributeOption {
    var attributes []AttributeOption
    for _, attrBucket := range agg.Attributes.AttributeId.Buckets {
        attr := AttributeOption{
            Id:   attrBucket.Key,
            Name: attrBucket.AttributeName.Buckets[0].Key,
        }
        for _, valBucket := range attrBucket.Values.Buckets {
            option := Option{
                Id:    valBucket.ValueId.Buckets[0].Key,
                Value: valBucket.Key,
            }
            attr.Source.Options = append(attr.Source.Options, option)
        }
        attributes = append(attributes, attr)
    }
    return attributes
}

关键注意点

  • 确保post_filter位置正确:聚合会基于post_filter过滤后的结果集计算,这样返回的可选值就是符合当前筛选条件的所有选项
  • 调整聚合size参数:根据业务需求设置terms聚合的size,避免遗漏可选值
  • 字段映射检查:attributes.attributeId、attributes.value、attributes.id等字段必须是keyword类型,否则分桶聚合会出现分词错误

内容的提问来源于stack exchange,提问作者wiliamvj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:27:02