设计一个呆萌的「搜索建议 API」
最近在实现一个内部系统的搜索功能,我们这个内部系统是一个运维平台, 功能和 Google Cloud Engine 的控制台特别相似。
这个平台中,有一些概念
- App 是基本单位
- app 会有一些资源
- 容器组,容器会有 IP
- redis,会有一个 url
- 域名
- app 也会有一些子项目
- 持续交付项目(项目会有名字)
- 等等
- 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 的系统中)查询。
方案整体思路
- 用户输入搜索关键词
- 后端解析关键词(通过规则匹配等手段),猜测用户搜索目标:猜测用户输入的是 IP,项目名或是应用名?
- 根据搜索目标,后端将搜索请求分发到各后端。
- 举个栗子:如果用户是想搜索应用,后端会发送一个请求到 A 后端
- 另外,如果不能明确用户的搜索目标,默认认为用户是想搜索应用(默认行为可以根据之后大家反馈进行微调)。
- 将搜索结果进行聚合,返回给前端。
- 搜索结果会分类:应用、项目、持续交付项目、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 =
- https://stackoverflow.com/questions/6142235/sql-like-vs-performance
- https://stackoverflow.com/questions/543580/equals-vs-like 感悟:可以看源码,=。=
从以上资料,使用 like 'xxx%'
来查,在小的数据集上 应该 不会有太大的性能问题,待验证。
搜索建议功能的一些实现方法
- https://stackoverflow.com/a/11419105
- 创建一个表,在这个表里存一些关键字,然后用 like 来作查询,在一定场景是适用的。
小结
在这次调研中,自己调研了几个系统的「搜索建议」接口的设计,明确了「搜索建议接口」常见的输入输出数据结构。 另外,对搜索和搜索建议这两个功能的联系也有了更深的认识。
另外,也了解了一下 MySQL 的 like 相关知识。
Comments