Penggunaan 'saudara berikut' dalam fungsi XQuery untuk mengembalikan elemen TIME jika pola tertentu cocok

Saya mencoba menulis fungsi di XQuery yang mengembalikan stempel waktu dari urutan data XML jika pola nilai tertentu terdeteksi. Data sebenarnya adalah log pengujian pesan api sistem

Contoh data XML terlihat serupa dengan cuplikan di bawah. Jika urutannya ditemukan maka diasumsikan bahwa stempel waktu (tag TIME) akan sama untuk setiap baris entri pola.

Pola spesifik yang saya perlukan untuk mendeteksi & mengembalikan TIME dari - adalah di mana ada empat entri <FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE> secara berurutan diikuti langsung oleh empat entri <FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE> secara berurutan - semuanya dengan stempel waktu yang sama.

<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>

Fungsi yang saya coba definisikan adalah sebagai berikut tetapi memberikan kesalahan run time dengan 'empty sequence not allowed'. Sayangnya saya tidak memiliki IDE di mana saya dapat menetapkan titik istirahat dan men-debug ini - saya pikir saya tidak dapat menggunakan saudara berikut setelah saya memilih entri dengan 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)
};

Terima kasih. Saya relatif baru di XQuery.

---------------------EDIT - TAMBAHKAN FUNGSI UJI DENGAN IDE DARI SARAN------------- --------

Terima kasih atas saran yang sudah diterima. Saya telah menulis fungsi pengujian mandiri berikut berdasarkan informasi bermanfaat yang diberikan - fungsi tersebut tidak dapat cocok dengan fungsi saudara berikutnya.

Fungsi tersebut membuat variabel data yang berisi urutan pengujian. Fungsinya mengembalikan urutan kosong. Persyaratannya adalah ia mengembalikan 14.050000 untuk menunjukkan skalar TIME yang memiliki empat entri <FIELD>TRACK_STATUS</FIELD><MODE>VALID</MODE> secara berurutan diikuti langsung oleh empat entri <FIELD>MULTI_CHAN_IND</FIELD><MODE>MULTI</MODE> secara berurutan (yaitu pada TIME 14.050000 dalam data pengujian).

(Menariknya, ini berhasil mengembalikan urutan ganda jika hanya ekspresi pertama yang digunakan, yaitu cocok dengan semua kemunculan TRACK_STATUS/VALID, tidak ada kecocokan saudara berikutnya yang ditentukan.)

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 sumber
comment
Tampak bagi saya bahwa elemen <SEQUENCE> Anda bukan saudara kandung (mereka adalah item terpisah secara berurutan), jadi following-sibling:: tidak akan berfungsi. Untuk menjadikannya saudara kandung, gabungkan mereka dalam elemen induk, mis. <root>, dan hapus koma di antara keduanya.   -  person LarsH    schedule 25.09.2012
comment
Terima kasih @LarsH - akan melakukan koreksi dan melihat apa yang terjadi   -  person mactwixs    schedule 26.09.2012


Jawaban (3)


Anda hampir berhasil.

Ada beberapa hal yang perlu dibersihkan. Pertama, kombinasi for $row in $msgSeq dan some $entry in $row melakukan iterasi pada urutan elemen yang sama (urutan diteruskan sebagai $msgSeq). Tidak jelas dari pertanyaan Anda apa yang Anda berikan sebagai nilai $msgSeq, tapi saya ingin tahu apakah yang Anda maksud adalah where some $entry in $row/* atau (mengeksploitasi kuantifikasi eksistensial implisit) hanya where $row/*/SEQUENCE ....

Kedua, uraian masalah Anda menyarankan Anda ingin mencari (orang tua dari) barisan delapan elemen SEQUENCE yang berdekatan dengan properti tertentu. Namun ekspresi XPath panjang Anda tidak memerlukan kedekatan: $foo/following-sibling::SEQUENCE cocok dengan semua saudara kandung $foo bernama SEQUENCE. Untuk membatasi jalur agar item berdekatan, Anda perlu mengubah langkah-langkah formulir

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

to

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

Jika saudara kandung berikut dijamin menjadi URUTAN, tentu saja, ini bisa dibuat lebih pendek, dengan kemungkinan kehilangan kejelasan.

Ketiga, pernyataan Anda menyatakan bahwa Anda mengembalikan tepat satu ganda. Namun isi fungsi tidak dijamin akan menghasilkan tepat satu ganda, jadi analisis tipe statis yang ketat oleh prosesor yang pesimistis mungkin akan menolaknya. Hal-hal yang pertama kali saya lihat adalah:

  • Jika $row berisi lebih dari satu elemen SEQUENCE, maka data($row/SEQUENCE/TIME) akan mengembalikan lebih dari satu nilai TIME, bukan hanya satu. Jika Anda cukup yakin bahwa semua nilai URUTAN/WAKTU akan sama, menambahkan [1] adalah cara untuk memastikan ekspresi ini mengembalikan paling banyak satu nilai, bukan (katakanlah) delapan atau dua puluh.

  • Ketika tidak ada yang cocok, fungsi Anda sebenarnya mengembalikan urutan kosong, bukan satu urutan ganda.

  • Jika lebih dari satu $row di $msgSeq memenuhi ketentuan, Anda akan mengembalikan urutan hasil yang dibentuk dengan mengevaluasi data($row/SEQUENCE/TIME) untuk setiap $row yang memenuhi ketentuan. Bentuk data Anda mungkin menjamin bahwa hal ini tidak akan pernah terjadi, namun penganalisis statis kemungkinan tidak akan mengetahuinya.

Bentuk revisi fungsi Anda yang diberikan di bawah ini mengasumsikan (a) bahwa $msgSeq adalah rangkaian elemen SEQUENCE, dan (b) Anda ingin menemukan setiap elemen SEQUENCE yang merupakan peristiwa pertama dalam rangkaian peristiwa yang Anda gambarkan, dan mengembalikan waktunya stempel (jadi fungsi secara keseluruhan menghasilkan nol atau lebih ganda -- Saya tidak akan menanyakan apa yang merasuki Anda untuk menggunakan double sebagai representasi jam dan menit alih-alih xs:time atau sesuatu yang lebih masuk akal, itu antara Anda dan teknik Anda hati nurani.

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)
};

Ketika urutan elemen SEQUENCE yang ditunjukkan dalam pertanyaan diteruskan ke fungsi ini, ia mengembalikan angka 14.05.

person C. M. Sperberg-McQueen    schedule 24.09.2012
comment
Terima kasih @ c-m-sperberg-mcqueen atas saran Anda yang bermanfaat & mendetail. Saya masih mengalami masalah jadi ambil ini bersamaan dengan jawaban pertama untuk menghasilkan fungsi pengujian mandiri (lihat pertanyaan awal saya yang muncul di pembaruan), dengan data sampel, untuk menguji following-sibling:: kecocokan. Saran Anda berfungsi jika saya menghapus following-sibling:: kecocokan sehingga fungsinya hanya mengembalikan TRACK_STATUS/VALID kecocokan. BTW - Saya tidak punya kendali atas format waktu karena waktu skalar adalah apa yang disediakan sistem dalam input XML. - person mactwixs; 25.09.2012

Alasan terjadinya kesalahan adalah karena fungsi Anda tidak mengembalikan apa pun (urutan kosong), tetapi dinyatakan mengembalikan xs:double. Jika Anda menjalankan kueri di bawah Saxon, Anda akan mendapatkan pesan kesalahan yang lebih informatif ini:

Urutan kosong tidak diperbolehkan sebagai hasil dari fungsi local:get_multi_track_sequence_time()

Jadi pertanyaan selanjutnya adalah, haruskah fungsi Anda selalu mengembalikan nilai ganda, atau haruskah Anda mengubah deklarasi as untuk memungkinkan kemungkinan bahwa fungsi tersebut akan mengembalikan urutan kosong? Demikian pula, cara kueri Anda ditulis, dapat menghasilkan beberapa hasil, satu untuk setiap baris yang memenuhi klausa where Anda. Itu akan menyebabkan kesalahan ketik juga. Apakah Anda ingin mengizinkannya?

Bahkan jika klausa where hanya dipenuhi untuk satu baris, Anda akan mengembalikan beberapa stempel waktu dengan

return data($row/SEQUENCE/TIME)

karena ini memilih setiap elemen <TIME> yang merupakan anak dari <SEQUENCE> yang merupakan anak dari elemen di $row. Sebaliknya, Anda menginginkannya

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

Demikian pula mengenai following-sibling::, jangan lupa gunakan [1] untuk menunjukkan bahwa Anda mencoba mengakses saudara berikutnya, bukan sembarang saudara berikut:

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

Itu akan memberi Anda kinerja yang lebih baik, serta memastikan klausa where Anda tidak memberikan hasil positif palsu.

person LarsH    schedule 24.09.2012
comment
Terima kasih @LarsH atas sarannya. Saya telah mengambil ini bersamaan dengan jawaban kedua untuk menghasilkan fungsi pengujian mandiri, dengan data sampel, untuk menguji kecocokan saudara berikut. - person mactwixs; 25.09.2012

Saya memiliki fungsi kerja berikut yang memberikan waktu skalar yang benar pada nilai pertama dalam urutan yang dikembalikan.

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