【实战ES】实战 Elasticsearch:快速上手与深度实践-5.2.2聚合筛选器性能优化(Filter vs Post Filter)

发布于:2025-03-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

👉 点击关注不迷路
👉 点击关注不迷路
👉 点击关注不迷路


5.2.1 电商商品搜索多字段权重控制实战指南

  • 多字段权重搜索在商品搜索中的应用架构
性能优化
权重配置管理
Elasticsearch 核心处理
解析查询词
结果缓存
结果排序与过滤
缓存命中
返回搜索结果
实时数据更新
权重参数存储
权重配置管理
权重规则引擎
权重验证机制
分词器处理
查询解析
生成查询 DSL
多字段权重计算
BM25 基础评分
应用权重函数
最终评分计算
用户输入搜索词
请求发送至 Elasticsearch 集群
多字段权重处理模块
计算相关性评分
动态权重调整

1. 权重分配核心原理

1.1 字段价值权重矩阵

字段类型 业务价值 搜索相关性权重 典型提升策略
商品标题 9.8/10 10x 同义词扩展+前缀匹配
品牌名称 8.5/10 5x 精准匹配+品牌别名库
商品类目 7.2/10 3x 类目路径加权+层级衰减
商品属性 6.5/10 2x 属性值归一化处理
商品描述 5.0/10 1x 停用词过滤+关键短语提取

1.2 BM25算法增强公式

在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 公式应用场景
    • 电商搜索: 通过调整字段权重(如标题权重设为 3,品牌设为 2),提升关键信息的匹配优先级。
    • 精准匹配: 结合 BM25 算法和业务规则,平衡词项相关性与业务需求。
    • 动态优化: 通过调整 k1​、b 参数或字段权重,适应不同场景的搜索需求。

2. 索引设计与配置

2.1 索引映射模板

// 创建一个名为 products 的索引
PUT /products
{
    // 索引的设置部分,包含分片数量、相似度算法、分析器等配置
    "settings": {
        "index": {
            // 设置索引的主分片数量为 5
            "number_of_shards": 5,
            // 配置自定义的相似度算法
            "similarity": {
                // 定义一个名为 custom_bm25 的自定义相似度配置
                "custom_bm25": {
                    // 指定使用 BM25 相似度算法
                    "type": "BM25",
                    // 设置 BM25 算法中的 k1 参数,用于控制词频的饱和度,这里设置为 1.2
                    "k1": 1.2,
                    // 设置 BM25 算法中的 b 参数,用于控制文档长度对相关性得分的影响,这里设置为 0.75
                    "b": 0.75
                }
            }
        },
        // 分析器相关的配置,用于对文本进行分词、过滤等操作
        "analysis": {
            // 定义自定义的过滤器
            "filter": {
                // 定义一个名为 brand_synonym 的同义词过滤器
                "brand_synonym": {
                    // 指定过滤器类型为同义词过滤器
                    "type": "synonym",
                    // 指定同义词文件的路径,该文件包含品牌的同义词映射关系
                    "synonyms_path": "analysis/brand_synonyms.txt"
                }
            },
            // 定义自定义的分析器
            "analyzer": {
                // 定义一个名为 title_analyzer 的自定义分析器,用于处理标题字段
                "title_analyzer": {
                    // 指定分析器类型为自定义分析器
                    "type": "custom",
                    // 使用 ik_max_word 分词器对文本进行分词,ik_max_word 是一个中文分词器,会将文本尽可能地切分成更多的词语
                    "tokenizer": "ik_max_word",
                    // 定义一系列的过滤器,依次对分词结果进行处理
                    "filter": ["lowercase", "stop", "title_synonym"]
                    // lowercase 过滤器将所有字符转换为小写
                    // stop 过滤器用于移除停用词(如“的”“是”等无实际意义的词)
                    // title_synonym 过滤器用于处理标题的同义词替换
                }
            }
        }
    },
    // 索引的映射部分,定义了文档中各个字段的类型、分析器、权重等信息
    "mappings": {
        "properties": {
            // 定义 title 字段,用于存储商品标题
            "title": {
                // 指定字段类型为文本类型
                "type": "text",
                // 使用之前定义的 title_analyzer 分析器对标题文本进行处理
                "analyzer": "title_analyzer",
                // 为 title 字段设置权重为 10.0,在搜索时该字段的匹配结果会有更高的相关性得分
                "boost": 10.0,
                // 为 title 字段创建一个子字段 exact,类型为 keyword,用于精确匹配
                "fields": {
                    "exact": { "type": "keyword" }
                }
            },
            // 定义 brand 字段,用于存储商品品牌
            "brand": {
                // 指定字段类型为文本类型
                "type": "text",
                // 使用 whitespace 分词器对品牌文本进行分词,该分词器会按照空格进行分词
                "analyzer": "whitespace",
                // 为 brand 字段设置权重为 5.0
                "boost": 5.0,
                // 为 brand 字段创建一个子字段 raw,类型为 keyword,用于精确匹配
                "fields": {
                    "raw": { "type": "keyword" }
                }
            },
            // 定义 category 字段,用于存储商品类目
            "category": {
                // 指定字段类型为文本类型
                "type": "text",
                // 使用 path_analyzer 分析器对类目文本进行处理,path_analyzer 通常用于处理类似文件路径的层级结构文本
                "analyzer": "path_analyzer",
                // 为 category 字段设置权重为 3.0
                "boost": 3.0
            }
        }
    }
}

2.2 字段权重配置表

字段 基础权重 匹配类型 特殊处理
title 10 ik_max_word+同义词 前缀匹配(edge_ngram)
title.exact 15 keyword精确匹配 查询时提升
brand 5 分词匹配 品牌别名库扩展
brand.raw 8 keyword精确匹配 查询时加权
category 3 path_hierarchy 层级衰减因子0.5

3. 多权重查询构建

3.1 复合查询模板

// 向 Elasticsearch 中的 products 索引发起搜索请求
GET /products/_search
{
    // 查询部分,定义了搜索的条件和逻辑
    "query": {
        // 使用布尔查询(bool),布尔查询可以组合多个子查询
        "bool": {
            // should 子句表示其中的查询条件只要满足一个或多个即可
            "should": [
                {
                    // 使用 multi_match 查询,用于在多个字段中进行匹配搜索
                    "multi_match": {
                        // 要搜索的关键词,这里是“苹果手机”
                        "query": "苹果手机",
                        // multi_match 查询的类型为 best_fields,它会在每个字段中进行匹配,然后选择匹配得分最高的字段作为最终得分
                        "type": "best_fields",
                        // 指定要搜索的字段列表,并为每个字段设置权重(使用 ^ 符号)
                        "fields": [
                            // title 字段权重为 10
                            "title^10", 
                            // title.exact 子字段权重为 15,用于精确匹配
                            "title.exact^15",
                            // brand 字段权重为 5
                            "brand^5",
                            // brand.raw 子字段权重为 8,用于精确匹配
                            "brand.raw^8",
                            // category 字段权重为 3
                            "category^3"
                        ],
                        // tie_breaker 参数用于处理多个字段匹配得分相近的情况,取值范围 0 到 1,这里设置为 0.3
                        "tie_breaker": 0.3
                    }
                },
                {
                    // 使用 term 查询,用于精确匹配某个字段的值
                    "term": {
                        // 要匹配的字段为 category_path
                        "category_path": {
                            // 要匹配的值为“手机/智能手机/苹果”
                            "value": "手机/智能手机/苹果",
                            // 为该查询设置权重为 2.0
                            "boost": 2.0
                        }
                    }
                }
            ],
            // 规定 should 子句中至少要有一个查询条件被满足,这里设置为 1
            "minimum_should_match": 1
        }
    },
    // 重评分部分,用于对初始查询结果进行二次评分,以调整结果的排序
    "rescore": {
        // window_size 表示重评分操作所考虑的初始查询结果的数量,这里设置为 100
        "window_size": 100,
        "query": {
            "rescore_query": {
                // 使用 function_score 查询,用于根据自定义函数对文档进行评分
                "function_score": {
                    // 基础查询,这里使用 match_all 查询,表示对所有文档进行重评分
                    "query": {"match_all": {}},
                    // 定义一系列的评分函数
                    "functions": [
                        {
                            // 使用 field_value_factor 函数,根据文档中某个字段的值来调整评分
                            "field_value_factor": {
                                // 要使用的字段为 sales_7d,代表 7 天内的销量
                                "field": "sales_7d",
                                // 因子值,用于调整字段值对评分的影响程度,这里设置为 0.1
                                "factor": 0.1,
                                // 修饰符,对字段值进行转换,这里使用 log1p 函数(log(1 + x))
                                "modifier": "log1p"
                            }
                        },
                        {
                            // 使用 gauss 函数,基于高斯分布对文档进行评分
                            "gauss": {
                                // 要使用的字段为 stock,代表库存数量
                                "stock": {
                                    // 中心点,这里设置为 100,即库存为 100 时得分最高
                                    "origin": 100,
                                    // 尺度参数,控制高斯分布的宽度,这里设置为 50
                                    "scale": 50
                                }
                            }
                        }
                    ],
                    // boost_mode 表示如何将基础查询得分和函数得分进行组合,这里使用 multiply 模式,即相乘
                    "boost_mode": "multiply"
                }
            }
        }
    }
}

3.2 权重分配策略对比

策略类型 优点 缺点 适用场景
静态权重 简单易用 无法动态调整 中小型商品库
动态权重 实时响应业务变化 实现复杂度高 促销活动期间
混合权重 兼顾稳定性与灵活性 需要精细调优 大型电商平台
机器学习权重 自动优化效果最佳 需要大量训练数据 有AI团队的头部企业

4. 效果验证与调优

4.1 搜索质量评估矩阵

测试用例 期望结果 基础权重结果 优化后结果 提升率
“苹果手机” 苹果品牌手机优先 第3位 第1位 100%↑
“小米充电器” 小米品牌充电器在前 第5位 第1位 400%↑
“冬季连衣裙” 当季热销商品优先 第8位 第2位 300%↑
“华为手机壳” 精准匹配型号优先 未匹配 第1位 N/A

4.2 性能压测数据

并发用户数 平均响应时间(基础) 平均响应时间(优化) 吞吐量提升 准确率变化
100 86ms 78ms +9% +22%
500 142ms 115ms +23% +18%
1000 238ms 182ms +31% +15%
5000 623ms 429ms +45% +9%

5. 企业级最佳实践

5.1 动态权重调整方案

// 伪代码:基于实时销量的动态权重
// 定义一个名为 DynamicWeightService 的类,该类用于实现动态权重更新的功能
public class DynamicWeightService {

    // @Scheduled 是 Spring 框架提供的注解,用于创建定时任务
    // fixedRate = 60000 表示该方法每隔 60000 毫秒(即 1 分钟)执行一次
    @Scheduled(fixedRate = 60000)
    public void updateWeights() {
        // 调用 getSalesLastHour 方法获取上一小时每个品牌的销售额
        // 返回的 salesMap 是一个键值对集合,键为品牌名称,值为该品牌上一小时的销售额
        Map<String, Double> salesMap = getSalesLastHour();

        // 创建一个 UpdateByQueryRequest 对象,用于对 Elasticsearch 中的文档进行批量更新操作
        // "products" 表示要更新的索引名称,即要对 products 索引下的文档进行更新
        UpdateByQueryRequest request = new UpdateByQueryRequest("products");

        // 设置更新操作使用的脚本
        // 该脚本是 Painless 脚本,用于在 Elasticsearch 中执行自定义逻辑
        // ctx 是脚本上下文对象,ctx._source 表示文档的源数据
        // params 是传递给脚本的参数,这里会传入 salesMap
        // 脚本逻辑为:从 params.salesMap 中获取当前文档品牌对应的销售额
        // 然后根据销售额计算新的权重值,新权重值为 5 加上销售额加 1 后的自然对数乘以 0.5
        request.setScript(new Script(
            "double sales = params.salesMap.get(ctx._source.brand);" +
            "ctx._source.weight = 5 + Math.log(sales + 1) * 0.5;"
        ));

        // 设置脚本执行时使用的参数
        // Collections.singletonMap 用于创建一个只包含一个键值对的不可变 Map
        // 这里将 salesMap 作为名为 "salesMap" 的参数传递给脚本
        request.setParams(Collections.singletonMap("salesMap", salesMap));

        // 调用 Elasticsearch 客户端的 updateByQuery 方法执行更新请求
        // 该方法会将更新操作发送到 Elasticsearch 集群,对符合条件的文档进行更新
        client.updateByQuery(request);
    }

    // 私有方法,用于获取上一小时每个品牌的销售额
    // 该方法的具体实现需要根据实际业务场景从数据源(如数据库)中获取数据
    // 这里只是一个示例方法,具体逻辑需要根据实际情况补充
    private Map<String, Double> getSalesLastHour() {
        // 这里可以添加从数据源获取销售额数据的逻辑
        return null;
    }
}

5.2 大促期间特殊策略

PUT /products/_settings
{
  "index": {
    "similarity": {
      "custom_bm25": {
        "type": "BM25",
        "k1": 0.8,    // 降低TF影响
        "b": 0.6      // 弱化文档长度影响
      }
    }
  }
}

// 查询时动态调整
GET /products/_search
{
  "query": {
    "function_score": {
      "functions": [
        {
          "filter": { "range": { "promotion_level": { "gte": 1 }}},
          "weight": 3
        }
      ]
    }
  }
}

6. 高级优化技巧

6.1 语义相关性增强

// 使用Elasticsearch语义搜索插件
// 向 Elasticsearch 的 products 索引发送搜索请求
GET /products/_search
{
    // 查询部分,定义搜索的具体条件
    "query": {
        // 使用 neural 查询,这是 Elasticsearch 中用于执行基于向量的语义搜索的查询类型
        "neural": {
            // 指定要进行向量搜索的字段为 text_embedding
            // 该字段通常存储文本的向量表示(嵌入向量)
            "text_embedding": {
                // 要搜索的文本内容,这里是“夏季薄款T恤”
                // Elasticsearch 会将该文本转换为向量,然后与 text_embedding 字段中的向量进行相似度比较
                "query_text": "夏季薄款T恤",
                // 指定用于将查询文本转换为向量的模型的 ID
                // 这里使用的是名为 my_text_embedding_model 的模型
                "model_id": "my_text_embedding_model",
                // k 参数指定了初始检索时要返回的最相似文档的数量
                // 这里设置为 100,意味着会先找出 100 个最相似的文档
                "k": 100
            }
        }
    },
    // 排名部分,用于对搜索结果进行重新排序
    "rank": {
        // 使用 RRF(Reciprocal Rank Fusion)算法进行结果融合和重新排序
        // RRF 算法可以结合多个不同查询的结果,以提高搜索的准确性和多样性
        "rrf": {
            // window_size 指定了参与 RRF 计算的每个查询结果的数量
            // 这里设置为 100,表示会使用每个查询的前 100 个结果进行 RRF 计算
            "window_size": 100,
            // rank_constant 是 RRF 算法中的一个常量参数
            // 它用于调整排名分数的计算,值越大,较早排名的文档对最终分数的影响越大
            // 这里设置为 20
            "rank_constant": 20
        }
    }
}
  • RRF(Reciprocal Rank Fusion)算法
    • 是一种用于多查询结果融合与排序的算法,其核心思想是通过整合多个不同查询的结果,提升搜索的准确性和多样性
      • 融合多源结果: 将多个独立查询(如向量搜索、关键词搜索、过滤条件)的结果合并,并重新排序。
      • 降低单一查询偏差: 避免因单一查询的局限性(如仅依赖语义相关性或仅依赖关键词匹配)导致的结果失衡。
      • 基于排名的融合: 通过每个文档在各查询结果中的排名计算综合分数,而非直接比较原始得分。
        在这里插入图片描述

6.2 多维度排序策略

排序因子 权重 计算方式 更新频率
文本相关性 0.6 BM25+自定义权重 实时
销量 0.2 7日销量对数 每小时
库存周转率 0.1 销量/库存 每天
用户行为 0.1 CTR+CVR 实时

7. 异常处理与监控

7.1 常见问题排查表

现象 可能原因 解决方案 优先级
相关商品排序异常 权重计算逻辑错误 检查function_score脚本 P0
新商品无法被搜索到 索引延迟 调整refresh_interval P1
长尾词效果差 分词策略不当 优化自定义词典 P1
高并发时响应慢 查询DSL复杂度高 启用查询缓存+精简排序条件 P0

7.2 关键监控指标

指标名称 告警阈值 监控方法 优化方向
搜索响应时间P99 > 500ms APM全链路监控 精简查询逻辑
权重更新延迟 > 10s 日志时间戳比对 优化批处理任务
缓存命中率 < 80% 索引缓存统计API 扩大缓存内存
长尾查询占比 > 30% 查询日志分析 优化搜索建议

附录:权重调优工具包

工具名称 用途 使用示例
Search Profiler 查询性能分析 GET /_search/profile
Term Vectors API 查看词项统计 GET /products/_termvectors
Explain API 理解评分细节 GET /products/_explain
Ranking EVAL 搜索效果评估 POST /_rank_eval

实施建议

  1. 新权重策略需通过A/B测试验证
  2. 重要调整需在低峰期灰度发布
  3. 建立搜索质量评估体系
  4. 定期进行词库维护
  • 灰度发布(Gray Release)
    • 灰度发布(又称金丝雀发布Canary Release)是一种分阶段部署策略,通过将新功能或更新逐步推送给部分用户(“金丝雀用户”),在真实环境中验证其稳定性、性能和用户反馈后,再扩大至全部用户。
    • 其核心目标是降低风险,避免大规模故障或用户体验下降。
    • 核心逻辑流程图
      • 分阶段验证: 先向小部分用户(如 1%)发布,监控日志、错误率、性能指标等,确保无重大问题后再逐步扩大范围(如 5%、20%、100%)。
      • 风险隔离: 若出现问题,可快速回滚,仅影响少量用户。
      • 数据驱动决策: 基于 A/B 测试或实时反馈,优化功能表现。
自动回滚触发条件
分桶规则示例
错误率>5%
是否满足条件?
响应时间突增30%
用户投诉率激增
按用户ID哈希分桶
定义用户分桶规则(如ID哈希)
按设备类型分桶
按地理位置分桶
开始
准备阶段
配置灰度规则
分桶策略设计
设置流量比例(如1%)
准备新版本代码
灰度部署
选择金丝雀用户组
部署新版本到生产环境
监控阶段
跟踪关键指标(错误率/响应时间)
收集用户反馈
扩大发布范围
回滚至旧版本
重复监控直到全量
结束