我正在尝试在XQuery中编写一个函数,如果检测到特定的值模式,它会从XML数据序列返回一个时间戳。数据实际上是系统api消息的测试日志
示例XML数据看起来类似于下面的代码段。如果找到序列,则假定时间戳(TIME标记)对于模式条目的每一行都是相同的。
我需要检测的特定模式&返回TIME
- 顺序中有四个<FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE>
条目,其后依次是四个<FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE>
条目 - 所有条目都具有相同的时间戳。
<SEQUENCE><TIME>13.00</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>INVALID</MODE></SEQUENCE>
<SEQUENCE><TIME>13.00</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>INVALID</MODE></SEQUENCE>
<SEQUENCE><TIME>13.00</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>
<SEQUENCE><TIME>13.00</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>
<SEQUENCE><TIME>13.00</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>
<SEQUENCE><TIME>13.00</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>
<SEQUENCE><TIME>14.05</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>
<SEQUENCE><TIME>15.94</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>INVALID</MODE></SEQUENCE>
<SEQUENCE><TIME>15.94</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>INVALID</MODE></SEQUENCE>
我试图定义的函数如下,但是'empty sequence not allowed'
给出了运行时错误。不幸的是我没有IDE,我可以设置一个断点并调试它 - 我想我选择一个带有FOR
的条目后我就不能使用follow-sibling。
declare function local:get_multi_track_sequence_time( $msgSeq as element()*) as xs:double {
for $row in $msgSeq
where some $entry in $row satisfies($entry/SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI'] )
return data($row/SEQUENCE/TIME)
};
感谢。我是XQuery的亲戚。
--------------------- 编辑 - 根据建议添加测试功能 ---------- -----------
感谢您收到的建议。我根据给出的有用信息编写了以下自包含的测试函数 - 该函数无法匹配后续兄弟的。
该函数创建一个包含测试序列的变量data
。它所处的功能返回一个空序列。要求是它返回14.050000
以指示标量TIME
,其中顺序有四个<FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE>
条目,后面依次是四个<FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE>
条目(即TIME {测试数据中的{1}}。
(有趣的是,如果仅使用第一个表达式,它会成功返回一系列双精度数,即匹配所有出现的TRACK_STATUS / VALID,并且未指定后续兄弟匹配。)
14.050000
答案 0 :(得分:1)
你接近成功了。
有些事情需要清理。首先,for $row in $msgSeq
和some $entry in $row
的组合正在迭代相同的元素序列(作为$msgSeq
传入的序列)。从你的问题中不清楚你传递的内容是$ msgSeq的值,但我想知道你是where some $entry in $row/*
还是(利用隐式存在量化)只是where $row/*/SEQUENCE ...
。
其次,您的问题描述建议您要查找具有特定属性的八个相邻SEQUENCE元素的序列(父项)。但是你的长XPath表达式不需要相邻:$foo/following-sibling::SEQUENCE
匹配名为SEQUENCE的$ foo兄弟之后的所有。要约束使项目相邻的路径,您需要更改表单的步骤
.../following-sibling::SEQUENCE[ ... conditions ... ]
到
.../following-sibling::*[1]/self::SEQUENCE[ ... ]
如果以下兄弟保证是SEQUENCE,当然,这可以缩短,但可能会失去一些清晰度。
第三,你的声明说你只返了一双。但是函数体不能保证只返回一个double,因此悲观处理器的严格静态类型分析可能会拒绝它。我首先看到的是:
如果$ row包含多个SEQUENCE元素,那么data($row/SEQUENCE/TIME)
将返回多个TIME值,而不仅仅是一个。如果您非常确信所有SEQUENCE / TIME值都相同,则添加[1]
是一种确保此表达式最多返回一个值的方法,而不是(例如)八或二十。
当没有匹配时,你的函数实际上返回空序列,而不是一个双精度序列。
如果$ msgSeq中有多个$行满足条件,则返回通过评估满足条件的每个$行data($row/SEQUENCE/TIME)
形成的结果序列。数据的形状可以保证永远不会发生这种情况,但静态分析器不太可能知道这一点。
下面给出的函数的修订形式假设(a)$ msgSeq是SEQUENCE元素的序列,(b)你想要找到每个SEQUENCE元素,它是你描述的事件序列中的第一个事件,并且返回它的时间戳(所以函数作为一个整体返回零或更多的双倍 - 我不会问你是什么拥有你使用double
代表小时和分钟而不是xs:时间或更合理的东西,这是你和你的工程良心之间的关系。
declare function local:get_multi_track_sequence_time(
$msgSeq as element()*
) as xs:double* {
for $entry in $msgSeq
where $entry/self::SEQUENCE
[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
/following-sibling::*[1]/self::SEQUENCE
[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
return data($entry/TIME)
};
当问题中显示的SEQUENCE元素序列传递给此函数时,它返回数字14.05。
答案 1 :(得分:0)
抛出错误的原因是你的函数没有返回任何东西(空序列),但它被声明为返回xs:double
。如果您在Saxon下运行查询,则会收到更多信息性错误消息:
作为功能的结果,不允许空序列 当地:get_multi_track_sequence_time()
那么接下来的问题是,如果你的函数总是返回一个double,或者你应该更改as
声明以允许它返回一个空序列吗?类似地,编写查询的方式,可以想象返回多个结果,每个结果对应一个满足where
子句的行。这也会导致类型错误。你想允许吗?
即使在where
子句仅满足一行的情况下,您最终也会返回多个时间戳
return data($row/SEQUENCE/TIME)
因为这会选择<TIME>
的{{1}}子元素作为<SEQUENCE>
中元素的子元素。相反,你想要
$row
同样,关于return data($row/SEQUENCE[1]/TIME)
,请不要忘记使用following-sibling::
表示您正在尝试访问 next 兄弟,而不仅仅是以下任何兄弟:
[1]
这应该会给你更好的表现,同时确保你的.../following-sibling::SEQUENCE[1][TAG='2900' and FIELD='TRACK_STATUS'
and MODE='VALID']...
条款不会给出误报。
答案 2 :(得分:0)
我有以下工作函数,它在返回序列的第一个值中给出正确的标量时间。
declare function local:get_multi_track_sequence_times( $msgSeq as element()* ) as xs:double* {
let $data := (<ROOT>{$msgSeq}</ROOT>)
let $s1 := $data/SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID'],
$s2 := $s1/following-sibling::SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID'],
$s3 := $s2/following-sibling::SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID'],
$s4 := $s3/following-sibling::SEQUENCE[TAG='2900' and FIELD='TRACK_STATUS' and MODE='VALID'],
$s5 := $s4/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI'],
$s6 := $s5/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI'],
$s7 := $s6/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI'],
$s8 := $s7/following-sibling::SEQUENCE[TAG='2900' and FIELD='MULTI_CHAN_IND' and MODE='MULTI']
return $s8/TIME
};