十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

yq 的 documentIndex 操作符:多文档 YAML 的按文档定位、筛选与溯源实战指南

yq 的 documentIndex 操作符:多文档 YAML 的按文档定位、筛选与溯源实战指南 yq 的 documentIndex 操作符多文档 YAML 的按文档定位、筛选与溯源实战指南【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq导读documentIndex别名di是 yq 中用于读取节点所属文档索引的核心操作符当输入文件包含多个用---分隔的 YAML 文档时它返回每个匹配节点所在文档的序号从 0 开始计数既可以直接输出索引值也可以与select组合按文档号精确过滤还能在构造结果时把匹配内容 文档号一并打包输出。读完本文你将掌握如何用documentIndex/di在多文档 YAML 中定位数据来源、按文档切片筛选以及这一功能在 yq 内部的实现原理源码位于 pkg/yqlib/operator_document_index.go。一、操作符速览documentIndex 与 didocumentIndex是一个零参数操作符NumArgs: 0在 yq 的表达式语法中写作documentIndex同时支持两个简写形式document_index与di。该别名关系由词法分析器统一注册见 pkg/yqlib/lexer_participle.go{DocumentIndex, documentIndex|document_?index|di, opToken(getDocumentIndexOpType), 0},即三种写法documentIndex、document_index、di在词法层面指向同一个操作类型getDocumentIndexOpType该类型在 pkg/yqlib/operation.go 中注册为var getDocumentIndexOpType operationType{Type: GET_DOCUMENT_INDEX, NumArgs: 0, Precedence: 50, Handler: getDocumentIndexOperator}其行为语义在 pkg/yqlib/doc/operators/document-index.md 中有明确定义返回输入流中每个匹配节点所属文档的索引。索引从 0 开始第一个文档为 0第二个为 1以此类推。二、检索文档索引确认每个节点来自哪个文档最直接的用法是把documentIndex接在任意路径表达式之后为每个匹配节点输出其文档号。假定文件sample.yml内容为两个文档a: cat --- a: frog执行yq .a | document_index sample.yml输出0 --- 1逐行解读.遍历到两个文档中各自的a节点管道|将每个节点依次送入document_index操作符对每个匹配节点分别返回其所在文档的索引因此结果与输入文档一一对应——第一个文档的a: cat来自文档 0第二个文档的a: frog来自文档 1输出时也以---分隔为两个独立文档。这一行为在 pkg/yqlib/operator_document_index_test.go 的测试场景中得到严格验证其期望结果逐字记录了 D0 与 D1 两个文档各自输出索引 0 和 1。三、使用简写 di同样的功能更短的表达式当表达式较长时可以使用di简写。对同一份sample.ymlyq .a | di sample.yml输出与完整写法完全一致0 --- 1由于di与documentIndex在词法层指向同一个操作类型见上文 lexer_participle.go二者没有任何行为差异可放心混用。同类场景下仓库的多文档示例文件 examples/multiple_docs.yaml 包含 3 个文档可以拿来实测yq .commonKey | di examples/multiple_docs.yaml会依次输出0、1、2对应三个文档中的commonKey节点。四、按文档索引过滤select 组合技documentIndex最常见的实战用途是作为select的过滤条件只保留指定文档中的节点。例如只取第二个文档yq select(document_index 1) sample.yml输出a: frog注意此处select作用于整个文档的根节点因此条件成立时输出的是整个文档文档 1 的全部内容a: frog而不是某个子节点。若想同时约束字段与文档可写成select(.a frog and document_index 1)这类复合条件。同样的过滤也可以用简写完成yq select(di 1) sample.yml输出a: frog测试用例 pkg/yqlib/operator_document_index_test.go 验证了select(document_index 1)与select(di 1)的期望结果均为D1, P[], (!!map)::a: frog——即只保留文档 1 的整份映射。这一模式非常适合从多文档配置中只取某一环境/某一段的场景。五、打印文档索引与匹配内容构建带来源标记的输出有时我们希望输出结果既包含匹配到的值、又标注它来自哪个文档。此时可以利用 yq 的对象构造语法{key: value}把document_index作为值写入结果对象的字段yq .a | ({match: ., doc: document_index}) sample.yml输出match: cat doc: 0 --- match: frog doc: 1拆解这个表达式.a选出两个文档中的a节点并逐个送入管道({match: ., doc: document_index})对每个节点构造一个映射——match字段存放原始节点值.doc字段存放该节点所在文档的索引。于是输出中内容与来源成对出现非常适合生成审计报告或调试多文档处理流水线。该场景同样有对应的测试断言见 pkg/yqlib/operator_document_index_test.go。六、源码原理文档索引是如何产生与读取的6.1 操作符实现逐节点读取并构造整数标量documentIndex的核心实现位于 pkg/yqlib/operator_document_index.gofunc getDocumentIndexOperator(_ *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) { var results list.New() for el : context.MatchingNodes.Front(); el ! nil; el el.Next() { candidate : el.Value.(*CandidateNode) scalar : candidate.CreateReplacement(ScalarNode, !!int, fmt.Sprintf(%v, candidate.GetDocument())) results.PushBack(scalar) } return context.ChildContext(results), nil }处理逻辑非常直白遍历上下文中的所有匹配节点context.MatchingNodes对每个候选节点candidate调用GetDocument()取得其文档索引再用CreateReplacement生成一个类型为!!int的整数标量节点作为输出。这也是为什么上文所有示例的输出都是不带引号的整数值——它们在内部被构造为!!int类型而非字符串。6.2 文档号的传递子节点向上委托给父节点节点上的文档号并非每个节点都冗余存储而是采用向上委托策略。CandidateNode.GetDocument()的实现见 pkg/yqlib/candidate_node.gofunc (n *CandidateNode) GetDocument() uint { // defer to parent if n.Parent ! nil { return n.Parent.GetDocument() } return n.document }即只要节点存在父节点就递归向上查找直到根节点返回真正存储的document字段。这保证了.a之类的子节点查询也能正确报告它所属的文档——子节点无需单独维护文档号天然继承根节点的归属。6.3 文档索引从哪来解码器在解析时分配文档索引由各解码器在解析输入时按顺序分配。以 YAML 为例pkg/yqlib/decoder_yaml.go 声明了解码器结构中的documentIndex uint字段并在解析每个文档的根节点时写入 pkg/yqlib/decoder_yaml.gocandidateNode : CandidateNode{document: dec.documentIndex}随后在下一个文档开始时递增 pkg/yqlib/decoder_yaml.go 处的计数器。同样的机制也出现在 HCL 解码器中pkg/yqlib/decoder_hcl.go 的documentIndex字段与 pkg/yqlib/decoder_hcl.go 的递增逻辑。因此文档索引从 0 开始、按文档出现的先后顺序递增这一结论可以从解码器源码中得到直接确认。七、实战延伸多文档场景下的组合用法documentIndex的价值在于让多文档输入可寻址。yq 官方使用文档 pkg/yqlib/doc/usage/convert.md 也明确指出多文档输出中的文档由documentIndex/di操作符进行索引。以下是一些可立即落地的组合用法定位第二个文档yq select(di 1) sample.yml输出整份文档 1逐文档加工yq .a | . ! | select(di 0) sample.yml先修改再按文档过滤带来源的输出如第五节所示把document_index打包进对象字段便于下游程序区分数据出处配合多文档样例验证用 examples/multiple_docs.yaml3 个文档实测yq select(di 2) | .commonKey examples/multiple_docs.yaml会得到第三个文档的commonKey值。所有上述示例均可直接复制运行若要对照期望结果做自动化验证仓库中的 pkg/yqlib/operator_document_index_test.go 就是最权威的参考答案。【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表