使用 XQuery/Xpath 检测 xml:id 序列中可用的间隙/第一个 ID
Detect a gap / the first ID available in a xml:id sequence with XQuery/Xpath
我有一个主 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 中调用一个应用程序并在主 xml 文件中插入一个新的 <person>
记录。如果 ID 序列中存在间隙(例如上面的 ID pe0003),我希望 eXist-db return 该 ID 和 'fill the gap',否则只输出最新的可用 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>
}
这 return 是一个 err:FORG0001 cannot construct xs:decimal from ""
错误。我做错了什么?
更新
这是我做的另一个测试,return是一个 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)
第二次更新
就 return 最后一个 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]
return 一个 err:XPDY0002 variable '$idnext' is not set.
错误。
第三次也是最后一次更新
下面的代码完全符合我的要求,即 return 第一个可用的 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
它也可以通过 window 子句来实现,尽管我不确定 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
另一种方法:
(for $key in (1 to 9999)!format-number(., '0000')
where empty($persons[@xml:id=$key])
return $key)[1]
获取 1 到 9999 范围内的第一个数字 NNNN,其中 $persons 中没有 xml:id 等于 peNNNN 的元素。
我有一个主 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 中调用一个应用程序并在主 xml 文件中插入一个新的 <person>
记录。如果 ID 序列中存在间隙(例如上面的 ID pe0003),我希望 eXist-db return 该 ID 和 'fill the gap',否则只输出最新的可用 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>
}
这 return 是一个 err:FORG0001 cannot construct xs:decimal from ""
错误。我做错了什么?
更新
这是我做的另一个测试,return是一个 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)
第二次更新
就 return 最后一个 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]
return 一个 err:XPDY0002 variable '$idnext' is not set.
错误。
第三次也是最后一次更新
下面的代码完全符合我的要求,即 return 第一个可用的 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
它也可以通过 window 子句来实现,尽管我不确定 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
另一种方法:
(for $key in (1 to 9999)!format-number(., '0000')
where empty($persons[@xml:id=$key])
return $key)[1]
获取 1 到 9999 范围内的第一个数字 NNNN,其中 $persons 中没有 xml:id 等于 peNNNN 的元素。