Использование 'following-sibling' в функции XQuery для возврата элемента TIME при совпадении определенного шаблона

Я пытаюсь написать функцию в 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.

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> в последовательности (т. е. в ВРЕМЯ 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)
};

person mactwixs    schedule 24.09.2012    source источник
comment
Мне кажется, что ваши элементы <SEQUENCE> не являются братьями и сестрами (это отдельные элементы в последовательности), поэтому following-sibling:: не будет работать. Чтобы сделать их братьями и сестрами, оберните их в родительский элемент, например. <root> и уберите запятые между ними.   -  person LarsH    schedule 25.09.2012
comment
Спасибо @LarsH - внесу исправления и посмотрим, что получится   -  person mactwixs    schedule 26.09.2012


Ответы (3)


Вы близки к успеху.

Несколько вещей нужно почистить. Во-первых, комбинация for $row in $msgSeq и some $entry in $row выполняет итерацию по одной и той же последовательности элементов (последовательность, переданная как $msgSeq). Из вашего вопроса неясно, что вы передаете в качестве значения $msgSeq, но мне интересно, имеете ли вы в виду where some $entry in $row/* или (используя неявную экзистенциальную количественную оценку) просто where $row/*/SEQUENCE ....

Во-вторых, ваше описание проблемы предполагает, что вы хотите найти (родители) последовательности из восьми смежных элементов SEQUENCE с определенными свойствами. Но ваше длинное выражение XPath не требует смежности: $foo/following-sibling::SEQUENCE соответствует всем следующим одноуровневым элементам $foo с именем SEQUENCE. Чтобы ограничить путь, чтобы сделать элементы смежными, вам нужно изменить шаги формы

.../following-sibling::SEQUENCE[ ... conditions ... ]

to

.../following-sibling::*[1]/self::SEQUENCE[ ... ]

Если следующий родственный элемент гарантированно будет ПОСЛЕДОВАТЕЛЬНОСТЬЮ, конечно, его можно сделать короче, возможно, за счет потери некоторой ясности.

В-третьих, в вашем объявлении говорится, что вы возвращаете ровно один двойник. Но не гарантируется, что тело функции вернет ровно одно значение типа double, поэтому строгий анализ статического типа пессимистичным процессором вполне может отклонить его. Вещи, которые я вижу в первую очередь, это:

  • Если $row содержит более одного элемента SEQUENCE, то data($row/SEQUENCE/TIME) будет возвращать более одного значения TIME, а не только одно. Если вы совершенно уверены, что все значения ПОСЛЕДОВАТЕЛЬНОСТЬ/ВРЕМЯ будут одинаковыми, добавление [1] — это способ гарантировать, что это выражение возвращает не более одного значения, а не (скажем) восемь или двадцать.

  • Когда ничего не совпадает, ваша функция фактически возвращает пустую последовательность, а не одну последовательность из одного двойника.

  • Если несколько $row в $msgSeq удовлетворяют условию, вы вернете последовательность результатов, сформированную путем вычисления data($row/SEQUENCE/TIME) для каждой $row, удовлетворяющей условию. Форма ваших данных может гарантировать, что этого никогда не произойдет, но статический анализатор вряд ли об этом знает.

Пересмотренная форма вашей функции, приведенная ниже, предполагает (а), что $msgSeq представляет собой последовательность элементов ПОСЛЕДОВАТЕЛЬНОСТИ, и (б) что вы хотите найти каждый элемент ПОСЛЕДОВАТЕЛЬНОСТИ, который является первым событием в последовательности событий, которую вы описываете, и вернуть его время штамп (поэтому функция в целом возвращает ноль или более двойных чисел — я не буду спрашивать вас, что заставило вас использовать 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.

person C. M. Sperberg-McQueen    schedule 24.09.2012
comment
Спасибо @c-m-sperberg-mcqueen за ваши полезные и подробные предложения. У меня все еще были проблемы, поэтому я взял их вместе с первым ответом, чтобы создать автономную тестовую функцию (см. мой исходный вопрос, где он появляется в обновлении) с примерами данных, чтобы проверить совпадения following-sibling::. Ваше предложение работает, если я удаляю совпадения following-sibling::, чтобы функция возвращала только совпадения TRACK_STATUS/VALID. Кстати, у меня нет контроля над форматом времени, поскольку скалярное время - это то, что система предоставляет во входном XML. - person mactwixs; 25.09.2012

Причина, по которой это вызывает ошибку, заключается в том, что ваша функция ничего не возвращает (пустая последовательность), но объявлена ​​как возвращающая 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 не дает ложных срабатываний.

person LarsH    schedule 24.09.2012
comment
Спасибо @LarsH за предложения. Я взял их вместе со вторым ответом, чтобы создать автономную тестовую функцию с примерами данных для проверки совпадений следующих братьев и сестер. - person mactwixs; 25.09.2012

У меня есть следующая рабочая функция, которая дает правильное скалярное время в первом значении в возвращаемой последовательности.

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           
};
person mactwixs    schedule 27.09.2012