ES_QUERY_RESTRUCTURE_COMPLETE.md 9.28 KB

ES查询结构重构完成报告

完成日期: 2025-11-12
核心原则: 统一约定,保持简单,类型安全


问题回顾

原始问题

  1. Facets返回为空 - 已修复
  2. 前端listing time筛选失败 - 已修复
  3. Filter不作用于KNN查询 - 已修复(核心问题)

根本原因

KNN和query平级,导致filter只作用于query,KNN召回的结果没有被过滤。

原结构(错误):

{
  "query": {
    "bool": {
      "must": [{"multi_match": {...}}],
      "filter": [...]  // 只作用于multi_match
    }
  },
  "knn": {...}  // 与query平级,filter不作用于它
}

解决方案

方案C:Function Score + Bool Should(已实施)

新结构(正确):

{
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  {"multi_match": {...}},  // 文本查询
                  {"knn": {...}}           // KNN查询
                ],
                "minimum_should_match": 1  // 至少匹配一个
              }
            }
          ],
          "filter": [...]  // 作用于整个查询
        }
      },
      "functions": [
        {
          "filter": {"range": {"days_since_last_update": {"lte": 30}}},
          "weight": 1.1
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply"
    }
  }
}

关键改进

  1. Filter统一作用 - 外层bool.filter同时作用于文本和KNN
  2. 灵活召回 - 文本或KNN至少匹配一个(minimum_should_match=1)
  3. ES层打分 - function_score支持多种打分因子
  4. 保留扩展性 - RerankEngine禁用但保留,未来可启用本地重排

修改文件清单

1. /home/tw/SearchEngine/search/multilang_query_builder.py

修改点

  • 重构 build_multilang_query 方法(156-210行)
  • 新增 _build_score_functions 方法(212-237行)

核心改动

# 构建内层bool: 文本和KNN二选一
inner_bool_should = [query_clause]
if enable_knn and query_vector is not None:
    inner_bool_should.append({"knn": {...}})

inner_bool = {
    "bool": {
        "should": inner_bool_should,
        "minimum_should_match": 1
    }
}

# 外层bool包含filter
outer_bool = {
    "bool": {
        "must": [inner_bool],
        "filter": filter_clauses  # 作用于整体
    }
}

# function_score包裹
function_score_query = {
    "function_score": {
        "query": outer_bool,
        "functions": self._build_score_functions(),
        "score_mode": "sum",
        "boost_mode": "multiply"
    }
}

2. /home/tw/SearchEngine/search/rerank_engine.py(新建)

来源:从 ranking_engine.py 重命名 修改

  • 类名:RankingEngineRerankEngine
  • 添加 enabled 参数(默认False)
  • 更新文档说明

关键代码

class RerankEngine:
    """本地重排引擎(当前禁用)"""

    def __init__(self, ranking_expression: str, enabled: bool = False):
        self.enabled = enabled
        self.expression = ranking_expression
        if enabled:
            self.parsed_terms = self._parse_expression(ranking_expression)

    def calculate_score(self, hit, base_score, knn_score=None):
        if not self.enabled:
            return base_score
        # ... 原有逻辑

3. /home/tw/SearchEngine/search/searcher.py

修改点

  • 导入:RankingEngineRerankEngine
  • 初始化:self.rerank_engine = RerankEngine(..., enabled=False)
  • 重排逻辑:检查 self.rerank_engine.enabled

4. /home/tw/SearchEngine/search/__init__.py

修改

from .rerank_engine import RerankEngine  # 原 RankingEngine

5. /home/tw/SearchEngine/search/ranking_engine.py

删除 - 已重命名为 rerank_engine.py


测试结果

✅ Test 1: Filter作用于文本查询

  • 查询:"玩具"
  • Filter: categoryName_keyword = "桌面休闲玩具"
  • 结果:15 hits(正确过滤)

✅ Test 2: Filter作用于KNN查询

  • 查询:"玩具"
  • Range filter: create_time >= "2023-01-01"
  • 结果:64 hits(正确过滤,KNN结果也被过滤)
  • 验证:所有返回结果的create_time都 >= 2023-01-01

✅ Test 3: Function Score时效性加权

  • Function: days_since_last_update <= 30 → weight 1.1
  • 结果:打分函数正常工作

✅ Test 4: 混合查询结构

  • Inner bool.should包含2个子句:
    • 文本查询(multi_match)
    • KNN查询
  • minimum_should_match=1(至少匹配一个)

✅ Test 5: Facets + Filters

  • 返回正确的facets
  • Selected字段正确标记

ES Query 结构验证

完整查询示例

{
  "size": 5,
  "from": 0,
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  {
                    "bool": {
                      "should": [
                        {"multi_match": {"query": "玩具", "fields": [...]}}
                      ],
                      "minimum_should_match": 1
                    }
                  },
                  {
                    "knn": {
                      "field": "name_embedding",
                      "query_vector": [...],
                      "k": 50,
                      "num_candidates": 200
                    }
                  }
                ],
                "minimum_should_match": 1
              }
            }
          ],
          "filter": [
            {"term": {"categoryName_keyword": "桌面休闲玩具"}},
            {"range": {"create_time": {"gte": "2023-01-01T00:00:00Z"}}}
          ]
        }
      },
      "functions": [
        {
          "filter": {
            "range": {"days_since_last_update": {"lte": 30}}
          },
          "weight": 1.1
        }
      ],
      "score_mode": "sum",
      "boost_mode": "multiply"
    }
  }
}

结构分析

三层嵌套

  1. 最外层function_score - 支持额外打分因子
  2. 外层bool:包含mustfilter - filter作用于所有查询
  3. 内层bool:包含should子句 - 文本OR KNN

数据流

用户查询
  → 文本查询 OR KNN查询(至少一个匹配)
  → 应用filter(同时过滤文本和KNN结果)
  → 应用function_score加权
  → 返回最终结果

架构优势

1. 正确性

  • ✅ Filter同时作用于文本和KNN
  • ✅ 不会有未过滤的KNN结果混入

2. 灵活性

  • ✅ 文本或KNN至少匹配一个(更高召回)
  • ✅ Function score支持多种打分因子
  • ✅ 保留RerankEngine用于未来扩展

3. 性能

  • ✅ Filter在ES层执行(硬过滤,不参与打分)
  • ✅ Function score在ES层执行(无需本地重排)
  • ✅ 减少数据传输(已过滤)

4. 可维护性

  • ✅ 查询结构清晰
  • ✅ 统一约定,不做兼容
  • ✅ 类型安全(Pydantic模型)

命名规范

RankingEngine → RerankEngine

语义区分

  • Ranking - 排序、打分(通常指ES层的原生排序)
  • Rerank - 重排序(通常指对ES结果的二次排序)

新架构

  • ES层:使用 function_score 进行打分和排序
  • 应用层:使用 RerankEngine 进行本地重排(当前禁用)

状态

  • RerankEngine.enabled = False - 暂时禁用
  • 未来如需复杂个性化排序可启用

对比总结

方面 重构前 重构后
KNN位置 与query平级 在bool.should内
Filter作用 只作用于文本 同时作用于文本和KNN
召回策略 文本必须匹配 文本OR KNN至少一个
打分方式 本地重排 ES function_score
时效性加权 本地计算 ES function加权
Rerank RankingEngine启用 RerankEngine禁用
前端错误 422错误 正常工作

后续优化建议

1. Function Score扩展

可添加更多打分因子:

functions:
  - filter: {range: {days_since_last_update: {lte: 30}}}
    weight: 1.1
  - filter: {term: {is_video: true}}
    weight: 1.05
  - field_value_factor:
      field: sales_count
      modifier: log1p
      factor: 0.01

2. RerankEngine应用场景

未来如需启用本地重排:

  • 实时个性化(基于用户画像)
  • 复杂业务规则(无法用ES表达)
  • A/B测试(不同排序策略)

3. 性能优化

  • 添加查询缓存
  • 优化embedding生成
  • 监控function_score性能影响

4. 测试覆盖

  • 添加集成测试
  • 性能基准测试
  • 边界情况测试

总结

✅ 核心成就

  1. 修复Filter问题 - Filter现在同时作用于文本和KNN
  2. 统一约定 - 全系统使用Pydantic模型,不做兼容
  3. 优化打分 - 使用ES function_score,性能更好
  4. 命名规范 - RerankEngine语义更清晰
  5. 代码简洁 - 移除所有兼容代码

🎯 架构原则

"统一约定,不做兼容,保持简单"

  • Pydantic模型贯穿全系统
  • 单一数据流
  • 明确的类型定义
  • 清晰的职责划分

📊 代码质量

  • ✅ 无Linter错误
  • ✅ 类型安全
  • ✅ 所有测试通过
  • ✅ 代码简洁清晰

版本: v3.3
状态: ✅ 完成并通过测试
下一步: 根据业务需求调整function_score权重