InterSystems 最佳实践之--发挥 $Query 的最大作用
我今天遇到了一个有趣的ObjectScript用例,有一个通用的解决方案,我想与大家分享。
用例:
我有一个JSON数组(在本例中具体而言是一个来自Jira的问题数组),我想在几个字段上进行聚合--例如,类别、优先级和问题类型。然后,我想把聚合的数据编平化到一个简单的列表中,其中包含每个组的总数。当然,对于聚合来说,使用一个本地数组的形式是有意义的,即:
agg(category, priority, type) = total
这样,对于输入数组中的每一条记录,我可以只用:
Do $increment(agg(category, priority, type))
但是一旦我做了聚合,我想把它变成一种更容易迭代的形式,比如一个整数下标的数组:
summary = n summary(1) = $listbuild(total1, category1, priority1, type1) ... summary(n) = $listbuild(totalN, categoryN, priorityN, typeN)
基础解决方案:
简单的方法是,用$Order嵌套三个For循环--比如说:
Set category = ""
For {
Set category = $Order(agg(category))
Quit:category=""
Set priority = ""
For {
Set priority = $Order(agg(category,priority))
Quit:priority=""
Set type = ""
For {
Set type = $Order(agg(category,priority,type),1,total)
Quit:type=""
Set summary($i(summary)) = $listbuild(total,category,priority,type)
}
}
}
这就是我开始使用的方法,但它有很多代码,而且如果我有更多的维度来聚合,它很快就会变得不方便了。这让我想知道--是否有一个通用的解决方案来完成同样的事情?事实证明是有的!
使用 $Query的优化方案:
我决定使用$query会有帮助。请注意,这个解决方案假定整个本地数组中子标/值的深度是一致的;如果没有了这个假设,就会导致奇怪的结果.
ClassMethod Flatten(ByRef deep, Output flat) [ PublicList = deep ]
{
Set reference = "deep"
For {
Set reference = $query(@reference)
Quit:reference=""
Set value = $listbuild(@reference)
For i=1:1:$qlength(reference) {
Set value = value_$listbuild($qsubscript(reference,i))
}
Set flat($i(flat)) = value
}
}
所以以上片段被替换为:
Do ..Flatten(.agg,.summary)
几点需要注意的:
- deep 需要在PublicList中,以便$query能够对其进行操作
- 在每个迭代中,reference 被改变为引用deep中下一组有价值的子标号--例如,值可能是:deep("foo", "bar")
- $qlength 返回reference中子标的数量
- $qsubscript 返回reference中第i个下标的值
- 当$listbuild列表被串联起来时,一个有效的$listbuild列表与合并后的列表就得出结果(这比使用任何其他分隔符都要好!)
总结
$query, $qlength, 和 $qsubscript 在处理任意深度的Global/Local数组时非常方便。
进阶阅读
$Query: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI...
$QSubscript: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.c...
$QLength: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.c...