首页/新闻资讯/正文详情

Synapse 用户目录(User Directory)实现与搜索算法深度解析

发布时间:2026/9/23 21:44:49 来源:云帆数科 栏目:资讯中心
Synapse 用户目录(User Directory)实现与搜索算法深度解析
后端即时通讯【免费下载链接】synapseSynapse: Matrix homeserver written in Python/Twisted.项目地址https://gitcode.com/gh_mirrors/sy/synapse点击查看免费下载用户目录User Directory是 Matrix 联邦网络中找人的核心功能本端服务器维护一份由对本服务器可见的用户本地用户 与本地用户共享房间的远端用户组成的目录客户端通过POST /_synapse/client/v3/user_directory/search即可按用户名或昵称搜索其他用户。本文以 SynapsePython/Twisted 实现的 Matrix homeserver为对象完整剖析用户目录的五张数据表模型、user_directory配置段全部选项、PostgreSQL 与 SQLite 两套全文检索评分算法以及目录失同步时如何通过后台更新任务regenerate_directory重建目录。读完本文你将能独立配置搜索行为、定位目录数据不一致问题并从源码层面理解 Synapse 的可见性边界与排序策略。用户目录的可见性模型Synapse 维护用户目录的依据是对本 homeserver 可见visible的用户——包括两类本地用户注册在本服务器上的用户。远端用户与本服务器某个本地用户共享同一房间的联邦用户。目录中记录的信息是公开可见信息用户 ID、显示名、头像。因此一个关键约束是每个用户在目录中只能有一条条目且该条目只能包含公开信息否则会泄漏用户在私密房间中使用的个性化昵称或头像对应源码中的user_directory表one directory entry per user设计见 synapse/storage/databases/main/user_directory.py。被排除的三类本地用户在填充目录时以下三类本地用户会被统一排除实现见 should_include_local_user_in_dir 及批量版本_filter_local_users_for_dir_txnsupport 用户user_type support用于诊断不应出现在目录中appservice 用户包括 appservice 的 sender 用户由注册文件中sender_localpart定义以及匹配 appservice 命名空间正则的所有用户已停用deactivated用户不可联系不应被检索到。注意远端用户不受上述规则限制——服务器无法对远端用户应用这些特殊规则因此远端用户只要与本服务器有共享房间即可进入目录见 synapse/handlers/user_directory.py 中对本地/远端用户的差异化处理。数据模型构成用户目录的五张表原文档指出五张表共同构成用户目录。其中三张表追踪所有已知用户另外两张合称搜索表追踪用户之间的可见关系。当前仓库的主 schemasynapse/storage/schema/main/full_schemas/72/full.sql.postgres中可看到它们的真实结构1.user_directory——目录主表CREATE TABLE user_directory ( user_id text NOT NULL, room_id text, display_name text, avatar_url text );每个用户仅一条记录存放用户 ID、显示名、头像因为一用户一条所以必须只存公开可见信息按房间room和用户user建有索引。2.user_directory_search——全文检索表CREATE TABLE user_directory_search ( user_id text NOT NULL, vector tsvector -- PostgreSQL 版本SQLite 版本为 value 列 );与user_directory通过user_id连接额外的一列用于对用户 ID 和显示名做全文检索PostgreSQL 与 SQLite 使用不同的 schemaPostgres 使用tsvector列SQLite 使用 FTS 的value列见 synapse/storage/databases/main/user_directory.py 中两种引擎各自的写入分支基于全文检索数据建立索引并索引用户。3.user_directory_stream_pos——增量更新水位线CREATE TABLE user_directory_stream_pos ( lock character(1) DEFAULT X::bpchar NOT NULL, stream_id bigint, CONSTRAINT user_directory_stream_pos_lock_check CHECK ((lock X::bpchar)) );当填充目录的初始后台更新完成后这里记录一个 stream 位置该位置表明 Synapse 现在应当监听房间变更并增量更新目录增量处理逻辑见 synapse/handlers/user_directory.py_unsafe_process从该位置开始消费current_state_deltas流处理房间可见性变化与成员事件处理完推进水位并写入 update_user_directory_stream_pos。更多 stream 背景可参考 streams 架构文档。4.users_in_public_rooms——公共房间成员表CREATE TABLE users_in_public_rooms ( user_id text NOT NULL, room_id text NOT NULL );记录用户 ↔ 其所在公共房间的关联用于判定哪些用户处于公共房间、应在目录中公开可见本地与远端用户都被追踪。5.users_who_share_private_rooms——私密房间共享表CREATE TABLE users_who_share_private_rooms ( user_id text NOT NULL, other_user_id text NOT NULL, room_id text NOT NULL );每一行是一个三元组(L, M, room_id)L是本地用户M是本地或远端用户L与M应当不同但 schema 层面并未用约束强制注意如果两个本地用户共享一个房间会存在两条记录(user1, user2, !room_id)和(user2, user1, !room_id)生成逻辑见 synapse/handlers/user_directory.py。表间关系小结表作用被谁使用user_directory每个用户的公开 ID/昵称/头像搜索结果 JOINuser_directory_search全文检索向量/值搜索匹配user_directory_stream_pos增量更新游标事件流处理users_in_public_rooms谁在公共房间可见性判定users_who_share_private_rooms谁与本地用户共享私密房间可见性判定配置选项user_directory配置段搜索行为可通过服务器级配置user_directory段调整。完整的 YAML 说明见 config_documentation.md参数解析实现在 synapse/config/user_directory.py。配置项类型默认值说明enabledbooltrue是否允许用户搜索目录。设为false时所有查询返回空响应。search_all_usersboolfalse是否搜索服务器已知的全部用户。见下方可见性过滤两档行为。prefer_local_usersboolfalse搜索时是否优先展示本地用户。show_locked_usersboolfalse是否在搜索结果中展示被锁定locked的用户。示例配置user_directory: enabled: false search_all_users: true prefer_local_users: true show_locked_users: truesearch_all_users的升级提醒原文档 配置文档共同强调如果你将此选项设为true而上次用户目录搜索索引被重建发生在 Synapse 1.44 之前那么必须重建索引才能搜索到所有已知用户。索引在 Synapse 首次启动时构建管理员也可以按 后台更新管理 API 的说明手动触发重建。与用户目录相关的另一个 worker 配置是update_user_directory_from_worker指定由哪个 worker 进程负责增量更新用户目录见 config_documentation.md。在 synapse/handlers/user_directory.py 中可以看到只有当该 worker 被配置为更新目录时才会注册notify_new_event复制回调并在启动时立即触发一次处理。搜索算法当search_all_users为false时搜索结果被限制为满足以下条件之一的用户出现在users_in_public_rooms表中或出现在users_who_share_private_rooms表中其中L是发起搜索的用户M是搜索结果。当search_all_users为true时不加上述限制服务器已知的所有匹配用户都会被返回。默认情况下被锁定locked的用户不会出现在结果中若show_locked_users为true则不再对用户的锁定状态做过滤。这段可见性过滤与锁定过滤的 SQL 拼接逻辑可参见 search_user_dir。搜索词预处理用户提供的搜索词在进入查询之前会经历两步规范化实现见 _filter_text_for_index小写化使搜索大小写不敏感。这一步对 Postgres 与 SQLite 都必要——Postgres 的to_tsquery/to_tsvector在使用C排序规则时不会对非 ASCII 字符小写化而 SQLite 根本不会小写化非 ASCII 字符。NFKC 规范化Unicode 规范化形式之一将文本视为大小写不敏感把同一文本的不同书写形式归一化并将大致等价的字符映射到一起。例如组合形式[e, ◌́]映射为[é][dž]映射为[d, ž][①]映射为[1][i⁹]映射为[i, 9]。需要说明当前实现没有做重音不敏感处理。源码注释指出若想实现重音不敏感可改用 NFKD 规范化并过滤组合重音字符unicodedata.combining但代价是显式带重音的搜索词会匹配到不带重音甚至完全不同的字符可能产生更多噪音结果。分词Word Splitting规范化后的搜索词会被切分为单词若系统装有ICUInternational Components for Unicode则使用系统的默认 locale的BreakIterator进行分词见 _parse_words_with_icu。ICU 的安装方法见 安装指南。若没有 ICU则回退到正则[\w\-]Unicode 模式将 ASCII 字符、数字、下划线和连字符的连续序列视为单词见 _parse_words_with_regex。该方案对大多数拉丁语系语言足够好但对其他语言效果不佳。分词能力检测在模块顶部完成import icu成功则USE_ICU True否则为False见 synapse/storage/databases/main/user_directory.py。总体排序目标无论哪种数据库查询的总体目标是找到匹配用户并优先展示真实用户例如非 bot、非停用用户因为可以假设真实用户会设置显示名和头像。排序时有 ID、有显示名、有头像的用户得分更高ts_rank_cd返回 0~1 的权重所有结果的初始权重为 1。PostgreSQL 查询与评分分词结果被转换为两个查询exact精确使用to_tsquery精确匹配解析出的词prefix前缀使用to_tsquery按前缀匹配解析出的词word:*形式。实际查询将两者合并to_tsquery(simple, ...)配合、|、:*构造见 _parse_query_postgres并从user_directory_search表向量与to_tsquery匹配先 LIMIT 10000中取行。结果按加权分数降序排列权重因子如下4×如果用户 ID 存在1.2×如果用户设置了显示名1.2×如果用户设置了头像0~3×基于ts_rank_cd对exact查询的全文检索评分其四个权重变量为D0.1 —— 用户 ID 的域名domainC0.1 —— 未使用B0.9 —— 用户的显示名未设置则为空字符串A0.1 —— 用户 ID 的 localpart本地部分0~1×基于ts_rank_cd对prefix查询的全文检索评分权重同上若prefer_local_users为true则2×如果该用户是本服务器的本地用户。以上权重在 SQL 中的实际体现见 search_user_dir 的 Postgres 分支CASE WHEN d.user_id IS NOT NULL THEN 4.0 ...连乘3 * ts_rank_cd({0.1, 0.1, 0.9, 1.0}, ...) ts_rank_cd(...)对应 exact 与 prefix 两项prefer_local_users则通过CASE WHEN user_id LIKE ? THEN 2.0 ELSE 1.0 END?绑定为%:本地服务器名实现。索引写入侧也与之对应Postgres 插入user_directory_search时使用setweight将 localpart 标为A、域名标为D、显示名标为B见 synapse/storage/databases/main/user_directory.py即localpart 权重最高其次是显示名最后是服务器名。SQLite 查询与排序SQLite 分支search_user_dir 的 SQLite 部分从user_directory_search中取匹配行按以下信息排序后续列作为平局决胜tiebreaker使用matchinfo函数的全文检索rankrank 高者在前若prefer_local_users为true则本服务器本地用户在前设置了显示名的用户在前设置了头像的用户在前。SQLite 的查询构造见 _parse_query_sqlite每个词生成(词* OR 词)并用连接即同时加入前缀与非前缀匹配项使精确匹配排序更高。搜索结果的后续处理handler 层search_users在拿到存储层结果后还会调用垃圾信息检查回调check_username_for_spam过滤掉被判定为 spam 的用户最后返回形如{limited: bool, results: [{user_id, display_name, avatar_url}]}的结果。客户端接口 UserDirectorySearchRestServlet 的细节值得注意路径为POST /_synapse/client/v3/user_directory/search不允许 guest 访问allow_guestFalseenabled: false时直接返回{limited: False, results: []}limit参数默认 10被夹在0到50之间max(min(limit, 50), 0)search_term为必填字段缺失返回 400 错误。管理员维护目录失同步与重建五张表之间可能偶尔出现不一致这被认为是 bug。如果发生当前推荐的修复方式是使用 管理后台更新 API 执行regenerate_directory任务它会启动一个后台任务清空现有表并重建目录。重建耗时取决于 homeserver 的规模用户与房间数量可能需要较长时间。API 调用形式见 background_updates.mdPOST /_synapse/admin/v1/background_updates/start_job请求体{ job_name: regenerate_directory }服务端实现位于 synapse/rest/admin/background_updates.pyregenerate_directory实际上会按依赖顺序注册四个后台更新任务populate_user_directory_createtables——建立临时暂存区_temp_populate_user_directory_rooms/users/positionpopulate_user_directory_process_rooms——重扫所有房间更新公共房间用户、私密房间共享关系与目录条目populate_user_directory_process_users——将所有本地用户及其 profile从profiles表读取避免泄漏按房间区分的私有 profile写入目录populate_user_directory_cleanup——更新user_directory_stream_pos水位并清理临时表。这四步的完整实现见 synapse/storage/databases/main/user_directory.py注册与 L114-L469各阶段逻辑。其中process_rooms阶段按事件数倒序每次最多取 250 个房间、以batch_size个状态事件为一批处理并维护progress[remaining]计数用于进度展示process_users阶段在 Postgres 上通过DELETE ... RETURNING带ORDER BY user_id LIMIT ?强制走索引批量取出待处理用户。若任务已在队列中API 会返回 400IntegrityError被捕获并转换为 Job ... is already in queue of background updates.。此外同一文件 还提供GET/POST /_synapse/admin/v1/background_updates/enabled临时启用/停用后台更新与GET /_synapse/admin/v1/background_updates/status查看当前更新进度。源码视角目录如何被持续维护UserDirectoryHandlersynapse/handlers/user_directory.py承担三项职责将/user_directory/search请求转发给存储层提供应用层钩子本地用户被创建/删除或 profile 变化时调用如 handle_local_profile_change 与 handle_local_user_deactivated监听房间状态变化远端用户加入/离开房间、profile 变化时更新目录_handle_deltas 处理RoomHistoryVisibility、JoinRules、Member三类事件。从源码结构可以推断出几个关键设计决策公共性判定is_room_world_readable_or_publicly_joinable只读取m.room.join_rules值为public与m.room.history_visibility值为world_readable两类状态事件见 synapse/storage/databases/main/user_directory.py。私密房间不采集远端 profile对于私有房间中的远端成员事件Synapse 不会直接更新目录 profile而是将用户标记为可能过期stale延迟 60 秒USER_DIRECTORY_STALE_REFRESH_TIME_MS后通过联邦/profile请求刷新见 synapse/handlers/user_directory.py 与user_directory_stale_remote_users表schema 增量见 synapse/storage/schema/main/delta/74/01_user_directory_stale_remote_users.sql因为私有房间的 profile 不保证与用户全局 profile 一致。刷新失败会按指数退避重试1 分钟、5 分钟、25 分钟、2 小时、10 小时、52 小时、10 天、7.75 周calculate_time_of_next_retry每次最多同时处理 5 个远端服务器、每 15 秒追加synapse/handlers/user_directory.py。离开房间的清理当服务器整体离开某个房间时会取出所有因该房间而进入目录的用户并逐个评估是否移除_handle_remove_user远端用户若不再与本服务器共享任何房间则从目录删除。测试验证仓库的测试 tests/storage/test_user_directory.py 覆盖了上述绝大部分行为可作为理解与验证的入口test_population_excludes_support_user/test_population_excludes_deactivated_user/test_population_excludes_appservice_user/test_population_excludes_appservice_sender验证三类被排除用户test_population_conceals_private_nickname验证私密房间中的个性化昵称不会被泄漏进目录test_search_user_dir/test_search_user_dir_all_users/test_search_user_limit_correct验证可见性过滤与 limit 行为test_search_user_dir_stop_words/test_search_user_dir_start_of_user_id验证停用词与用户 ID 前缀匹配test_search_user_dir_ascii_case_insensitivity/test_search_user_dir_unicode_case_insensitivity/test_search_user_dir_dotted_dotless_i_case_insensitivity验证大小写不敏感含点状 i/无点 ı 这类 Unicode 边界情形test_search_user_dir_unicode_normalization验证 NFKC 规范化test_search_user_dir_accent_insensitivity验证当前实现不做重音不敏感对应源码中的明确注释test_icu_word_boundary/test_icu_word_boundary_punctuation/test_regex_word_boundary_punctuation验证 ICU 与正则两套分词路径。小结Synapse 的用户目录是一个可见性驱动、增量维护、双引擎全文检索的系统五张表分别承载公开 profile、检索向量、增量游标与两种可见性关系user_directory配置段用四个布尔开关控制搜索开关、全量搜索、本地优先与锁定用户展示PostgreSQL 依赖tsvector/to_tsquery/ts_rank_cd的加权评分SQLite 依赖 FTSmatchinforank 加多级 tiebreaker当数据失同步时管理员可通过后台更新 API 的regenerate_directory任务按四个阶段重建整个目录。理解这些机制既能帮助你正确配置搜索体验也能在出现搜不到人排序不符合预期等问题时快速定位到具体的表、配置项与代码路径。赞分享后端即时通讯【免费下载链接】synapseSynapse: Matrix homeserver written in Python/Twisted.项目地址https://gitcode.com/gh_mirrors/sy/synapse点击查看免费下载相关推荐ta4j指标系统深度剖析从简单移动平均到复杂艾略特波浪分析ta4j指标系统深度剖析从简单移动平均到复杂艾略特波浪分析 ta4j是一个强大的Java技术分析库提供了从基础到高级的完整指标系统帮助开发者构建专业的交易金融科技MiniSearch模糊搜索算法深度解析Levenshtein距离实现MiniSearch模糊搜索算法深度解析Levenshtein距离实现 MiniSearch是一个轻量级且功能强大的JavaScript全文搜索引擎专为浏览搜索引擎深度优先搜索DFS算法详解 - 图解与Python实现深度优先搜索DFS算法详解 图解与Python实现 深度优先搜索Depth First Search简称DFS是图论中最基础也是最重要的算法之一。本文教程文档知识库上一篇t3code 内嵌 Effect 源码解析:Graph 集合运算 API(Graph.make、compose、intersection、difference、symmetricDifference)下一篇终极PCB设计可视化工具pcb-tools完整应用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

eSIM全面落地:从开通实操到双卡双eSIM的取舍指南
eSIM全面落地:从开通实操到双卡双eSIM的取舍指南

eSIM这个词,过去几年在数码圈里一直属于“狼来了”的状态——每年都说要普及,每年都只闻楼梯响。直到最近,移动、联通、电信三家运营商陆续在更多省市开放了eSIM的办理通道,尤其是手机端的独立eSIM业务开始真正落地,我… · 2026/9/23 21:44:49

海康工业相机帧率不达标?从MVS参数到链路带宽的完整排查指南
海康工业相机帧率不达标?从MVS参数到链路带宽的完整排查指南

很多做机器视觉项目的人,第一次接触海康工业相机时,都会碰到一个让人抓狂的问题:明明相机标称最大帧率是几十甚至上百帧,在实际项目里怎么调都达不到这个值,MVS软件里显示的帧率总是差一截。有人怀疑是相机坏了&#x… · 2026/9/23 21:44:43

pi-web Web 界面架构解读:一条消息如何走进 .jsonl 会话文件
pi-web Web 界面架构解读:一条消息如何走进 .jsonl 会话文件

pi-web Web 界面架构解读:一条消息如何走进 .jsonl 会话文件 【免费下载链接】pi-web Web UI for the pi coding agent 项目地址: https://gitcode.com/GitHub_Trending/pi/pi-web 你在浏览器里敲下一句指令,pi-web 要做的第一件事不是调云端接口… · 2026/9/23 21:44:43

119、Agent的配置管理与动态化
119、Agent的配置管理与动态化

119、Agent的配置管理与动态化 那晚线上告警响得人头皮发麻。一个负责代码审查的Agent,突然开始对每一行 print 都提出“请使用日志框架”的整改意见,连测试文件都不放过。我拉出日志,发现它加载的规则版本号还停留在三天前——可我明明昨天才在配置中心把这条规则下架了。… · 2026/9/23 22:19:52

118、构建可扩展的Agent基础架构
118、构建可扩展的Agent基础架构

118、构建可扩展的Agent基础架构 那天晚上十一点,线上的Agent实例突然开始集体超时,日志里刷满了TooManyRequests,但我们的API配额明明还有余量。查了一整夜,最后发现根因不在模型服务,也不在业务代码,而在我们引以为傲的“灵活”的Agent调度层——每个请求进来都会动态… · 2026/9/23 22:19:52

120、Agent的可观测性:Metrics与Tracing
120、Agent的可观测性:Metrics与Tracing

120、Agent的可观测性:Metrics与Tracing 昨天下午我盯着监控面板,发现那个Agent在凌晨开始不断重试同一个工具调用,日志里全是“timeout”,可面板上延迟曲线却是一条直线。不是不报,时候未到——因为当时只打了log,没打metrics,也没trace。后来把请求路径串起来才发现,… · 2026/9/23 22:19:46

传统师承证哪家培训机构靠谱?从报名学习到考试拿证,报考全攻略
传统师承证哪家培训机构靠谱?从报名学习到考试拿证,报考全攻略

近两年,传统师承证的报考热度持续上升,想考的人不少,但绝大多数人卡在了同一个问题上:培训机构那么多,到底哪家靠谱?网上搜一圈,广告铺天盖地、说法互相矛盾,越看越不知道信谁。本文… · 2026/9/23 22:19:15

国内主流主数据管理平台推荐,2026年选型避坑指南
国内主流主数据管理平台推荐,2026年选型避坑指南

摘要 随着企业数智化转型步入深水区,主数据管理已从"锦上添花"变为"刚需基建"。数据编码不统一、一物多码、信息孤岛等问题持续困扰着集团型企业。本文聚焦2026年国内主数据管理平台市场,从技术架构、落地能力、行业适配等维度&… · 2026/9/23 22:19:09

人工智能训练工程师证哪家培训机构靠谱?从报名学习到考试拿证,报考全攻略
人工智能训练工程师证哪家培训机构靠谱?从报名学习到考试拿证,报考全攻略

近两年,人工智能训练工程师证的报考热度持续上升,想考的人不少,但绝大多数人卡在了同一个问题上:培训机构那么多,到底哪家靠谱?网上搜一圈,广告铺天盖地、说法互相矛盾,越看越不知道… · 2026/9/23 22:19:09

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码