multi_select_faceting.md 8.98 KB

Multi-Select Faceting 功能说明

概述

Multi-Select Faceting(多选分面)是业界标准的 Faceted Search 功能,允许用户在选中某个分面筛选项后,仍然能看到该分面的其他可选项,提供更好的探索式搜索体验。

功能特性

1. 两种 Faceting 模式

标准模式(Conjunctive Faceting)

  • 设置: multi_select: false(默认)
  • 行为: 选中某个分面值后,该分面只显示选中的值
  • 适用场景: 层级下钻、逐步精炼
  • ES 实现: 过滤器应用在 query.bool.filter

Multi-Select 模式(Disjunctive Faceting)

  • 设置: multi_select: true
  • 行为: 选中某个分面值后,该分面仍显示所有可选项
  • 适用场景: 颜色、品牌、尺码等可切换属性
  • ES 实现: 过滤器应用在 post_filter

2. Selected 状态标记

所有 facet 值都包含 selected 字段,标记当前是否被选中:

  • selected: true - 当前筛选项已被选中
  • selected: false - 当前筛选项未被选中

使用示例

示例 1: 标准 Category Faceting

{
  "query": "T恤",
  "filters": {
    "category1_name": "服装"
  },
  "facets": [
    {
      "field": "category1_name",
      "size": 10,
      "type": "terms",
      "multi_select": false
    }
  ]
}

响应:

{
  "results": [...],
  "facets": [
    {
      "field": "category1_name",
      "values": [
        {"value": "服装", "count": 150, "selected": true}
      ]
    }
  ]
}

示例 2: Multi-Select Brand Faceting

{
  "query": "手机",
  "filters": {
    "brand_name": "苹果"
  },
  "facets": [
    {
      "field": "brand_name",
      "size": 10,
      "type": "terms",
      "multi_select": true
    }
  ]
}

响应:

{
  "results": [...只包含苹果手机...],
  "facets": [
    {
      "field": "brand_name",
      "values": [
        {"value": "苹果", "count": 150, "selected": true},
        {"value": "华为", "count": 120, "selected": false},
        {"value": "小米", "count": 98, "selected": false}
      ]
    }
  ]
}

示例 3: Specifications Multi-Select

{
  "query": "衬衫",
  "filters": {
    "specifications": {
      "name": "颜色",
      "value": "白色"
    }
  },
  "facets": [
    {
      "field": "specifications.颜色",
      "size": 10,
      "type": "terms",
      "multi_select": true
    },
    {
      "field": "specifications.尺码",
      "size": 10,
      "type": "terms",
      "multi_select": false
    }
  ]
}

响应:

{
  "results": [...只包含白色衬衫...],
  "facets": [
    {
      "field": "specifications.颜色",
      "label": "颜色",
      "values": [
        {"value": "白色", "count": 50, "selected": true},
        {"value": "蓝色", "count": 35, "selected": false},
        {"value": "黑色", "count": 28, "selected": false}
      ]
    },
    {
      "field": "specifications.尺码",
      "label": "尺码",
      "values": [
        {"value": "M", "count": 20, "selected": false},
        {"value": "L", "count": 18, "selected": false},
        {"value": "XL", "count": 12, "selected": false}
      ]
    }
  ]
}

注意:尺码分面(multi_select: false)的统计是基于白色衬衫的。

示例 4: 混合多个 Multi-Select Facets

{
  "query": "*",
  "filters": {
    "category1_name": "玩具",
    "specifications": [
      {"name": "颜色", "value": "红色"},
      {"name": "材质", "value": "塑料"}
    ]
  },
  "facets": [
    {
      "field": "category1_name",
      "size": 10,
      "multi_select": true
    },
    {
      "field": "specifications.颜色",
      "size": 10,
      "multi_select": true
    },
    {
      "field": "specifications.材质",
      "size": 10,
      "multi_select": true
    },
    {
      "field": "specifications.年龄段",
      "size": 10,
      "multi_select": false
    }
  ]
}

行为说明:

  • category1_name: 显示所有类目选项(玩具被标记为 selected)
  • specifications.颜色: 显示所有颜色选项(红色被标记为 selected)
  • specifications.材质: 显示所有材质选项(塑料被标记为 selected)
  • specifications.年龄段: 只显示符合当前过滤条件的年龄段选项

前端集成建议

React 示例

function FacetComponent({ facet }) {
  return (
    <div className="facet">
      <h3>{facet.label}</h3>
      {facet.values.map(value => (
        <label key={value.value} className={value.selected ? 'active' : ''}>
          <input 
            type="checkbox"
            checked={value.selected}
            onChange={() => toggleFilter(facet.field, value.value)}
          />
          {value.value} ({value.count})
        </label>
      ))}
    </div>
  );
}

Vue 示例

<template>
  <div class="facet">
    <h3>{{ facet.label }}</h3>
    <label 
      v-for="value in facet.values" 
      :key="value.value"
      :class="{ active: value.selected }"
    >
      <input 
        type="checkbox"
        :checked="value.selected"
        @change="toggleFilter(facet.field, value.value)"
      />
      {{ value.value }} ({{ value.count }})
    </label>
  </div>
</template>

技术实现细节

Elasticsearch Query 结构

Multi-Select Faceting 使用 post_filter 实现:

{
  "query": {
    "bool": {
      "must": [...],
      "filter": [
        // 只包含 multi_select=false 的过滤器
        {"term": {"category2_name": "短袖T恤"}}
      ]
    }
  },
  "post_filter": {
    "bool": {
      "filter": [
        // 包含 multi_select=true 的过滤器
        {"term": {"brand_name": "苹果"}},
        {
          "nested": {
            "path": "specifications",
            "query": {
              "bool": {
                "must": [
                  {"term": {"specifications.name": "颜色"}},
                  {"term": {"specifications.value": "白色"}}
                ]
              }
            }
          }
        }
      ]
    }
  },
  "aggs": {
    // 所有聚合都基于 query 的结果(不受 post_filter 影响)
    "brand_name_facet": {...},
    "specifications_颜色_facet": {...}
  }
}

关键点:

  • query.bool.filter: 影响结果和聚合
  • post_filter: 只影响结果,不影响聚合
  • 聚合统计基于 query 的结果,因此 multi-select facet 可以显示多个选项

最佳实践

1. 何时使用 Multi-Select

Facet 类型 推荐模式 原因
颜色 multi_select: true 用户需要切换颜色
品牌 multi_select: true 用户需要比较不同品牌
尺码 multi_select: true 用户需要查看其他尺码
类目 multi_select: false 层级下钻
价格区间 multi_select: false 互斥选择
是否有货 multi_select: false 布尔值筛选

2. 性能考虑

  • 过多 Multi-Select: 会增加 ES 查询复杂度
  • 建议: 最多 3-5 个 multi-select facets
  • 优化: 对于不常用的属性使用标准模式

3. UI 设计建议

  • Multi-Select Facets: 使用复选框(Checkbox)
  • Standard Facets: 使用单选框(Radio)或链接
  • Selected 状态: 使用不同颜色或图标标识

API 变更说明

新增字段

FacetConfig:

{
  "field": "brand_name",
  "size": 10,
  "type": "terms",
  "multi_select": true  // 新增字段
}

FacetValue:

{
  "value": "苹果",
  "count": 150,
  "selected": true  // 现在由后端返回真实状态
}

兼容性

  • multi_select 默认为 false,保持向后兼容
  • 旧版 API 调用仍然有效(使用标准模式)

测试验证

运行测试脚本:

python test_multi_select_facet.py

测试覆盖:

  1. ✓ 标准 Faceting (multi_select=false)
  2. ✓ Multi-Select Faceting (multi_select=true)
  3. ✓ Specifications Multi-Select
  4. ✓ ES Query 结构验证

故障排查

问题 1: Multi-Select 不生效

症状: 设置了 multi_select: true,但仍然只返回一个值

检查:

  1. 确认 multi_select 字段在请求中正确设置
  2. 检查 ES query 是否包含 post_filter(开启 debug: true
  3. 验证 Elasticsearch 版本支持 post_filter

问题 2: Selected 标记不正确

症状: selected 字段没有正确标记

检查:

  1. 确认 filters 中的字段名与 facet 字段名一致
  2. 对于 specifications,检查 namevalue 是否匹配
  3. 检查 filters 的值类型(字符串、数组等)

问题 3: 性能问题

症状: 启用 Multi-Select 后查询变慢

优化:

  1. 减少 multi-select facets 数量
  2. 降低 facet size 参数
  3. 考虑使用缓存
  4. 为常用字段建立索引

参考资料