设计一个呆萌的「搜索建议 API」

最近在实现一个内部系统的搜索功能,我们这个内部系统是一个运维平台, 功能和 Google Cloud Engine 的控制台特别相似。

这个平台中,有一些概念

  • App 是基本单位
    • app 会有一些资源
      • 容器组,容器会有 IP
      • redis,会有一个 url
      • 域名
    • app 也会有一些子项目
      • 持续交付项目(项目会有名字)
    • 等等

之前,这个系统有个搜索接口,这个接口支持搜索应用。用户输入应用名的前几个字母, 搜索框下面会弹出一些候选项,然后用户点击候选项,就能进入对应的 App 页面。

我的任务是:让这个搜索框支持搜索 IP、域名。当用户输入 IP 时,输入框下弹出候选项, 候选项中包含了一些信息:IP 对应的是哪个 App、哪个容器,用户点击这个信息,就可以跳转到详情页面。

收到这个任务时,心里有个特别多的疑问:

  • 要不要用 ES 来实现,还是直接写 SQL 语句?
  • 要用 ES 的话,怎么用?
  • SQL 会不会有性能方面的问题?
  • 搜索接口的 query 参数设计和返回数据结构设计

在整个功能调研结束后,我也解决了最后一个问题。

由于自己对 ES 目前处于一窍不通的状态,和其它小伙伴简单讨论后, 我们认为使用 ES 来实现会遇到一些比较棘手的问题,决定先依赖 MySQL `like` 实现一个简单版本。 等之后有一定积累了,我们可以再考虑 ES 方案也不迟(希望自己真的会去学习 ES 吧)。

另外,还发现一个比较有趣的事情:

  • 「搜索建议」和「搜索」区别和联系

搜索建议和搜索

在上文和下文中,我们把功能描述为搜索,没有区分「搜索建议」等同于「搜索」。搜索建议功能一般用于用户输入补全。 它们典型的差别有这些:

  • 搜索是支持模糊匹配的,而自动补全更多的是前缀匹配/精确匹配
  • 搜索建议的结果会包含多个类别的对象
    • 举个栗子:在 twitter 搜索框输入文字的时候,触发搜索建议功能,返回结果包含多个类型:用户、tweet等
  • 搜索一般是指定类别进行搜索的
    • 举个栗子:在网易云音乐搜索框输入文字的时候,也是搜索建议功能,返回结果包括:歌曲、歌手、专辑。 而当用户按下 enter,会进入搜索页面,搜索页面会分多个 tab,用户每点击一个 tab,都会触发一次搜索

在 API 设计上,也会将两者区分开来,像知乎、网易云音乐、Google Cloud Engine, 它们的搜索建议接口名类似 /search/suggest ,搜索接口则是 /search

方案

经过简单的讨论,我们的方案是:搜索时解析用户输入,将搜索请求分发到各后端/各个数据表。 比如说:如果用户输入的是一个 IP 时,我们就去 IP 表(或者负责管理 IP 的系统中)查询。

方案整体思路

  1. 用户输入搜索关键词
  2. 后端解析关键词(通过规则匹配等手段),猜测用户搜索目标:猜测用户输入的是 IP,项目名或是应用名?
  3. 根据搜索目标,后端将搜索请求分发到各后端。
    • 举个栗子:如果用户是想搜索应用,后端会发送一个请求到 A 后端
    • 另外,如果不能明确用户的搜索目标,默认认为用户是想搜索应用(默认行为可以根据之后大家反馈进行微调)。
  4. 将搜索结果进行聚合,返回给前端。
    • 搜索结果会分类:应用、项目、持续交付项目、IP等,前端在展示上,也会进行区分

部分实现细节

搜索结果数据结构参考

Google Cloud
  • 一个大的 list
  • list 中的各个对象有类型字段
  • list 中各个对象也会有 rank 分
[
  {
    "rank": 10,
    "resource": {
      "type": App/Project/CD_Project/...,
      "id": globalId,
      "project_id": app_name/project_name/...,
      "display_name": "",
    }
  }
]
netease cloud music && twitter
  • 返回一个字典
  • 字典各个 key 是类型,值是一个列表
  • 字典单独有个字段 order,order 是个列表,将类型进行排序

返回结果种类和顺序根据 order 来定

{
  "app": [],
  "project": [],
  "cd_project": [],
  "container_ip": [],
  "vm_ip": [],
  "pm_ip": [],
  "vip": [],
  "order": ["app", "cd_project", "..."],
}
qq yinyue

和上面的差不多,相对嵌套更深。

{
  "app": {
    "count": 2,
    "name": "应用",
    "order": 2,
    "type": 3,
    "items": {
      AppObject,
      ...
    }
  }
}

目前决定使用第二种(Twitter 和 网易云音乐)数据结构。

搜索的 query 参数

几乎不需要 query 参数

MySQL like 语句

使用LIKE进行搜索匹配的时候,这样索引是有效的: select * from tbl1 where name like 'xxx%' , 而 like '%xxx%' 时索引无效,应该是和 B+ 树的特性。

like vs =

从以上资料,使用 like 'xxx%' 来查,在小的数据集上 应该 不会有太大的性能问题,待验证。

搜索建议功能的一些实现方法

小结

在这次调研中,自己调研了几个系统的「搜索建议」接口的设计,明确了「搜索建议接口」常见的输入输出数据结构。 另外,对搜索和搜索建议这两个功能的联系也有了更深的认识。

另外,也了解了一下 MySQL 的 like 相关知识。

Updated:

Comments