如果匹配特定模式,则在XQuery函数中使用'following-sibling'来返回TIME元素

时间:2012-09-24 15:10:56

标签: xml xquery saxon

我正在尝试在XQuery中编写一个函数,如果检测到特定的值模式,它会从XML数据序列返回一个时间戳。数据实际上是系统api消息的测试日志

示例XML数据看起来类似于下面的代码段。如果找到序列,则假定时间戳(TIME标记)对于模式条目的每一行都是相同的。

我需要检测的特定模式&amp;返回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

3 个答案:

答案 0 :(得分:1)

你接近成功了。

有些事情需要清理。首先,for $row in $msgSeqsome $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           
};