Huly Fulltext 服务深入指南:全文索引、Elasticsearch 集成与 Workspace 重建索引实战 📅 发布时间:2026/9/12 14:53:36 👁 浏览次数: Huly Fulltext 服务深入指南全文索引、Elasticsearch 集成与 Workspace 重建索引实战【免费下载链接】platformHuly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)项目地址: https://gitcode.com/GitHub_Trending/platform80/platformHulyPlatform的 Fulltext 服务是负责全文索引的核心微服务它通过 Elasticsearch 为文档、附件与通信消息提供全文检索能力并与 Rekoni 服务协作完成多种文件类型的内容抽取。本文以 pods/fulltext/README.md 为骨架结合 pods/fulltext/src 下的源码实现完整讲解环境变量、API 端点、重建索引操作与底层队列架构帮助你在自建部署中正确配置、排障并二次开发该服务。一、服务定位Fulltext 在 Huly 架构中的角色Fulltext 服务是 Huly 平台中独立的索引微服务对应代码仓库位于 pods/fulltext官方 README 对其定位描述为Fulltext indexing service for the Platform. Provides full-text search capabilities by indexing documents and communication messages.它的核心职责可拆解为三个层面全文索引持续消费数据变更事务Tx将文档、附件等实体写入 Elasticsearch 索引内容抽取通过 Rekoni 服务默认http://localhost:4004把 PDF、DOCX 等各类文件解析为可检索的文本内容检索服务对外暴露一组 HTTP API供前端与上层服务执行全文搜索、定向索引与重建索引。从入口文件 pods/fulltext/src/index.ts 可以看到服务启动时依次完成加载模型文件MODEL_JSON默认model.json→ 校验SERVER_SECRET→ 初始化监控与日志基于 OpenTelemetry→ 校验DB_URL/FULLTEXT_DB_URL/REKONI_URL/ELASTIC_INDEX_NAME/ACCOUNTS_URL→ 组装索引适配器 → 拉起 Kafka 平台队列 → 调用startIndexer()启动 HTTP 服务。任何一个必需环境变量缺失都会导致进程直接退出因此启动前的环境变量配置是全流程的第一道门槛。二、环境变量全解析必需项与可选项原文档将环境变量分为 Required 与 Optional 两组这里结合 pods/fulltext/src/index.ts 与 pods/fulltext/run.sh 中的本地示例值整理成可直接对照的表格。必需环境变量Required变量名用途本地示例值SERVER_SECRET服务间认证的密钥用于签发与校验 workspace tokensecretDB_URL业务数据库连接串支持 PostgreSQL 或 MongoDBmongodb://localhost:27017FULLTEXT_DB_URLElasticsearch 连接地址http://localhost:9200REKONI_URLRekoni 内容抽取服务地址http://localhost:4004ELASTIC_INDEX_NAME使用的 Elasticsearch 索引名local_storage_indexACCOUNTS_URLAccount 服务地址用于获取 workspace 信息与事务端点http://localhost:3000关于DB_URL的两种数据库支持在 pods/fulltext/src/server.ts 中有明确实现证据服务通过registerTxAdapterFactory/registerAdapterFactory/registerDestroyFactory同时注册了mongodb与postgresql两套适配器其中 PostgreSQL 被标记为默认优先setAdapterSecurity(postgresql, true)而run.sh中则给出了 MongoDB 的本地开发示例。server.ts中setDBExtraOptions({ prepare: usePrepare })还负责把DB_PREPARE透传给数据库适配层。可选环境变量Optional变量名默认值说明PORT4700服务 HTTP 监听端口源码通过parseInt(process.env.PORT ?? 4700)读取MODEL_JSONmodel.json模型 JSON 文件路径启动时readFileSync加载为Tx[]数组HULYLAKE_URL空字符串Hulylake 服务地址用于通信消息索引仅当COMMUNICATION_API_ENABLEDtrue时必需COMMUNICATION_API_ENABLED关闭设为true时启用对卡片通信消息的索引关闭时即使配置了HULYLAKE_URL也会跳过通信索引避免 Hulylake 不可用时报错ENABLE_CONSOLEtrue是否启用控制台日志输出VERSION0.7.0服务版本号用于监控与遥测打点DB_PREPAREtrue是否启用数据库预处理语句prepared statementsSTORAGE_CONFIG无外部存储配置如 MinIO用于读取文档附件原始内容格式参考存储服务文档STORAGE_CONFIG的读取逻辑在入口处通过storageConfigFromEnv()完成随后buildStorageFromConfig(storageConfig)构建出外部存储适配器最终传入索引流水线用于在抽取内容时定位并读取附件文件。一个可直接运行的本地配置样例pods/fulltext/run.sh 给出了完整的最小本地启动配置export FULLTEXT_DB_URLhttp://localhost:9200 export DB_URLmongodb://localhost:27017 # DB_URL: postgresql://postgres:examplelocalhost:5432, export STORAGE_CONFIGminio|localhost?accessKeyminioadminsecretKeyminioadmin export SERVER_SECRETsecret export REKONI_URLhttp://localhost:4004 export MODEL_JSON./bundle/model.json export ELASTIC_INDEX_NAMElocal_storage_index export STATS_URLhttp://huly.local:4900 export ACCOUNTS_URLhttp://localhost:3000 rushx bundle node --inspect ./bundle/bundle.js注意run.sh中还需要先执行rushx bundle内部调用rushx get-model见 pods/fulltext/package.json 的bundle/get-model脚本通过 esbuild 打包并用node ./bundle/bundle.js ./bundle/model.json生成模型文件然后才启动服务。三、容器化部署与端口约定pods/fulltext/Dockerfile 展示了生产镜像的构建方式基于hardcoreeng/base-slim基础镜像将打包产物bundle/bundle.js、bundle.js.map与bundle/model.json拷贝进镜像固定暴露 4700 端口并以CMD [ node, --expose-gc, bundle.js ]启动——--expose-gc用于让 Elasticsearch 索引写入与内存回收更可控。镜像脚本同样定义在 pods/fulltext/package.json 中rushx docker:build构建镜像hardcoreeng/fulltextrushx docker:abuild/rushx docker:tbuild分别为linux/arm64与linux/amd64平台构建并推送rushx docker:staging/rushx docker:push打 staging tag 或正式 tag 并推送。四、HTTP API 端点详解服务基于 Koa koa-router 提供 5 个端点全部为PUT方法路由定义集中在 pods/fulltext/src/server.ts。所有端点都支持两种鉴权方式请求体中的token字段或Authorization: Bearer token请求头服务端通过decodeToken(token)校验并解出workspace。方法路径功能请求体字段PUT/api/v1/search按类检索文档token,_classes,query,fullTextLimitPUT/api/v1/full-text-search全文搜索token,query,optionsPUT/api/v1/index-documents触发指定文档索引token,requests[]_class_idPUT/api/v1/reindex重建 workspace 索引token,onlyDropPUT/api/v1/close关闭 workspace 索引器token1./api/v1/search文档检索请求体携带_classes要检索的类引用数组、queryDocumentQueryDoc文档查询与fullTextLimit服务端调用manager.fulltextAdapter.search()在 Elasticsearch 上执行结构化检索响应体直接返回搜索结果。值得注意的是这里的token在 pods/fulltext/src/server.ts 中被显式decodeToken(token)校验即使请求体没有 token 也会从Authorization头解析。2./api/v1/full-text-search全文搜索请求体包含querySearchQuery与optionsSearchOptions服务端调用searchFulltext()来自hcengineering/server-indexer包结合系统层级sysHierarchy与全文适配器完成语义化的全文检索。3./api/v1/index-documents定向索引请求体为{ token, requests: [{ _class, _id }] }用于对指定文档执行索引。实现上通过manager.withIndexer()获取对应 workspace 的索引器并刷新其lastUpdate时间戳源码注释表明这是“just to be safe”的保活行为后续实际的索引流程由索引器的 Tx 流水线负责。4./api/v1/reindex重建索引详见下一节5./api/v1/close关闭 workspace 索引器收到请求后调用manager.closeWorkspace(decoded.workspace)将对应 workspace 的索引器从内存中移除并释放连接。这一端点通常配合生命周期管理使用例如 workspace 升级或删除时关闭其索引上下文。所有端点出错时统一返回404并结束响应错误信息通过Analytics.handleError上报。五、重建索引Reindex实战Dev Tool 与直接 API 调用原文档提供了两种重建索引方式其中Dev Tool 是官方推荐做法。方式一使用 Dev Tool推荐Dev Tool 通过队列系统发送重建事件不直接依赖 Fulltext 服务本身的可用性因此更可靠。命令定义在 dev/tool/src/index.ts# 设置 FULLTEXT_URL 环境变量 export FULLTEXT_URLhttp://localhost:4700 # 重建指定 workspace rushx tool fulltext-reindex workspace-name该命令的执行逻辑是从账户数据库查找 workspace → 校验存在 → 通过getPlatformQueue(tool, ws.region)获取该 region 的平台队列 → 向QueueTopic.Workspace主题发送workspaceEvents.fullReindex()事件 → 关闭队列。也就是说它先把重建请求投入消息队列由 Fulltext 服务的队列消费者异步处理这正是推荐它的原因。此外工具还提供批量版本fulltext-reindex-alldev/tool/src/index.ts会遍历所有mode: active且未禁用的 workspace按最近访问时间lastVisit倒序逐个发送重建事件export FULLTEXT_URLhttp://localhost:4700 rushx tool fulltext-reindex-all方式二直接调用 HTTP API端点PUT http://fulltext-service-url/api/v1/reindex请求体形式{ token: workspace-token, onlyDrop: false }或使用 Authorization 头curl -X PUT http://localhost:4700/api/v1/reindex \ -H Authorization: Bearer workspace-token \ -H Content-Type: application/json \ -d {onlyDrop: false}参数说明token必需携带 workspace 信息的有效令牌可通过请求体或Authorization: Bearer token头提供onlyDrop可选为true时仅清空索引而不重建默认false。关于 token 的关键限制原文档明确强调token 必须使用workspace UUID生成并且用与 Fulltext 服务相同的SERVER_SECRET签名否则服务端decodeToken会校验失败。reindex 端点的服务端行为在 pods/fulltext/src/server.ts 中/api/v1/reindex的处理逻辑为await manager.withIndexer(ctx, decoded.workspace, token, true, async (indexer) { indexer.lastUpdate Date.now() if (request?.onlyDrop ?? false) { await manager.fulltextProducer.send(ctx, decoded.workspace, [workspaceEvents.clearIndex()]) } else { await manager.fulltextProducer.send(ctx, decoded.workspace, [workspaceEvents.fullReindex()]) } })即先按需创建/复用 workspace 索引器然后根据onlyDrop向QueueTopic.Fulltext主题发送clearIndex()或fullReindex()事件。真正的重建动作发生在队列消费者侧见第六节。六、底层架构队列驱动的索引流水线从源码结构看Fulltext 服务的核心抽象分三层WorkspaceManager生命周期与队列消费→ WorkspaceIndexer单 workspace 索引上下文→ FullTextIndexPipeline真正的索引流水线。6.1 WorkspaceManager队列消费者与事件路由pods/fulltext/src/manager.ts 中的WorkspaceManager在startIndexer()中创建了三个队列消费者消费者订阅主题职责workspaceConsumerQueueTopic.Workspace处理 workspace 生命周期事件创建/恢复/删除/归档/升级/重建fulltextConsumerQueueTopic.Fulltext处理全量重建与定向重建事件txConsumerQueueTopic.Tx消费数据变更事务并喂给索引流水线关键行为包括workspace 事件路由收到Created/Restored/FullReindex时向 Fulltext 主题转发fullReindex()收到Deleted/Archived/ClearIndex时先通过 Account 服务ACCOUNTS_URL查询 workspace 信息再调用fulltextAdapter.clean()清理 Elasticsearch 中的索引数据收到Upgraded时关闭对应索引器等待版本对齐。恢复保护workspace 处于Restoring状态时withIndexer会阻塞等待、跳过全量重建避免恢复期间写入脏数据。版本校验createIndexer会比对 workspace 的模型版本major/minor/patch与 Fulltext 支持的版本不匹配时最多重试 4 次每次间隔 10 秒超过后抛出Workspace limit reached。死信队列Tx 处理失败时消息会被转发到getDeadletterTopic(QueueTopic.Tx)对应的死信主题避免数据永久丢失。索引器自动回收每隔 5 分钟扫描一次超过 5 分钟未更新的索引器会被close()释放。6.2 WorkspaceIndexer单 workspace 的索引上下文pods/fulltext/src/workspace.ts 的WorkspaceIndexer为每个 workspace 维护独立的Pipeline与FullTextIndexPipeline。创建流程为基于getConfig()解析DB_URL对应的数据库适配配置disableTriggers: true并注入外部存储组装中间件链LowLevelMiddleware → ContextNameMiddleware → DomainFindMiddleware → DBAdapterInitMiddleware → ModelMiddleware → DBAdapterMiddleware通过createPipeline()构建数据访问管道建立 Hierarchy 与 ModelDb生成系统级 token解析 transactor 端点供索引进度广播IndexingUpdateEvent使用实例化FullTextIndexPipeline来自hcengineering/server-indexer传入全文适配器、内容适配器、Hulylake 客户端、通信 API 与监听器。值得一提的是ModelMiddleware加载模型时使用了 pods/fulltext/src/utils.ts 中的fulltextModelFilter只保留Class、Attribute、Mixin、Type、Status、Permission、Space、Tx、FullTextSearchContext等结构类元数据大幅裁剪索引器内存中的模型体积。6.3 全量重建的执行路径当fulltextConsumer收到FullReindex事件时pods/fulltext/src/manager.ts执行顺序为indexer.dropWorkspace()清空该 workspace 在 Elasticsearch 中的旧索引indexer.getIndexClassess()按 domain 分组列出待索引的类对每个 domain 调用indexer.reindex(ctx, domain, classes, control)分批重灌数据全程通过control.heartbeat()维持消费者心跳防止长任务被判定超时。测试侧同样覆盖了这一链路pods/fulltext/src/__tests__/indexing.spec.ts通过真实 Kafka 队列 内存模型minmodel.ts驱动WorkspaceManager完成从建索引、搜索到销毁的全流程验证是理解该服务行为的可运行样例。七、Communication API通信消息索引可选特性原文档明确指出通信消息索引默认关闭。只有当COMMUNICATION_API_ENABLEDtrue时服务才会索引来自卡片的通信消息message groups 与 messages使用 Hulylake 服务拉取消息分组与消息强制要求设置HULYLAKE_URL。在 pods/fulltext/src/workspace.ts 中可以看到对应实现仅当process.env.COMMUNICATION_API_ENABLED true时才会通过CommunicationApi.create()创建通信 API 实例并注入FullTextIndexPipeline而manager.ts中始终会调用getHulylakeClient(this.opt.hulylakeUrl, ...)构造 Hulylake 客户端地址默认为空串。实践建议如果部署环境中没有部署 Hulylake 服务请保持COMMUNICATION_API_ENABLED关闭默认值这样通信索引会被完整跳过不会因 Hulylake 不可用而报错——这正是该开关被设计出来的目的。八、部署检查清单与常见注意点综合上文落地部署或排障时可对照以下清单环境变量完备性SERVER_SECRET、DB_URL、FULLTEXT_DB_URL、REKONI_URL、ELASTIC_INDEX_NAME、ACCOUNTS_URL六项缺一不可缺失即退出外部依赖可达性Elasticsearch9200、Rekoni4004、Account3000、数据库、Kafka 平台队列、存储MinIO 等需在同一网络拓扑内可访问token 一致性任何调用 API 或 Dev Tool 的场景token 必须用 workspace UUID 与 Fulltext 相同的SERVER_SECRET签发重建优先走队列生产环境尽量使用rushx tool fulltext-reindex workspace或fulltext-reindex-all让重建请求经队列异步执行避免直接 HTTP 调用的超时与耦合只清不建需要清空索引而不重建时调用/api/v1/reindex并设置onlyDrop: true通信索引按需开启无 Hulylake 环境务必保持COMMUNICATION_API_ENABLED关闭。通过本文覆盖的 README 原文内容与源码级印证入口 pods/fulltext/src/index.ts、路由 pods/fulltext/src/server.ts、队列管理 pods/fulltext/src/manager.ts、索引上下文 pods/fulltext/src/workspace.ts你可以独立完成 Fulltext 服务的部署配置、索引重建与问题排查并在此基础上深入阅读hcengineering/server-indexer与hcengineering/elastic包进一步定制索引行为。【免费下载链接】platformHuly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)项目地址: https://gitcode.com/GitHub_Trending/platform80/platform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考