网站搜索

Odoo 网站搜索为什么不只是模糊匹配:autocomplete、结果映射与混合搜索页主链路讲透

很多人以为 Odoo 网站搜索只是前台输入框加一个 ilike,但 website 模块其实把自动补全、字段映射、结果高亮、fuzzy term 回退和混合搜索页拆成了一条完整链路。

网站
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 官网搜索不是“输入关键字,然后全站 ilike 一把梭”。

website 模块真正做的是一条分层搜索链路:

  1. 前端请求 /website/snippet/autocomplete
  2. 网站对象根据 search_type 组装多个模型的搜索细节;
  3. 先做精确搜索,必要时再尝试 fuzzy term;
  4. 各模型把记录渲染成统一结构;
  5. 自动补全和 /website/search 混合列表页复用这套结果。

也就是说,Odoo 的网站搜索核心不是“搜到什么”,而是“把不同模型的结果统一成同一种前台可渲染协议”。

这点非常重要。因为只有这样,页面、产品、博客、活动这些不同对象,才能共用一个搜索框和结果页。


一、入口路由为什么是 autocomplete,而不是单独做一个搜索 API

addons/website/controllers/main.py 里,公开入口是:

  • /website/snippet/autocomplete

这个接口返回的不是某个模型的原始记录,而是已经准备好给前端展示的数据:

  • results
  • results_count
  • parts
  • fuzzy_search

这里的设计思路很清楚:

  • 自动补全是第一消费方;
  • 混合搜索页也是第二消费方;
  • 所以前端真正需要的是“可展示数据”,不是 ORM 原始行。

换句话说,Odoo 把“搜索结果渲染”前移到了服务端。

这就是为什么返回里会带:

  • 图标;
  • 字段映射;
  • 高亮后的文本;
  • 截断后的描述;
  • 已格式化的 monetary / html / text 值。

这样前端模板就不必知道每个模型该怎么展示。


二、搜索的关键不是 SQL,而是 _search_get_detail() 协议

真正决定一个模型能不能被网站搜索接入的,不是“有没有 name 字段”,而是它能不能提供搜索细节描述。

在 Odoo 这套机制里,模型通常提供类似 _search_get_detail() 的信息,包括:

  • model
  • base_domain
  • search_fields
  • fetch_fields
  • mapping
  • icon
  • order

这意味着网站搜索不是硬编码“产品搜标题、页面搜 URL”。

而是让每个模型自己声明:

我允许被搜索哪些字段; 搜到后要取哪些字段; 这些字段在前台该如何映射成 name / description / website_url / image_url 之类的统一槽位。

这套协议的好处有三个:

  1. 扩展性强:新模块只要实现协议,就能挂进搜索体系;
  2. 前端稳定:前台永远读统一的结果结构;
  3. 排序与过滤可控:每个模型可以保留自己的 domain 和 order。

所以如果你以后想给自定义网站模型接入搜索,正确做法通常不是改 controller,而是补齐模型侧的搜索细节。


三、为什么 autocomplete 返回前还要走一次 _search_render_results()

website.py 里,_search_render_results() 会遍历每种搜索明细,把记录集转成可展示数据。

这个阶段不是简单 read(),而是把:

  • 取数字段 fetch_fields
  • 字段映射 mapping
  • 图标 icon
  • limit

一起交给记录集的 _search_render_results()

结果里每一行都会带 _mapping,后续 controller 再根据 mapping 组装输出。

这说明 Odoo 的搜索体系故意分了两层:

  • 模型层:知道记录本身该怎样变成展示数据;
  • 控制器层:把多个模型结果拼成统一 JSON。

这个边界特别实用。

因为“产品该显示价格还是分类”“页面该显示摘要还是 URL”这种决策,最懂的一定是对应模型,不应该全塞到 controller 里。


website.py 里的 _search_find_fuzzy_term() 有几个很值得注意的保护条件:

  • 少于 4 个字符,不做 fuzzy;
  • 含空格的多词搜索,不做 fuzzy;
  • 数字占比 80% 以上,不做 fuzzy。

这三个限制非常有经验。

1)太短的词不适合模糊匹配

例如用户输入 crmapierp 这种短词时,模糊匹配特别容易把结果带偏。

2)多词搜索更适合保留原意

multi website domain 这类词组,一旦拿去对单词做拼写近似,反而会把真正语义拆坏。

3)数字占比高时,通常是货号、编码、SKU

这类场景最怕“好心办坏事”。

比如用户搜 P79355,系统如果把它糊成别的词,结果体验会非常差。

所以 Odoo 的策略不是“尽可能模糊”,而是:

只在最有可能帮到用户、且误伤较低的场景下启用 fuzzy。

这是一种很产品化的保守设计。


五、结果高亮为什么放在服务端做

在 controller 的 autocomplete() 里,如果某个映射字段声明了 match=True,并且字段类型是文本,Odoo 会:

  • 按搜索词切分文本;
  • 渲染 website.search_text_with_highlight 模板;
  • 返回带高亮片段的 HTML。

这说明 Odoo 不想把“高亮策略”下放到前端各处重复实现。

这样做有几个实际好处:

  1. 同一搜索结果在不同组件里表现一致;
  2. 服务端知道哪些字段允许高亮,哪些不允许;
  3. 截断、转义、HTML 类型格式化能放在一个管道里完成。

也因此,前端看到的并不只是纯文本,而是已经经过:

  • 截断;
  • escape;
  • qweb field 格式化;
  • 高亮模板渲染;

的一份“半成品 UI 数据”。


六、为什么 /website/search 结果页其实复用了 autocomplete

很多人会本能地以为:

  • 输入框自动补全是一套接口;
  • 搜索结果页是另一套完全不同的实现。

但 Odoo 在 hybrid_list() 里直接调用了 self.autocomplete(...),只是把 limit 放大到 500,然后分页展示。

这背后的含义很直接:

  • 自动补全和结果页应该基于同一份搜索事实;
  • 否则用户在下拉框里看到的内容,和点进搜索页看到的内容,会出现解释不一致。

所以 Odoo 的思路是:

先有统一搜索结果协议,再决定它是以下拉框还是列表页的形式消费。

这比“两个页面各写一套搜索逻辑”稳定得多。


七、这套设计对定制最重要的启发是什么

如果你想扩展 Odoo 网站搜索,最常见的误区有三个:

误区一:只改前端输入框

这样最多只是换 UI,真正搜什么、怎么渲染、能不能高亮,都没有解决。

误区二:在 controller 里硬塞自定义模型

短期能跑,但会把字段映射、图标、URL、摘要逻辑全写死,后面非常难维护。

误区三:盲目增强 fuzzy

搜索不是“召回越多越好”,而是错配越少越好。对 SKU、编码、短词来说,过强的 fuzzy 往往会适得其反。

正确方向通常是:

  • 在模型层补 _search_get_detail()
  • 明确 search_fieldsfetch_fields
  • 设计好 mapping
  • 只在合适字段上启用 match=True
  • 谨慎看待 fuzzy。

八、从产品体验角度,Odoo 网站搜索到底强在哪里

它不一定是“最聪明”的搜索引擎,但它非常像 Odoo 一贯的风格:

  • 不追求花哨算法堆料;
  • 重视统一协议;
  • 重视多模型可复用;
  • 重视前后台边界清晰;
  • 重视展示结果的一致性。

所以它更像一个“可持续扩展的网站搜索框架”,而不是一个只服务某个页面的小功能。

对于内容型官网、产品型官网、混合型站点来说,这种架构比一时看起来聪明的前端小技巧更值钱。


最后一句

理解 Odoo 网站搜索,重点不在于“模糊匹配怎么拼”,而在于看懂这条链路:

搜索细节声明 → 搜索执行 → 结果映射 → 高亮与格式化 → autocomplete 与结果页复用。

把这个主链路看懂后,你再去做站内搜索优化、接自定义模型、调结果展示,才不会越改越乱。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。