去哪儿 IM 分析 - Search
前言
昨天「去哪儿」开源了自研的 IM Startalk,作为一个在 IM 领域划水了一段时间的人,也想了解下其他人是如何去考虑 IM 设计,所以就开始了源码阅读之旅。接下来会用几篇文章简单分析一下。
本篇分析的是 Search.
介绍
简单浏览了代码,可以看到 IM Search 是基于 Flask 开发的,而且是单例运行的,带有几个全局变量,甚至还运行在 debug 模式下。没有使用 gunicorn 等 Server。
qtalk_search 提供了搜索用户和搜索群组的功能。
搜索用户
搜索用户是通过一次很晦涩的 SQL 查询得出的,核心 SQL (原本不带分行的,格式化了一下...):
sql = "select aa.user_id,aa.department,aa.icon,aa.user_name,aa.mood from
(SELECT a.user_id, a.department, b.url AS icon, a.user_name, b.mood FROM host_users a
LEFT JOIN vcard_version b ON a.user_id = b.username
WHERE a.hire_flag = 1
AND LOWER(a.user_type) != 's'
AND (a.user_id ILIKE '%" + username + "%'
OR a.user_name ILIKE '%" + username + "%'
OR a.pinyin ILIKE '%" + username + "%')) aa
left join
(select case when m_from = '" + user_id + "' then m_to else m_from end as contact,
max(create_time) mx
from msg_history
where m_from = '" + user_id + "'
or m_to = '" + user_id + "'
group by contact)
bb on aa.user_id = bb.contact order by bb.mx desc nulls last limit " + str(limit) + " offset " + str(offset) + " ;"
通过字段名猜测能查出:
- 用户的 ID
- 用户的部门
- 用户的头像
- 用户名
- 用户的心情(或者是签名、状态)?
整个查询会联合 用户表、聊天历史搜索出来和当前用户有聊过天的人。
虽然代码洁癖被多次电疗已经能接受比较奇怪的风格,但是这段 SQL 还是不认直视,大小写不统一、alias 命名随意等问题。 除此之外,用了字符串拼接的方式构造出 SQL 而不是 passing parameters, 在其他地方也没有发现参数转义,也就表示存在 SQL 注入的可能。 然而不得不承认这段 SQL 功力强大,估计能写出来的人也不多。
搜索群组
搜索群组的流程和搜索用户的流程大致相同,这里就不累述,只贴一下 SQL
sql = "select a.muc_name, a.domain, b.show_name, b.muc_title, b.muc_pic
from user_register_mucs as a
left join muc_vcard_info as b
on concat(a.muc_name, '@', a.domain) = b.muc_name
where a.username = '" + user_id + "'
and (b.show_name ilike '%" + groupkey + "%'
or b.muc_name like '%" + groupkey + "%')
limit " + str(limit) + " offset " + str(offset) + ";"
搜索共同群组
sql = "SELECT A.muc_room_name, B.show_name, B.muc_title, B.muc_pic FROM (SELECT muc_room_name, MAX(create_time) as max FROM muc_room_history aa RIGHT JOIN (SELECT muc_name FROM user_register_mucs WHERE username = '" + user_id + "' AND registed_flag != 0 AND muc_name in (SELECT muc_name FROM user_register_mucs WHERE username IN (SELECT user_id FROM host_users WHERE hire_flag = 1 AND (user_id ~ any(array[" + key_str + "]) OR user_name ~ any(array[" + key_str + "]) OR pinyin ~ any(array[" + key_str + "]))) GROUP BY muc_name HAVING COUNT(*) = " + str(
key_count) + ")) bb ON aa.muc_room_name = bb.muc_name GROUP BY muc_room_name ORDER BY max DESC nulls last LIMIT " + str(
limit) + " OFFSET " + str(
offset) + ") A JOIN muc_vcard_info B ON (a.muc_room_name || '@"+conference_str+"') = b.muc_name;"
comments powered by Disqus