MeiliSearch 搜索
CmsKit 以 MeiliSearch 作为全文检索引擎,为文章与沸点提供检索、搜索建议、热搜、历史与高亮。它处在基础设施层,被 Content / Discovery 复用,本身不直接对外暴露业务接口。
为什么引入 MeiliSearch
社区内容的检索需要满足:全文相关性排序、关键词高亮、前缀/模糊建议、热搜榜。关系库的全文检索在中文分词与相关性排序上代价高,因此 CmsKit 把检索职责整体交给 MeiliSearch,业务库只保留「内容本体」。检索结果经 ISearchSuggestionService 包装成建议/热搜/历史/高亮等体验能力(见 发现)。
索引与内容的关系
文章索引按 ArticleCategory 拆分为多个索引(索引名 = articles + _ + 分类名);沸点使用单一索引 shortmsgs。
| 索引 UID | 对应内容 | 说明 |
|---|---|---|
articles_Normal | 普通文章(ArticleCategory.Normal) | 标题、正文、标签等可检索 |
articles_AiNews | AI 资讯(ArticleCategory.AiNews) | 同上 |
shortmsgs | 沸点 | 正文、话题、圈子相关字段可检索 |
同步机制:以「轮询增量」为主,发布瞬间直写为辅
内容写入索引有两条路径(均来自 CmsKitJobService,而非请求链路里的领域事件):
- 增量同步
IncrementalSyncMeiliSearchAsync:每轮扫描UpdateTime落在最近 5 分钟窗口、且「PrivacyType == Public+AuditStatus == Approved+!IsDeleted」的内容,逐条IndexOrUpdate*;软删除的内容则从索引移除。 - 定时发布直写:
ScheduledPublishAsync在到点发布文章时,直接IndexOrUpdateArticleAsync并发送系统通知——这条路径不走 5 分钟轮询,故定时发布的文章无检索延迟。
选择轮询而非事件驱动是刻意权衡:主写入链路不被搜索引擎拖慢,写入吞吐更稳;代价是新内容最多约 5 分钟才可被检索到(热点发布场景由路径 2 补偿)。后台还提供 InitAllArticlesAsync / InitAllShortMsgsAsync 做全量索引重建(通常由运维/初始化任务触发)。
核心服务
IArticleMeiliSearchService:IndexOrUpdateArticleAsync/DeleteArticleIndexAsync/BulkSyncArticlesAsync/GetQueryArticleListAsync(列表式检索)/SearchArticlesAsync(关键词检索)。IShortMsgMeiliSearchService:IndexOrUpdateShortMsgAsync/DeleteShortMsgIndexAsync/BulkSyncShortMsgsAsync/GetQueryShortMsgListAsync/SearchShortMsgsAsync/InitAllShortMsgsAsync。ISearchSuggestionService:搜索建议、热搜、历史、记录。IMeiliSearchConfigService:索引/配置相关管理(如InitializeIndexSettingsAsync)。- 底层
MeilisearchClient在CmsKitModuleStartup中注册,地址来自运行时设置。
配置(业务含义)
MeiliSearch 的地址与密钥不是 appsettings 顶层 MeiliSearch 节,而是由 ICmsKitRuntimeSettingProvider 从系统设置(数据库 Setting 表)读取:
| 系统设置键名 | 默认值 | 业务含义 |
|---|---|---|
MeiliSearch.Host | 空(回落 http://127.0.0.1:7700) | MeiliSearch 地址 |
MeiliSearch.ApiKey | 空 | Master / Search API Key |
源码中没有独立的
Enable开关;只要MeiliSearch.Host可达,检索即可用。地址为空时客户端回落到本地默认地址。生产环境建议用 Search-only Key 暴露给前端直连,通过 Identity 系统设置接口写入。
开发示例
// 关键词检索文章(按分类),注入 IArticleMeiliSearchService
var docs = await _articleMeiliSearchService.SearchArticlesAsync("cmskit", page: 1, pageSize: 20,
category: ArticleCategory.Normal);
// 检索沸点,注入 IShortMsgMeiliSearchService
var sms = await _shortMsgMeiliSearchService.SearchShortMsgsAsync("周末", page: 1, pageSize: 20);
// 后台全量重建索引
await _shortMsgMeiliSearchService.InitAllShortMsgsAsync();
部署建议
- 用 Docker 启动 MeiliSearch 并配置 Master Key;生产环境为前端直连再创建 Search-only Key。
- 内容发布/更新由增量作业自动维护索引;如需全量重建,调用
InitAll*Async或批量BulkSync*Async(通常由运维/后台任务触发)。 - 监控索引大小与查询耗时,定期用 MeiliSearch 快照备份。