
OpenMetadata 词表术语关系类型 CSV 导入导出设计计划与源码实现全解析【免费下载链接】OpenMetadataThe Open Context Layer for Data and AI , OpenMetadata is the open platform for building trusted data context and business semantics for humans, AI assistants, and agents.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMetadata本文围绕 OpenMetadata 仓库中的规划文档 csv-relation-types-plan.md系统讲解词表Glossary术语 CSV 导入/导出增强方案从旧格式丢失关系类型的数据问题出发完整呈现relationType:termFQN新格式的设计、解析规则与向后兼容策略并对照 CsvUtil.java 与 GlossaryRepository.java 的当前源码还原该方案在导出、导入两侧的实际实现、边界处理与单元测试验证方式。读完后你将能够编写带关系类型前缀的术语 CSV 文件理解导入解析器如何区分关系类型前缀与含冒号的 FQN并掌握其单元测试的组织思路。一、问题背景旧版 CSV 格式丢失关系类型OpenMetadata 的词表术语实体通过relatedTerms字段维护与其他术语的关系每个关系由TermRelation对象承载包含目标术语引用term和关系类型relationType两个部分。在旧版 CSV 导入/导出实现中导出侧只导出关联术语的 FQN形如Glossary.Term1;Glossary.Term2关系类型信息被丢弃导入侧对所有解析出的关系硬编码withRelationType(relatedTo)。这导致两类数据损失术语本身带有synonym、broader、narrower或自定义关系类型时导出的 CSV 无法表达将导出的 CSV 再次导入后所有关系类型都会退化为relatedTo。而数据库中实际上一直正确存储着关系类型——损失只发生在 CSV 序列化/反序列化环节。因此该增强方案的核心目标是在不破坏旧格式兼容性的前提下让 CSV 成为无损往返round-trip的载体。二、新 CSV 格式设计relationType:termFQN2.1 格式定义新格式采用relationType:termFQN键值对多个值之间以分号;即FIELD_SEPARATOR分隔。这与 OpenMetadata CSV 体系中既有的type:value编码惯例保持一致例如 owner 字段的team:marketing、extension 字段的key:value。规划文档 csv-relation-types-plan.md 中给出的三种典型形态# 新格式带关系类型前缀 relatedTerms synonym:Finance.Revenue;broader:Finance.Income;narrower:Finance.Net Revenue # 向后兼容无前缀时默认 relatedTo relatedTerms Finance.Revenue;Finance.Income # 混合格式新旧条目共存 relatedTerms synonym:Finance.Revenue;Finance.Income;broader:Finance.Gross Income2.2 默认关系类型方案定义了以下内置关系类型关系类型说明relatedTo通用关联术语默认值synonym同义术语broader更宽泛的上位词narrower更具体的下位词antonym反义术语partOf部分/组件关系hasPart包含关系2.3 解析规则若条目包含:且冒号前的前缀是有效的关系类型→ 使用该关系类型冒号后的部分作为术语 FQN若无:或前缀不是有效关系类型 → 默认按relatedTo处理整个字符串视为 FQN有效关系类型的判定依据glossaryTermRelationSettings关系类型配置或内置默认值——这一条在最终实现中得到了动态化落地见下文 4.2 节。2.4 向后兼容行为矩阵CSV 格式导入行为Glossary.Term1;Glossary.Term2全部关系 →relatedTosynonym:Glossary.Term1;Glossary.Term2前者 →synonym后者 →relatedTosynonym:Glossary.Term1;broader:Glossary.Term2两个关系类型均保留三、导出侧实现CsvUtil.addTermRelations3.1 当前源码该方案的导出增强已在 CsvUtil.java 中落地。方法职责与 Javadoc 描述/** * Add term relations to CSV record with relation type prefix. * Format: relationType:termFQN for non-default relations, or just termFQN for relatedTo. * Example: synonym:Glossary.Term1;broader:Glossary.Term2;Glossary.Term3 */ public static ListString addTermRelations( ListString csvRecord, Listorg.openmetadata.schema.type.TermRelation termRelations) { csvRecord.add( nullOrEmpty(termRelations) ? null : termRelations.stream() .map( tr - { String relationType tr.getRelationType(); String fqn tr.getTerm().getFullyQualifiedName(); // Include relation type prefix for non-default relations if (relationType ! null !relationType.isEmpty() !relationType.equals(relatedTo)) { return relationType ENTITY_TYPE_SEPARATOR fqn; } return fqn; }) .sorted() .collect(Collectors.joining(FIELD_SEPARATOR))); return csvRecord; }实现要点对照规划文档中的New版本代码省略默认前缀仅当relationType非空且不等于relatedTo时才拼接relationType:前缀默认关系保持裸 FQN 输出使导出文件与旧格式视觉上兼容复用既有分隔符常量前缀与 FQN 之间使用ENTITY_TYPE_SEPARATOR值为:条目之间使用FIELD_SEPARATOR值为;两者定义于 CsvUtil.java排序保证确定性拼接前对条目做.sorted()确保同一份数据多次导出结果一致对比旧实现只排序 FQN新实现排序的是前缀 FQN整体字符串空值处理nullOrEmpty(termRelations)为真时写入null与工具类中addEntityReferences、addTagLabels等其他字段方法的行为一致。3.2 导出调用链导出入口在 GlossaryRepository.java 的addRecord方法中relatedTerms位于词表术语 CSV 的第 6 列下标 5CsvUtil.addFieldList(recordList, entity.getSynonyms()); // 列 4synonyms addTermRelations(recordList, entity.getRelatedTerms()); // 列 5relatedTerms addField(recordList, termReferencesToRecord(...)); // 列 6references从源码结构看词表术语 CSV 的完整列布局为0parent、1name、2displayName、3description、4synonyms、5relatedTerms、6references、7tags、8reviewers、9owner、10glossaryStatus、11color、12iconURL、13domains、14extension。3.3 导出侧单元测试CsvUtilTest.java 覆盖了规划文档Phase 2: Testing中的导出用例且断言比规划更具体测试方法验证点testAddTermRelationsHandlesNullAndEmptyInputsnull与空列表均输出单一null字段testAddTermRelationsOmitsRelatedToPrefixrelatedTo关系导出为裸 FQNGlossary.AlphatestAddTermRelationsTreatsNullRelationTypeAsRelatedTo关系类型为null时按默认处理不产生前缀testAddTermRelationsEmitsPrefixForNonDefaultType非默认类型输出synonym:Glossary.AlphatestAddTermRelationsSortsAndMixesTypes混合类型排序后输出Glossary.Alpha;broader:Glossary.Beta;synonym:Glossary.Zeta最后一个用例同时验证了排序作用于带前缀的完整字符串这一行为可认为是对规划文档中New版导出代码的回归验证。四、导入侧实现GlossaryRepository.getTermRelationsFromCsv4.1 解析主流程导入侧实现位于 GlossaryRepository.javaGlossaryTerm内部导入器的第 6 列解析由第 320 行withRelatedTerms(getTermRelationsFromCsv(printer, csvRecord, 5))调用/** * Parse term relations from CSV field with support for relation type prefix. * Format: relationType:termFQN or just termFQN (defaults to relatedTo). * Example: synonym:Glossary.Term1;broader:Glossary.Term2;Glossary.Term3 */ private ListTermRelation getTermRelationsFromCsv( CSVPrinter printer, CSVRecord csvRecord, int fieldNumber) throws IOException { if (!processRecord) { return null; } String fieldValue csvRecord.get(fieldNumber); if (nullOrEmpty(fieldValue)) { return null; } ListTermRelation termRelations new ArrayList(); String[] entries fieldValue.split(FIELD_SEPARATOR); for (String entry : entries) { String relationType relatedTo; // Default relation type String termFqn entry.trim(); // Check for relationType:fqn format int colonIndex entry.indexOf(:); if (colonIndex 0) { String prefix entry.substring(0, colonIndex).trim(); String suffix entry.substring(colonIndex 1).trim(); if (isValidRelationType(prefix)) { relationType prefix; termFqn suffix; } else if (!prefix.contains(.)) { // Prefix has no dots, so it looks like an intended relation type, not part of an FQN importFailure( printer, invalidField( fieldNumber, String.format( Invalid relation type %s in entry %s. Valid types: %s, prefix, entry.trim(), getValidRelationTypeNames())), csvRecord); continue; } // If prefix contains dots, its likely part of an FQN — treat entire string as FQN } // Resolve the term FQN to an EntityReference EntityReference termRef getEntityReference(printer, csvRecord, fieldNumber, GLOSSARY_TERM, termFqn); if (termRef ! null) { GlossaryTerm resolvedTerm Entity.getEntity(GLOSSARY_TERM, termRef.getId(), , Include.NON_DELETED); if (resolvedTerm.getEntityStatus() ! null resolvedTerm.getEntityStatus() ! EntityStatus.APPROVED) { importFailure( printer, invalidField( fieldNumber, String.format( Glossary term %s must have APPROVED status. Current: %s, termFqn, resolvedTerm.getEntityStatus())), csvRecord); processRecord false; continue; } termRelations.add(new TermRelation().withTerm(termRef).withRelationType(relationType)); } } return termRelations.isEmpty() ? null : termRelations; }逐段对照规划文档的New版本代码实现的要点与增强如下1冒号定位与三元判定逻辑解析器对每个条目查找第一个冒号entry.indexOf(:)并按前缀特征做三分支判定colonIndex 0且前缀命中有效关系类型集合 → 前缀作为关系类型、冒号后作为 FQN前缀不在有效集合中但前缀不含点号→ 判定用户本意是写关系类型但写错了通过importFailure记录字段级错误报错信息中列出全部合法类型名并跳过该条目前缀含点号→ 大概率是 FQN 自身的一部分例如Database:Schema.Table这类含冒号的 FQN整个字符串按 FQN 处理关系类型回落为relatedTo。规划文档中Edge Cases第 1 条FQN 含冒号与第 2 条无效关系类型正是由此覆盖。需要注意实现与规划的一处差异规划建议无效前缀时整体按 FQN relatedTo处理而实现引入了前缀无点号则报错的更严格分支——这使拼写错误的关系类型如synonm:...不会静默降级而是在导入报告中显式暴露。另外规划中空关系类型:Glossary.Term默认relatedTo的场景对应colonIndex 0不满足 0条件的路径整个条目按 FQN 处理行为与规划一致。2关系类型白名单的动态化规划文档给出的实现草案使用硬编码集合加占位方法private static final SetString VALID_RELATION_TYPES Set.of( relatedTo, synonym, broader, narrower, antonym, partOf, hasPart );而当前源码改为从关系类型 DAO 动态加载GlossaryRepository.javaprivate boolean isValidRelationType(String relationType) { return validRelationTypeNames.contains(relationType); } private String getValidRelationTypeNames() { return validRelationTypeNames.stream().sorted().collect(Collectors.joining(, )); } private static SetString loadValidRelationTypeNames() { RelationshipTypeResolver resolver new RelationshipTypeResolver(Entity.getCollectionDAO().relationshipTypeDAO()); return resolver.list().stream() .map(RelationshipType::getName) .collect(Collectors.toUnmodifiableSet()); }白名单由 RelationshipTypeResolver.java 基于relationshipTypeDAO列出全部关系类型RelationshipType::getName。这意味着规划文档Edge Cases第 4 条——自定义关系类型——在实现中是真实生效的只要实例的关系类型配置glossaryTermRelationSettings中登记了自定义类型CSV 前缀即可使用该类型无需修改代码对应的解析与报错行为由 RelationshipTypeResolverTest.java 所在模块的测试保障。3关联术语状态校验实现中还包含规划文档未提及的一层校验每个被关联的术语解析后会通过Entity.getEntity回查其实体状态若状态非APPROVED则报字段级错误。这保证了 CSV 导入不会把草稿或已弃用术语建立为关系目标属于导入质量约束的加强。五、字段文档更新glossaryCsvDocumentation.json规划文档Phase 1 / 1.3要求同步更新术语 CSV 的字段文档。当前 glossaryCsvDocumentation.json 中的relatedTerms字段说明{ name: relatedTerms, required: false, description: List of related glossary terms with optional relation type. Format: relationType:termFQN or just termFQN (defaults to relatedTo). Multiple terms separated by ;. Valid relation types: relatedTo (default), synonym, broader, narrower, antonym, partOf, hasPart., examples: [ Business terms.Client Identifier;Support.Subscriber Id - Both default to relatedTo, synonym:Business terms.Client Identifier;broader:Support.Subscriber Id - With explicit relation types, synonym:Finance.Revenue;Finance.Income;narrower:Finance.Net Revenue - Mixed format ] }该文档与规划中提出的格式说明 三类示例纯旧格式 / 显式类型 / 混合格式要求一一对应且示例使用真实术语 FQN可直接复制到 CSV 文件中验证。六、边界场景与迁移说明6.1 边界场景核对对照规划Phase 3: Edge Cases场景规划预期当前源码实现FQN 含冒号如Database:Schema.Term前缀校验失败时整体按 FQN 处理前缀含点号 → 整体按 FQN 处理默认relatedTo无效关系类型整体按 FQN relatedTo前缀无点号 → 显式importFailure报错更严格前缀含点号 → 按 FQN 处理空关系类型:Glossary.Term默认relatedTocolonIndex 0不进入前缀分支整体按 FQN 处理自定义关系类型依赖glossaryTermRelationSettings查询loadValidRelationTypeNames()经RelationshipTypeResolver动态加载真实生效6.2 迁移说明规划Migration Notes无需数据库迁移数据库早已正确存储关系类型改动只涉及 CSV 序列化层存量 CSV 继续可用旧格式文件无前缀导入后全部映射为relatedTo行为不变新导出非默认关系自动带类型前缀导出→再导入的往返过程不再丢失synonym、broader、narrower等语义。6.3 涉及文件清单对照规划Files to Modify文件变更当前仓库状态CsvUtil.javaaddTermRelations()输出关系类型前缀已落地GlossaryRepository.javagetTermRelationsFromCsv()解析关系类型已落地且白名单动态化glossaryCsvDocumentation.json字段说明与示例更新已落地CsvUtilTest.java导出侧 5 个单元用例已落地RelationshipTypeResolverTest.java关系类型解析器测试已落地七、小结这份CSV Import/Export Enhancement for Glossary Term Relations方案的价值在于用最小的格式扩展解决了语义丢失问题通过relationType:termFQN前缀在导出/导入两侧无损保留关系类型默认关系省略前缀与旧格式 CSV 完全向后兼容存量文件无需迁移解析器以冒号位置 前缀是否含点号双特征区分关系类型前缀与含冒号 FQN并在前缀拼写错误时显式报错有效关系类型白名单由RelationshipTypeResolver从关系类型配置动态加载自定义关系类型无需改码即可参与 CSV 往返导出确定性排序、空值语义、字段文档与单元/集成测试均按规划逐项落地。对使用者的直接建议编写或审查词表术语 CSV 时relatedTerms列中凡是需要表达同义、上下位、反义等语义的条目都应使用显式前缀而普通关联可继续书写裸 FQN导入前若不确定合法类型名可参考导入错误信息中列出的Valid types清单或核对该实例的关系类型配置。【免费下载链接】OpenMetadataThe Open Context Layer for Data and AI , OpenMetadata is the open platform for building trusted data context and business semantics for humans, AI assistants, and agents.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMetadata创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考