การใช้ '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 ที่สามารถตั้งค่าจุดพักและแก้ไขข้อบกพร่องนี้ได้ - ฉันคิดว่าฉันไม่สามารถใช้ follow-sibling ได้เมื่อฉันเลือกรายการที่มี 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 ในข้อมูลทดสอบ)

(ที่น่าสนใจคือสามารถส่งคืนลำดับของ doubles ได้สำเร็จหากใช้เฉพาะนิพจน์แรกเท่านั้น เช่น จับคู่ 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[ ... ]

หากลำดับข้างเคียงต่อไปนี้ได้รับการรับรองว่าเป็นลำดับ แน่นอนว่าอาจทำให้สั้นลงได้ โดยอาจทำให้สูญเสียความชัดเจนไปบ้าง

ประการที่สาม การประกาศของคุณบอกว่าคุณจะคืนสองเท่าอย่างแน่นอน แต่เนื้อหาของฟังก์ชันไม่รับประกันว่าจะส่งคืน 1 double อย่างแน่นอน ดังนั้นการวิเคราะห์ประเภทคงที่ที่เข้มงวดโดยโปรเซสเซอร์ที่มองโลกในแง่ร้ายอาจปฏิเสธได้ สิ่งที่ฉันเห็นเป็นอันดับแรกคือ:

  • หาก $row มีองค์ประกอบ SEQUENCE มากกว่าหนึ่งรายการ ดังนั้น data($row/SEQUENCE/TIME) จะส่งกลับค่า TIME มากกว่าหนึ่งค่า ไม่ใช่เพียงค่าเดียว หากคุณค่อนข้างมั่นใจว่าค่า SEQUENCE/TIME ทั้งหมดจะเหมือนกัน การเพิ่ม [1] เป็นวิธีที่จะทำให้นิพจน์นี้ส่งคืนค่าได้มากที่สุดเพียงค่าเดียว ไม่ใช่ (พูด) แปดหรือยี่สิบ

  • เมื่อไม่มีสิ่งใดตรงกัน ฟังก์ชันของคุณจะส่งคืนลำดับว่าง ไม่ใช่ลำดับเดียวของหนึ่งคู่

  • หากมี $row มากกว่าหนึ่งรายการใน $msgSeq ตรงตามเงื่อนไข คุณจะส่งคืนลำดับผลลัพธ์ที่เกิดขึ้นโดยการประเมิน 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

person C. M. Sperberg-McQueen    schedule 24.09.2012
comment
ขอบคุณ @ c-m-sperberg-mcqueen สำหรับคำแนะนำที่เป็นประโยชน์และละเอียดของคุณ ฉันยังคงประสบปัญหาอยู่ ดังนั้นจึงได้นำสิ่งเหล่านี้ร่วมกับคำตอบแรกเพื่อสร้างฟังก์ชันการทดสอบในตัว (ดูคำถามเดิมของฉันที่ปรากฏในการอัปเดต) พร้อมข้อมูลตัวอย่าง เพื่อทดสอบการจับคู่ following-sibling:: คำแนะนำของคุณใช้งานได้หากฉันตัดการจับคู่ following-sibling:: ออก เพื่อให้ฟังก์ชันส่งคืนเฉพาะการจับคู่ TRACK_STATUS/VALID BTW - ฉันไม่สามารถควบคุมรูปแบบเวลาได้ เนื่องจากเวลาสเกลาร์คือสิ่งที่ระบบจัดเตรียมไว้ใน 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