Search 把 Bitzsoft.Integrations.Search 的检索端口、Lucene provider、平台权限和两张 owner-local 配置表组合为应用模块。它提供统一搜索、利冲检查、索引统计/目录、分词弱词管理以及基于已注册数据源的全量重建,但当前不是一条完整的数据索引平台:没有业务模块发布 SearchIndexChangedIntegrationEvent,Created/Updated consumer 只是日志 no-op。
1. 当前真实能力
已实现:
- Lucene provider,配置入口
Search:Provider与Search:LuceneBasePath; POST /api/search/unified,在调用者 TenantId 下查询任意 IndexKey;POST /api/search/conflict/check,固定使用conflict索引执行当事人匹配;- 目录、统计、重建、利冲记录四条 generated endpoint;
- 分词弱词增/删/查三条手写 endpoint;
- 全局
SearchIndexCatalog与SysSegmentConfig表; - Lucene/ElasticSearch/OpenSearch provider manifest,其中只有 Lucene 标记已实现;
search.index.changedconsumer 的 Deleted 分支;- API Shell 无数据库/搜索能力时的 unavailable 默认端口与生产显式覆盖。
尚未实现:业务文档生产者、Created/Updated Upsert、事件 inbox/版本门、利冲记录持久化、源资源授权复核、StoredFields allowlist/脱敏、Feature 运行时映射、完整权限一致性、索引 seed、健康/新鲜度指标、多副本一致性、灾备恢复和 Search 端到端测试。
2. 运行结构
Search 当前运行在 API 组合中。JobHost 只有 Infrastructure 项目引用,没有调用 AddBitzOrcasSearchPlatform,也没有重建 executor。默认 search-index 是相对进程目录;多 API 副本会拥有各自文件系统视图,CAP 负载均衡也不会自动复制 Lucene 文档。
3. 九条 HTTP 端点
| 方法与路由 | 当前作用 | 运行时权限后缀 |
|---|---|---|
POST /api/search/unified | 统一查询 | search.search.view |
POST /api/search/conflict/check | 利冲匹配 | search.conflict.search |
GET /api/search/conflict/records/{recordId} | 固定未就绪 | search.conflict.view |
GET /api/search/index/catalog | 全局目录 | search.index.view |
GET /api/search/index/{indexKey}/statistics | 租户索引统计 | search.index.view |
POST /api/search/index/{indexKey}/rebuild | 全量重建 | search.index.manage |
POST /api/search/segment/words | 新增全局弱词 | search.index.create |
DELETE /api/search/segment/words | 删除全局弱词 | search.index.delete |
GET /api/search/segment/words/{segmentType} | 列出全局弱词 | search.index.view |
治理目录只声明 search.search.view、search.conflict.search/view、search.index.manage/view。因此利冲 check 调用利冲用 search.conflict.search,弱词写用 search.index.create/delete,与目录 manage/view 存在 action suffix 漂移。详见 HTTP 契约、权限与 Feature。
4. 第一次统一搜索
下面的应用内调用在当前租户的 conflict 索引查询“海岚科技”。Handler 把 Keyword 同时放入 Name、Subject、Content 三个默认字段;它不验证 IndexKey、Skip/Take,也不检查调用者是否有权查看每个命中所属的案件/文档。完整 HTTP body 见端点契约。
var result = await mediator.Send( new UnifiedSearch.Query( IndexKey: "conflict", Keyword: "海岚科技", Skip: 0, Take: 20), cancellationToken);
// Provider、目录或查询错误必须进入统一错误映射,不能伪装成“没有命中”。if (result.IsFailure) return result.Error;
// 命中是索引投影,不是案件访问授权,也不是业务事实来源。return result.Value!;响应包含 BusinessId、BusinessType、MatchKeywords、HitLevel 和原样映射的 StoredFields。当前没有字段 allowlist;如果 producer 把敏感值存进 StoredFields,Search 会全部返回。
5. 利冲不是普通关键字搜索
利冲入口忽略客户端 ConflictCheckRequest.TenantId,正确地使用当前用户 TenantId。它把 Name、ForeignName、FormerName、CreditCode 组成 category/keyword map,并支持用 SourceBusinessId 排除当前案件。
但当前结果只是“索引有匹配”。模块没有:检查记录、审查人/时间、规则版本、人工确认、override、源案件授权复核、不可否认审计或索引 watermark。GetConflictRecord 永远返回 ConflictRecord.PendingCasesModule。商业交付不能把布尔 HasConflict 当成完整利冲工作流。
详见统一搜索、利冲与结果安全。
6. 增量索引并未闭环
SearchIndexChangedIntegrationEvent 定义 EventId、IndexKey、DocumentId、TenantId、EntityType、Action、SourceVersion 和 OccurredAt,但没有 [IntegrationTopic],仓库中没有构造/发布它的代码。
consumer 对 Created/Updated 只记录“缺少 IIndexDocumentProvider”,随后仍调用 NotifyIndexChangedAsync;异常也会被吞掉。Deleted 调 ISearchEngine.DeleteAsync,失败时重抛触发 CAP retry。SourceVersion 只传给 notifier,没有用于索引幂等或乱序判断。
详见事件索引、版本顺序与重建。
7. Provider、目录与分词配置
Lucene 是唯一已实现 provider;ElasticSearch/OpenSearch 配置会启动失败,不会回退。外部包 1.0.0-alpha.8 提供 Search 抽象和 Lucene 引擎;平台模块负责组合、目录/词库持久化与 API。
目录表是全局 catalog,没有 seed 生产者,GetAll 无稳定排序,未知 Status 会被恢复为 Active。分词词库同样全局;增删以精确字符串工作,唯一键是 (SegmentType,Content),而内存桶使用 OrdinalIgnoreCase,数据库 collation 与运行时集合可能不一致。Store 注册 Scoped,缓存也是实例级,并非跨请求/节点缓存。
8. 安全边界
已做对的部分:搜索和统计使用当前 TenantId,利冲不信任请求 TenantId,整个 /api/search group 要求认证和 user rate limit。
尚缺:
- IndexKey allowlist 与每场景授权;
- 命中级源资源授权/DataScope;
- StoredFields 列级 allowlist、脱敏和大小限制;
- 利冲人员数据的专用审计/保留;
- 全局目录与弱词变更的更高平台权限;
- Feature
search.index的运行时 entitlement; - 防止错误/恶意索引文档跨租户写入的 producer contract。
租户过滤只是第一层;索引中的 TenantId 若在写入时就错误,查询层无法自行修复。
9. 可观测性与恢复
目录有 DocumentCount、LastRebuiltAt、Version,但当前重建/统计链不更新目录。查询响应没有 index version、as-of、lag 或 producer watermark。也没有索引健康检查、损坏检测、磁盘阈值、commit lag、CAP backlog、reconciliation 或 shadow rebuild runbook。
Lucene 目录不应被普通数据库备份流程假定覆盖。生产必须明确索引是可重建缓存,还是需要独立备份;前者首先要求真正可工作的数据源和演练。
10. 现有测试证据
现有测试主要证明:一个 ListSegmentWords Handler 委托、两个内存 IEntitySet store 场景、owner-local 元数据、ORM 中立、host composition、provider manifest 与 unavailable 默认端口。
缺少:真实 Lucene 查询/Upsert/Delete、事件 byte contract、Created/Updated/Deleted 行为、权限 suffix、九条 endpoint、请求规则、StoredFields 安全、利冲业务规则、数据库 provider parity、索引损坏/多副本/重建/恢复和规模性能。
11. GA 红线
- 建立业务 owner→版本化事件/快照→Search 的文档生产契约;
- Created/Updated 真正 Upsert,Deleted、重复、乱序与 gap 有确定语义;
- 实现数据源、shadow rebuild、checkpoint、对账、切换与回滚;
- 统一
.check/.search、.manage/.create/.update/.delete; - 将
search.index接入运行时 Feature evaluator; - 加 IndexKey allowlist、命中级源授权和 StoredFields schema;
- 把利冲提升为有证据、审阅、override 和记录的工作流;
- 验证全局词库作用域、规范化、并发和跨节点失效;
- 明确多副本 Lucene 拓扑、持久卷、所有权和灾备;
- 补齐契约、端点、provider、恢复、安全与性能测试。
12. 源码导航
| 主题 | 入口 |
|---|---|
| 手写端点 | src/Hosts/BitzOrcas.Api/Endpoints/SearchEndpoints.cs |
| 查询与利冲 | src/Platform/Search/...Search.Application/Queries、Commands/CheckConflict |
| 权限/Feature | ...Search.Contracts/SearchPermissions.cs、SearchFeatures.cs |
| Lucene 组合 | ...Search.Infrastructure/SearchDependencyInjection.cs |
| provider manifest | ...Search.Infrastructure/Providers/SearchProviderManifest.cs |
| 增量 consumer | ...Search.Infrastructure/Events/SearchIndexEventHandler.cs |
| 目录/词库 | ...Search.Infrastructure/Persistence 与 Stores |
| 外部能力版本 | Directory.Packages.props 中 Bitzsoft.Integrations.Search* 1.0.0-alpha.8 |