Backstage Search 架构深度解析:可插拔搜索引擎与统一索引流水线
Backstage Search 架构深度解析可插拔搜索引擎与统一索引流水线【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage导读本文基于 Backstage 官方文档《Search Architecture》展开系统讲解 Backstage Search 如何以可插拔搜索引擎 统一文档索引流水线的方式让整个 Backstage 生态软件目录、TechDocs、任意插件的内容变得可搜索。你将掌握 Search 的架构目标、技术栈分层、核心抽象Collator、Decorator、Query Translator、Search Engine并结合仓库源码理解索引构建与查询翻译的底层实现以及如何为 Backstage 接入 Lunr、Postgres、Elasticsearch/OpenSearch 等不同搜索引擎。一、架构总览一套为任意搜索引擎设计的搜索框架Backstage Search 并不是一个搜索引擎本身而是一个位于 Backstage 实例与所选搜索引擎之间的集成与翻译层。它的设计目标是支持多种多样的搜索引擎Elasticsearch、OpenSearch、Postgres、Lunr 等为插件开发者提供简单的开发体验为 Backstage 终端用户提供开箱即用的良好体验。下图是该架构的官方总览图源文件位于 docs/assets/search/architecture.drawio.svg在基础层面base-level该架构要达成以下能力跨生态搜索支持在整个 Backstage 生态中进行搜索范围不限于软件目录Software Catalog中的实体。可搜索内容并不强制要求与软件目录直接相关但按约定官方鼓励使用众所周知的字段名或属性建立松散关联以便统一检索体验。任意搜索引擎可部署通过核心搜索插件与搜索引擎特定逻辑之间的集成翻译层支持使用任意搜索引擎部署 Backstage并且该翻译层可以针对不同搜索引擎进行扩展。官方还计划引入能力将后端 API 端点替换为自定义端点以便做更简单的定制。从架构上希望支持的更高级用例advanced use-cases包括任何插件都可以向搜索暴露新内容例如实体元数据、TechDocs 文档任何插件都可以向既有搜索内容追加相关元数据例如 TechDocs 页面的路径 location可以精炼搜索查询例如调整 ranking、scoring 等可以自定义搜索 UI可以为任意 Backstage 插件或部署添加搜索功能。架构非目标Non-goals需要明确的是当前版本不打算直接支持事件驱动或增量式的索引管理。官方架构将重点放在按计划、批量式的索引管理scheduled, bulk index management上——这一点也直接决定了后文调度器Scheduler在整个索引体系中的核心地位。二、技术栈分层Search 功能被拆分为多个 npm 包以插件 / 插件库 / 后端插件模块的形式分层官方技术栈表如下技术栈包名仓库源码位置前端插件 Frontend Pluginbackstage/plugin-searchplugins/search前端插件库 Frontend Plugin Librarybackstage/plugin-search-reactplugins/search-react同构插件库 Isomorphic Plugin Librarybackstage/plugin-search-commonplugins/search-common后端插件 Backend Pluginbackstage/plugin-search-backendplugins/search-backend后端插件库 Backend Plugin Librarybackstage/plugin-search-backend-nodeplugins/search-backend-node后端插件模块Elasticsearch/OpenSearchbackstage/plugin-search-backend-module-elasticsearchplugins/search-backend-module-elasticsearch后端插件模块Postgresbackstage/plugin-search-backend-module-pgplugins/search-backend-module-pg这种分层的意义在于前端、后端、以及前后端共享的类型与契约被严格隔离。plugin-search-common属于同构库isomorphic因为它定义了前后端共用的类型如SearchQuery、SearchDocument前后端都能安全引用而plugin-search-backend-node承载了索引构建、调度、批处理等纯后端逻辑。三、核心概念与数据流要理解整个架构必须先掌握以下八个核心概念官方定义见 docs/features/search/concepts.md1. 搜索引擎Search EngineSearchEngine是一个接口interface其具体实现负责与不同搜索引擎Elasticsearch、Lunr、Solr 等通信。这个抽象存在的目的就是为了满足组织对不同搜索引擎的选型需求。开箱即用时Backstage Search 自带一个构建在Lunr之上的内存搜索引擎实现。2. 查询翻译器Query Translator由于你可以自带搜索引擎而每种搜索引擎又拥有自己独特而强大的查询语言因此需要一个翻译层把抽象的搜索查询包含搜索词 term、过滤器 filters、文档类型 types转换为具体搜索引擎能够理解的查询。SearchQuery的抽象形态定义于 plugins/search-common/src/types.tsexport interface SearchQuery { term: string; types?: string[]; filters?: JsonObject; pageLimit?: number; pageCursor?: string; }搜索引擎通常自带简单的翻译器能对搜索词与过滤器做基础变换但如果你想针对组织内部的检索调优如调整评分、模糊匹配策略可以编写自己的翻译器。3. 文档与索引Documents and Indices文档Document是一个抽象概念代表任何可以被搜索找到的东西——可以是一个软件实体、一个 TechDocs 页面等等。文档由元数据字段组成至少必须包含 title、text、location作为 URL三个字段。索引Index则是某一种类型的文档集合。从源码看这个最小字段契约被固化在SearchDocument接口中plugins/search-common/src/types.tsexport interface SearchDocument { /** 文档的主名称如名称、标题、标识符等 */ title: string; /** 文档的自由文本如描述、内容等 */ text: string; /** 文档的相对或绝对 URL点击搜索结果时的跳转目标 */ location: string; }而面向索引阶段的IndexableDocument则在其基础上追加了可选的authorization.resourceRef字段用于在搜索权限系统中判断某条结果对某用户是否可见plugins/search-common/src/types.ts——这是可搜索内容与权限系统打通的架构基础。4. 收集器Collator有了索引概念还需要定义什么内容可以被搜索——Collator 就是干这个的。Collator 是可读的对象流readable object streams产出符合最小字段集title、location、text的文档流并且可以携带 Collator 自定义的任何其他字段。一个 Collator 负责定义并收集一种类型的文档。从源码看DocumentCollatorFactory接口plugins/search-common/src/types.ts只要求两件事一个type用作搜索引擎的索引名和一个返回Readable流的方法getCollator()。Catalog 后端等插件就提供了开箱即用的默认 Collator 工厂default collator factories让你能快速开始跨 Backstage 搜索。5. 装饰器Decorator有时你想往索引文档里补充 Collator 本身不知道的额外信息。例如软件目录知道软件实体但可能不知道它们的使用情况或质量评分。Decorator 是位于 Collator读流与 Indexer写流之间的转换流transform stream可以在文档被收集和索引的过程中追加额外字段这些额外元数据随后可用于偏置搜索结果或改进搜索体验。从源码看DecoratorBaseplugins/search-backend-node/src/indexing/DecoratorBase.ts的decorate()方法有三种返回方式赋予了它比加字段更强的能力返回undefined表示该文档应从索引中剔除返回单个修改后的文档可以新增、编辑或删除字段返回一个文档数组用于把一个文档转换派生为多个文档。也就是说Decorator 在索引期既可以加元数据也可以过滤文档甚至可以额外产出新文档。6. 调度器Scheduler索引的构建与维护方式有很多种但 Backstage Search 选择了按计划整体重建索引completely rebuild indices on a schedule。不同的 Collator 可以根据源信息的更新频率配置不同的刷新间隔。当搜索索引分布在多个后端节点时防止冲突的协调工作通常由分布式的SchedulerServiceTaskRunner完成关于多节点数据库配置可参考 docs/tutorials/configuring-plugin-databases.md。7. 搜索页Search Page搜索页面是非常定制化的东西——并非每个 Backstage 实例都想要相同的界面。为此Search 插件负责状态管理与搜索逻辑而搜索页面的大部分布局则定义在 Backstage App 中的搜索页组件里。这样用户就可以尽情定制自己的搜索体验。8. 搜索上下文与组件Search Context and Components一个搜索体验如一个页面由任意数量的搜索组件组成它们通过**搜索上下文search context**串联起来。每个搜索体验的上下文包含搜索词、过滤器、类型、结果以及用于分页的 page cursor。不同组件以不同方式使用这个上下文SearchBar /设置搜索词SearchFilter /设置过滤器SearchResult /展示搜索结果。其中SearchResult /与SearchFilter /本身是可扩展的如果还需要更多定制你可以像使用其他 React context 一样直接使用搜索上下文编写属于自己的自定义搜索组件。四、源码视角索引流水线如何被组装理解了概念之后再从源码看这些抽象是如何被组装成一条可运行的流水线的。IndexBuilder索引的组装器IndexBuilderplugins/search-backend-node/src/IndexBuilder.ts是索引体系的装配中心它接收一个searchEngine通过addCollator({ factory, schedule })注册收集器、通过addDecorator(...)注册装饰器并把它们编译成调度任务。注册 Collator 时它会记录该文档类型的visibilityPermission可见性权限供搜索权限系统使用getSearchEngine()与getDocumentTypes()则把最终装配好的搜索引擎与文档类型信息暴露给上层。从读流到写流的批处理管道索引流水线的核心是一条 Node.js 流式管道pipelineCollator (Readable) → Decorator (Transform) → Search Engine Indexer (Writable)其中搜索引擎的 Indexer 通常继承自BatchSearchEngineIndexerplugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts它以objectMode流式接收文档并按batchSize攒批后调用抽象方法index(documents)批量写入搜索引擎。批量大小是 ES 引擎调优如batchSize的底层机制来源。Lunr开箱即用的内存实现LunrSearchEngineplugins/search-backend-node/src/engines/LunrSearchEngine.ts是SearchEngine接口的默认内存实现。其内置的QueryTranslator会把抽象的SearchQuery翻译成 Lunr 查询构建器lunr.Index.QueryBuilder并默认pageLimit为 25引擎内部还通过随机 UUID 生成高亮标签highlightPreTag/highlightPostTag为搜索结果命中词高亮提供基础。Lunr 适合本地零配置开发但不建议在生产环境使用详见后文引擎选型。文档类型与搜索结果结构查询返回的结果通过Result/ResultSetplugins/search-common/src/types.ts结构化表达每条结果包含type对应 Collator 工厂的type、原始document、可选的高亮信息highlightpreTag/postTag包裹命中词由 UI 解析渲染以及可选排名rankResultSet通过nextPageCursor/previousPageCursor支撑分页。这些类型就是前后端搜索交互的统一契约。五、把 Search 接入你的 Backstage安装与引擎选型架构最终要落地为可运行的代码。官方推荐的接入方式见 docs/features/search/getting-started.md 与 docs/features/search/search-engines.md。后端安装在packages/backend中添加搜索相关的后端插件与模块yarn --cwd packages/backend add backstage/plugin-search-backend backstage/plugin-search-backend-module-pg backstage/plugin-search-backend-module-catalog backstage/plugin-search-backend-module-techdocs然后在packages/backend/src/index.ts中注册const backend createBackend(); // Other plugins... // search plugin backend.add(import(backstage/plugin-search-backend)); // search engines backend.add(import(backstage/plugin-search-backend-module-pg)); // search collators backend.add(import(backstage/plugin-search-backend-module-catalog)); backend.add(import(backstage/plugin-search-backend-module-techdocs)); backend.start();上述配置会默认使用 Lunr 内存搜索引擎如果你的数据库是 Postgres则会自动改用 Postgres 作为搜索引擎。同时上述安装也自动配置了两个 Collator——Catalog 与 TechDocs——它们会把软件目录实体与 TechDocs 文档索引进搜索系统。三种搜索引擎的选型对比引擎状态适用场景配置复杂度Lunr默认启用内置于搜索后端插件本地开发、零配置起步最低只需backend.add(import(backstage/plugin-search-backend))Postgres官方支持要求Postgres 12想避免额外维护 ES 等外部服务的生产环境上万文档量级表现良好低通过 Backstage 数据库管理器建立连接Elasticsearch / OpenSearch官方支持大规模生产环境、需要高级查询能力较高需额外配置Lunr是零配置引擎但它强烈不建议用于生产环境——部署时请改用其他引擎。Postgres只需把 Postgres 配置为 Backstage 的数据库即可额外依赖只需安装backstage/plugin-search-backend-module-pg。其可选的highlightOptions配置基于 Postgres 的ts_headline实现命中词高亮docs/features/search/search-engines.mdsearch: pg: highlightOptions: useHighlight: true # 是否启用高亮默认 true maxWord: 35 # 输出标题最长长度默认 35 minWord: 15 # 输出标题最短长度默认 15 shortWord: 3 # 长度小于等于该值的词会在标题首尾被丢弃除非是查询词默认 3可过滤常见英文冠词 highlightAll: false # 为 true 时整篇文档作为标题忽略以上三个参数默认 false maxFragments: 0 # 显示的最大文本片段数默认 0 表示非片段式标题生成大于 0 启用片段式生成 fragmentDelimiter: ... # 拼接片段的分隔符默认 ... 注意ts_headline高亮对性能有潜在影响如遇问题可用useHighlight: false快速关闭。Elasticsearch/OpenSearch是生产环境最常用的选择官方支持 AWS、Elastic.co、自托管等多种形态详见 docs/features/search/search-engines.md 中的完整配置示例# AWS 托管 search: elasticsearch: provider: aws node: https://my-backstage-search-asdfqwerty.eu-west-1.es.amazonaws.com # Elastic Cloud使用 Cloud ID search: elasticsearch: provider: elastic cloudId: backstage-elastic:asdfqwertyasdfqwertyasdfqwertyasdfqwerty auth: username: elastic password: changeme # 自托管 OpenSearch search: elasticsearch: provider: opensearch node: http://0.0.0.0:9200 auth: username: opensearch password: changeme值得注意的几个调优项batchSize默认 1000。低配实例如 AWS 小规格可能因thread_pool限制触发429 Too Many Requests /_bulk此时应调小如batchSize: 100大规格实例可调大batchKeyField默认由 ES 自动生成_id如需高频查找/更新既有文档可设置一致的文档标识注意该字段值不唯一时 ES 会覆盖同_id文档indexPrefix默认索引名形如software-catalog-index__20250219类型__日期可配置自定义前缀如custom-prefix-queryOptions通过fuzziness默认AUTO最大 Levenshtein 距离与prefixLength默认 0控制查询词开头必须精确匹配的最小字符数调节模糊匹配自定义认证扩展点企业环境可用elasticsearchAuthExtensionPoint提供动态认证如自动轮换的 Bearer TokengetAuthHeaders()会在每次请求前调用实现 token 的即时获取与自动轮换该能力支持elastic、opensearch及默认 providerawsprovider 使用 SigV4 签名不支持自定义认证。前端安装前端安装新前端系统若仍使用旧前端系统请参见 docs/features/search/getting-started--old.mdyarn --cwd packages/app add backstage/plugin-search backstage/plugin-search-react安装后搜索插件通过默认功能发现feature discovery自动生效提供/search搜索页、侧边栏搜索导航项与可打开的搜索弹窗更多安装方式见 docs/frontend-system/building-apps/05-installing-plugins.md。搜索页可在app-config.yaml中配置例如关闭搜索结果的访问追踪app: extensions: - page:search: config: noTrack: true搜索结果列表项如 Catalog 插件的CatalogSearchResultListItem、TechDocs 插件的TechDocsSearchResultListItem以及搜索过滤器扩展都会被自动发现并注册需要自定义时可使用SearchResultListItemBlueprint、SearchFilterBlueprint等蓝图扩展见 docs/features/search/getting-started.md 与 docs/features/search/how-to-guides.md。六、Collator定义可搜索的内容内置 CollatorBackstage 开箱即用提供两个官方 CollatorCatalog Collatorbackstage/plugin-search-backend-module-catalog索引软件目录中的所有实体TechDocs Collatorbackstage/plugin-search-backend-module-techdocs索引目录中的全部 TechDocs。两者的默认调度都是每 10 分钟运行一次可通过app-config.yaml覆盖配置项与SchedulerServiceTaskScheduleDefinition一致支持 cron、ISO 时长、代码中使用的人类可读时长三种写法search: collators: catalog: schedule: initialDelay: { seconds: 90 } frequency: { hours: 6 } timeout: { minutes: 3 }Catalog Collator 还支持filter配置用于只收集实体的特定子集。该配置基于EntityFilterQuery语法实现docs/features/search/collators.md# 收集 kind 为 component 或 api 且 spec.lifecycle 为 production 的实体 search: collators: catalog: filter: kind: [component, api] spec.lifecycle: production更复杂的或组合可以通过列表形式表达api/openapi实体或component/experimental实体search: collators: catalog: filter: - kind: [API] spec.type: openapi - kind: [Component] spec.lifecycle: experimental编写自定义 Collator任何数据源都可以通过自定义 Collator 接入搜索。官方推荐先用模板脚手架生成模块再实现数据抓取逻辑完整教程见 docs/features/search/custom-collators.mdyarn new --select search-collator-module生成的模块会创建plugins/search-backend-module-模块名/包并自动注册到后端。核心实现是继承DocumentCollatorFactory的工厂类getCollator()返回Readable.from(this.execute())而execute()是一个 async generator逐条 yield 至少包含title、text、location三个字段的IndexableDocument。一个从内部 API 抓取博客文章的最小实现如下export class BlogPostsCollatorFactory implements DocumentCollatorFactory { public readonly type blog-posts; async getCollator(): PromiseReadable { return Readable.from(this.execute()); } private async *execute(): AsyncGeneratorIndexableDocument { const response await fetch(${this.baseUrl}/blog-posts); const posts: BlogPost[] await response.json(); for (const post of posts) { yield { title: post.title, text: post.body, location: /blog-posts/${post.id}, }; } } }对于大数据集建议在execute()中使用基于游标的分页避免一次性把所有记录载入内存。生成的测试文件使用TestPipeline来自backstage/plugin-search-backend-node对 Collator 进行端到端验证。装饰器为索引补充额外元数据与如何给搜索索引追加元数据这一架构目标对应的能力由DocumentDecoratorFactoryplugins/search-common/src/types.ts承载它通过可选的types字段声明作用于哪些文档类型缺省则作用于全部getDecorator()返回一个Transform流——即上文提到的 Decorator 转换流挂在 Collator 与 Indexer 之间。七、索引调度与多节点一致性架构的非目标决定了索引采用定期整体重建策略。通过IndexBuilder注册 Collator 时可以传入自定义的SchedulerServiceTaskRunner来控制重建频率——例如为更新频繁的文档类型配置更短的间隔const every10MinutesSchedule env.scheduler.createScheduledTaskRunner({ frequency: { minutes: 10 }, timeout: { minutes: 15 }, initialDelay: { seconds: 3 }, }); indexBuilder.addCollator({ schedule: every10MinutesSchedule, factory: DefaultCatalogCollatorFactory.fromConfig(env.config, { discovery: env.discovery, tokenManager: env.tokenManager, }), });特别注意如果使用内存型 Lunr 引擎并运行多个搜索后端节点每个节点会持有各自的内存索引官方建议实现一个非分布式non-distributed的SchedulerServiceTaskRunner来保证一致性例如忽略错误、每 10 分钟循环触发的自定义 runner或者为搜索插件配置非分布式数据库如 SQLite。当搜索索引分布在多个后端节点时防冲突协调则通常交给分布式的SchedulerServiceTaskRunner。八、进阶定制方向架构中任何插件都能暴露新内容、追加元数据、精炼查询、定制 UI的高级目标在实际开发中对应以下定制入口索引字段定制通过DefaultCatalogCollatorFactory的entityTransformer回调控制哪些数据进入索引TechDocs 侧还额外支持documentTransformer注意authorization与location不能经entityTransformer修改location只能通过locationTemplate修改详见 docs/features/search/how-to-guides.md自定义 Search API实现SearchApi接口对接你自己的搜索后端并用createApiExtension覆盖默认 API 扩展见 docs/frontend-system/utility-apis/01-index.md自定义结果展示通过SearchResultListItemBlueprint注册与 Collator 的type匹配的自定义结果列表项前端模块经createFrontendModule挂载到 App自定义查询翻译器为搜索引擎提供组织级的查询调优如评分、模糊匹配参数即上文 ES 的queryOptions。九、总结Backstage Search 的架构核心可以概括为一句话用一套统一、可扩展的索引与查询契约隔离内容生产Collator/Decorator与检索执行Search Engine/Query Translator。上层插件只需产出符合title/text/location最小契约的文档流即可被索引下层搜索引擎只需实现SearchEngine接口并配备查询翻译器即可接入。而调度器负责按计划批量重建索引前端则通过搜索上下文把各组件编织成可定制的搜索体验。这套设计同时满足了官方声明的三大诉求——多搜索引擎支持、插件开发者友好、终端用户开箱即用。深入阅读架构总览见 docs/features/search/architecture.md核心概念见 docs/features/search/concepts.md引擎配置见 docs/features/search/search-engines.mdCollator 见 docs/features/search/collators.md上手实践见 docs/features/search/getting-started.md。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考