xml - 检测 xml 中的间隙/第一个可用 ID :id sequence with XQuery/Xpath

标签 xml xpath xquery exist-db

我有一个主 xml 文件,其中包含如下列表:

<listPerson>
    <person xml:id="pe0001">
        <persName>
            <surname>Anderson</surname>
            [...]
       </persName>
    </person>
    <person xml:id="pe0002">
        <persName>
            <surname>Smith</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0004">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
</listPerson>

我有一个 html 表单,它调用 app.xql 中的应用程序并插入新的 <person>记录在主 xml 文件中。如果 ID 序列中有间隙(例如上面的 ID pe0003),我希望 eXist-db 返回该 ID 并“填补间隙”,否则仅输出最新的可用 ID(即 pe0005)。我已经完成了最后一件事:

declare function app:addPers($node as node(), $model as map(*)) {

    let $peid := doc('masterfile.xml')//tei:listPerson/tei:person[@xml:id][last()]/@xml:id
    let $idnumber := xs:decimal(substring-after($peid, 'pe'))
    let $newidnumber := (sum($idnumber + 1))
    let $newpeid := concat('pe0', $newidnumber)

    return

<html stuff>

}

我现在想做的是使用 XQuery/Xpath 代码来检测序列中何时存在间隙并采取相应的操作。这是我到目前为止所做的:

[app.xql]

declare function app:addPers($node as node(), $model as map(*)) {

let $seqpe := doc('masterfile.xml')//tei:listPerson/tei:person[@xml:id]/@xml:id
        let $peid := 
        for $item at $pos in $seqpe
            let $item := xs:decimal(substring-after($seqpe, 'pe'))
            return if ($item[$pos + 1] - $item[$pos] != 1) then 
            doc('masterfile.xml')//tei:listPerson/tei:person[@xml:id][$item]/@xml:id
        else 
        doc('masterfile.xml')//tei:listPerson/tei:person[@xml:id][last()]/@xml:id

        let $newidnumber := (sum($peid + 1))
        let $newpeid := concat('pe0', $newidnumber)
 return 

<html stuff>

}

这将返回 err:FORG0001 cannot construct xs:decimal from ""错误。我做错了什么?

更新

这是我所做的另一个测试,它返回 err:XPDY0002 Undefined context sequence for 'following-sibling::tei:person错误:

let $seqpe := doc('masterfile.xml')//tei:listPerson/tei:person
         let $peid := 
         for $item in $seqpe
             return if ((xs:decimal(substring-after(following-sibling::tei:person/@xml:id, 'pe'))) - (xs:decimal(substring-after($item/@xml:id, 'pe'))) ne 1) then 
             doc('masterfile.xml')//tei:listPerson/tei:person[@xml:id eq $item/@xml:id]/@xml:id
         else 
         doc('masterfile.xml')//tei:listPerson/tei:person[@xml:id][last()]/@xml:id

     let $newidnumber := (sum($peid + 1))
     let $newpeid := concat('pe0', $newidnumber)

第二次更新

就返回最后一个 ID 而言,这段代码:

(let $idnext :=

  for $person in doc('/db/apps/app-ct/data/indices/pedb.xml')//tei:listPerson/tei:person[position() ne last()]
  where local:get-id($person/@xml:id) ne (local:get-id($person/following-sibling::tei:person[1]/@xml:id) - 1)
return 
    if (empty($idnext)) then
    (local:get-id(listPerson/person[last()]/@xml:id) + 1)
    else (local:get-id($person/@xml:id) + 1)
let $newpeid := 
if (fn:string-length($idnext) = 1) then
   concat('pe000', $idnext) else if
   (fn:string-length($idnext) = 2) then 
   concat('pe00', $idnext) else if 
   (fn:string-length($idnext) = 3) then 
   concat('pe0', $idnext) else 
   concat('pe', $idnext)

return

<html stuff>)[1]

还有这个:

    (let $idnext :=

      for $person in doc('/db/apps/app-ct/data/indices/pedb.xml')//tei:listPerson/tei:person[position() ne last()]
      where local:get-id($person/@xml:id) ne (local:get-id($person/following-sibling::tei:person[1]/@xml:id) - 1)
      return local:get-id($person/@xml:id) + 1
    return 
        if (empty($idnext)) then
        (local:get-id(listPerson/person[last()]/@xml:id) + 1)
        else ($idnext),
    let $newpeid := 
    if (fn:string-length($idnext) = 1) then
       concat('pe000', $idnext) else if 
       (fn:string-length($idnext) = 2) then 
       concat('pe00', $idnext) else if 
       (fn:string-length($idnext) = 3) then 
       concat('pe0', $idnext) else 
       concat('pe', $idnext)

    return

<html stuff>)[1]

返回err:XPDY0002 variable '$idnext' is not set.错误。

第三次也是最后一次更新

下面的代码完全符合我的要求,即返回第一个可用的 ID,无论它是否在间隙内。

let $id_gap :=

        (for $person in doc('myfile.xml')//tei:listPerson/tei:person[position() ne last()]
        where local:get-id($person/@xml:id) ne (local:get-id($person/following-sibling::tei:person[1]/@xml:id) - 1)
        return (local:get-id($person/@xml:id) + 1))[1]

        let $idnext :=
        if (empty($id_gap))
        then (local:get-id(doc('myfile.xml')//tei:listPerson/tei:person[last()]/@xml:id) + 1)
        else ($id_gap)

        let $newpeid := 
         if (fn:string-length($idnext) = 1) then
            concat('pe000', $idnext) else if 
            (fn:string-length($idnext) = 2) then 
            concat('pe00', $idnext) else if 
            (fn:string-length($idnext) = 3) then 
            concat('pe0', $idnext) else 
            concat('pe', $idnext)

       return

  <html code>

最佳答案

我尝试过这样的:

declare function local:get-id($xml-id as xs:string) as xs:integer {
    xs:integer(replace($xml-id, '[^0-9]+', ''))
};

for $person in (listPerson/person)[position() ne last()]
where local:get-id($person/@xml:id) ne (local:get-id($person/following-sibling::person[1]/@xml:id) - 1)
return local:get-id($person/@xml:id) + 1

以及http://xqueryfiddle.liberty-development.net/nbUY4kh对于示例输入

<listPerson>
    <person xml:id="pe0001">
        <persName>
            <surname>Anderson</surname>
            [...]
       </persName>
    </person>
    <person xml:id="pe0003">
        <persName>
            <surname>Smith</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0004">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0005">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0006">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0008">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0009">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0010">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
    <person xml:id="pe0014">
        <persName>
            <surname>Another</surname>
            [...]
        </persName>
    </person>
</listPerson>

它给出

2
7
11

也可以通过窗口子句来实现,尽管我不确定 Exist-Db 支持这一点。

至于如果没有间隙则返回新的 id,我不确定是否有更优雅或更紧凑的解决方案,但我想一个简单的检查

let $new-ids :=
    for $person in (listPerson/person)[position() ne last()]
    where local:get-id($person/@xml:id) ne (local:get-id($person/following-sibling::person[1]/@xml:id) - 1)
    return local:get-id($person/@xml:id) + 1
return
    if (empty($new-ids))
    then local:get-id(listPerson/person[last()]/@xml:id) + 1
    else $new-ids

实现您的口头描述:http://xqueryfiddle.liberty-development.net/nbUY4kh/2

关于xml - 检测 xml 中的间隙/第一个可用 ID :id sequence with XQuery/Xpath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48881682/

相关文章:

c# - 带有 Saxon API 的 XQuery/XPath - 需要不区分大小写的路径

xml - 使用 saxon xquery 从 xml 中删除元素

xml - 如何使用带有样式表和 xsltproc 的 xslt 从 xml 中删除元素?

java - XSLT 处理巨大的 XML 文件(几乎 5 GB)

java - 我应该把 beans.xml 内容放在哪里(Jdbc Connect with Spring-Eclipse Dynamic webapplication)

html - 包含 2 个或更多 "OR"条件的 XPath 不起作用?

xpath - XPath评估失败

Python:通用 XPATH 上的 Selenium、NoSuchElementException

SQL Server 查询元素值的 xml 属性

javascript - 如何在 iframe 中强制 xml-outlined/pretty 打印?