0

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

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

我需要检测并返回TIMEof - 的特定模式是依次有四个<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.

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 )。14.050000

(有趣的是,如果只使用第一个表达式,即匹配所有出现的 TRACK_STATUS/VALID 没有指定后续兄弟匹配,它会成功返回一个双精度序列。)

declare function local:get_multi_track_sequence_time( ) as xs:double* {

    let $data as element()* := (

<SEQUENCE><TIME>13.04080</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>INVALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>13.04080</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>INVALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>13.05000</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>13.06900</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>,
<SEQUENCE><TIME>13.06900</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>,
<SEQUENCE><TIME>14.05000</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE></SEQUENCE>,
<SEQUENCE><TIME>15.06700</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>,
<SEQUENCE><TIME>15.06700</TIME><TAG>2900</TAG><FIELD>MULTI_CHAN_IND</FIELD><MODE>SINGLE</MODE></SEQUENCE>

)

    for $entry in $data
    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)
};
4

3 回答 3

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 中有多个 $row 满足条件,您将返回通过data($row/SEQUENCE/TIME)对满足条件的每个 $row 求值形成的结果序列。您的数据的形状可以保证这永远不会发生,但静态分析器不太可能知道这一点。

下面给出的函数的修订形式假设 (a) $msgSeq 是 SEQUENCE 元素的序列,并且 (b) 您想要找到每个 SEQUENCE 元素,它是您描述的事件序列中的第一个事件,并返回它的时间stamp (所以函数作为一个整体返回零个或多个双精度数——我不会问你是什么让你用double它来表示小时和分钟而不是 xs:time 或更合理的东西,这是你和你的工程良心之间的事.

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。

于 2012-09-24T16:39:43.227 回答
0

这引发错误的原因是您的函数没有返回任何内容(空序列),但它被声明为返回一个xs:double. 如果您在Saxon下运行查询,您会收到以下信息更丰富的错误消息:

函数 local:get_multi_track_sequence_time() 的结果不允许空序列

那么下一个问题是,你的函数应该总是返回一个双精度数,还是应该更改as声明以允许它返回一个空序列的可能性?同样,您的查询的编写方式可能会返回多个结果,满足您的where子句的每一行都有一个结果。这也会导致类型错误。你想允许吗?

即使在仅一行满足子句的情况下where,您最终也会返回多个时间戳

return data($row/SEQUENCE/TIME)

因为这会选择每个<TIME>元素的子元素,它是元素<SEQUENCE>的子元素$row。相反,你想要

return data($row/SEQUENCE[1]/TIME)

同样,关于following-sibling::,不要忘记使用[1]表示您正在尝试访问下一个兄弟姐妹,而不仅仅是任何以下兄弟姐妹:

.../following-sibling::SEQUENCE[1][TAG='2900' and FIELD='TRACK_STATUS'
  and MODE='VALID']...

这应该会给您带来更好的性能,并确保您的where条款不会给出误报。

于 2012-09-24T16:30:28.317 回答
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           
};
于 2012-09-27T12:37:43.957 回答