Skip to content

Instantly share code, notes, and snippets.

@lguariento
Last active May 31, 2018 09:11
Show Gist options
  • Save lguariento/8129477a4501faf617d28d385e6aeee7 to your computer and use it in GitHub Desktop.
Save lguariento/8129477a4501faf617d28d385e6aeee7 to your computer and use it in GitHub Desktop.
Cardinality error
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Add a person</title>
</head>
<body>
<div class="templates:surround?with=templates/page.html&amp;at=content">
<div data-template="app:addPers"/>
</div>
</body>
</html>
xquery version "3.1";
module namespace app="http://editor.curioustravellers.ac.uk/templates";
import module namespace templates="http://exist-db.org/xquery/templates" ;
import module namespace config="http://editor.curioustravellers.ac.uk/config" at "config.xqm";
declare namespace tei="http://www.tei-c.org/ns/1.0";
declare namespace functx = 'http://www.functx.com';
declare option exist:serialize "method=html comedia-type=text/html";
declare function local:get-id($xml-id as xs:string) as xs:integer {
xs:integer(replace($xml-id, '[^0-9]+', ''))
};
declare function app:listpers($node as node(), $model as map(*)) {
for $person in doc('/db/apps/app-ct/data/indices/pedb.xml')//tei:listPerson/tei:person
return
<tr>
<td>{$person/tei:persName/tei:surname}</td>
<td>{$person/tei:persName/tei:forename}</td>
</tr>
};
declare function app:addPers($node as node(), $model as map(*)) {
let $peid := doc('/db/apps/app-ct/data/indices/pedb.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)
(: This returns the first available id, even if it's within a gap:)
let $id_gap :=
(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))[1]
(: This does the same, although it takes way longer to evaluate than the above solution:
(for $key in (1 to 9999)!format-number(., '0000')
let $pei := concat('pe', $key)
where empty(doc('/db/apps/app-ct/data/indices/pedb.xml')//tei:listPerson/tei:person[@xml:id=$pei])
return $key)[1] :)
let $idnext :=
if (empty($id_gap)) then
(local:get-id(doc('/db/apps/app-ct/data/indices/pedb.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
<div class="container">
<form action="persadded.html" target="persadded" method="POST">
<h1 style="text-align:center;">Add a person</h1>
<div class="form-group col-md-3">
<label for="newpeid">ID:</label>
<input readonly="readonly" class="form-control" type="text" name="newpeid" value="{$newpeid}"/>
<br/>
<label for="surname">Surname:</label>
<input class="form-control" type="text" name="surname" placeholder="surname"/>
<br/>
<label for="forename">Forename:</label>
<input class="form-control" type="text" name="forename" placeholder="forename"/>
<br/>
</div>
<div class="col-md-9">
<br/>
<iframe scrolling="no" style="border: none; width:100%; height: 350px;" name="persadded"></iframe>
</div>
<br/>
<input class="btn btn-primary btn-lg" id="submit" type="submit" value="Submit"/>
</div>
<br/>
</form>
</div>
};
declare function app:persadded($node as node(), $model as map(*)) {
let $newpeid := request:get-parameter('newpeid', '')
let $surname := request:get-parameter('surname', '')
let $forename := request:get-parameter('forename', '')
let $on-disk := doc('/db/apps/app-ct/data/indices/pedb.xml')
let $newrecord :=
<person xmlns="http://www.tei-c.org/ns/1.0" xml:id="{xs:ID($newpeid)}" >
<persName>
<surname>{$surname}</surname>
<forename>{$forename}</forename>
</persName>
</person>
let $previous := local:get-id($newpeid) - 1
let $previd :=
if (fn:string-length($previous) = 1) then
concat('pe000', $previous) else if
(fn:string-length($previous) = 2) then
concat('pe00', $previous) else if
(fn:string-length($previous) = 3) then
concat('pe0', $previous) else
concat('pe', $previous)
let $insert := update insert $newrecord following $on-disk//tei:person[@xml:id eq $previd]
return
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Added {$newpeid}</title>
</head>
<body>
<div style="background:floralwhite; text-align:center; border-radius: 80px; overflow: hidden;">
<br />
<h3>You have just added {($surname, $forename)} to the database</h3>
</div>
</body>
</html>
};
<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?><?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml"
schematypens="http://purl.oclc.org/dsdl/schematron"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="pldb">
<teiHeader>
<fileDesc>
<titleStmt>
<title>Database of places</title>
</titleStmt>
<publicationStmt>
<ab/>
</publicationStmt>
<sourceDesc>
<ab/>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<body>
<listPerson>
<person xml:id="pe0001">
<persName>
<surname>Person</surname>
<forename>One</forename>
</persName>
</person>
<person xml:id="pe0007">
<persName>
<surname>Person 2</surname>
<forename/>
</persName>
</person>
<person xml:id="pe0009">
<persName>
<surname>Person</surname>
<forename>Three</forename>
</persName>
</person>
<person xml:id="pe0011">
<persName>
<surname>Person</surname>
<forename>Four</forename>
</persName>
</person>
</listPerson>
</body>
</text>
</TEI>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Person added</title>
</head>
<body>
<div data-template="app:persadded"/>
</body>
</html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Persons list</title>
</head>
<body id="body">
<div class="templates:surround?with=templates/page.html&amp;at=content">
<h1 style="text-align:center;">Persons list</h1>
<p>
<br/>
<br/>
<a style="font-size: 18px;" class="btn btn-primary btn-block" href="addnewperson.html">
<span aria-hidden="true"/> Add a new person
</a>
</p>
<table id="persons" class="display table table-hover table-bordered">
<thead>
<tr>
<th class="col-md-1">ID<br/>
</th>
<th class="col-md-11">Surname<br/>
</th>
</tr>
</thead>
<tbody data-template="app:listpers"/>
</table>
</div>
</body>
</html>
@lguariento
Copy link
Author

lguariento commented May 31, 2018

The persons.html page shows a table with all the persons in the pedb.xml.
The addnewpersons.html calls the app:addPers, which iterates in the xml:ids in pedb.xml and, if it finds a 'gap', returns the first available xml:id (in this case pe0002). Once the form is filled and submitted, a new record is created (e.g. pe0002). Now, if we go to persons.html we don't see the new person added, even if it is actually in the pedb.xml file. One needs to force a refresh of the page, or append a cache-busting parameter to the address (e.g. persons.html?rand=345345345). Then the new person is shown.

Moreover, if we go to addnewperson.html again immediately after pe0002 has been added, one gets this error (line 855 is where where local:get-id($person/@xml:id) ne (local:get-id($person/following-sibling::tei:person[1]/@xml:id) - 1) is):

exerr:ERROR XPTY0004: The actual cardinality for parameter 1 does not match the cardinality declared in the function's signature: local:get-id($xml-id as xs:string) xs:integer. Expected cardinality: exactly one, got 0. [at line 855, column 63, source: /db/apps/app-ct-ed/modules/app.xql]
In function:
	local:get-id(xs:string) [855:49:/db/apps/app-ct-ed/modules/app.xql]
	app:addPers(node(), map(*)) [-1:-1:/db/apps/app-ct-ed/modules/app.xql]
	templates:process-output(element(), map(*), item()*, element()) [205:9:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:call-by-introspection(element(), map(*), map(*), function(*)) [187:28:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:call(item(), element(), map(*)) [135:36:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [146:81:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [146:81:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [146:81:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [146:81:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [420:17:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process-output(element(), map(*), item()*) [224:9:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process-output(element(), map(*), item()*, element()) [205:9:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:call-by-introspection(element(), map(*), map(*), function(*)) [187:28:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:call(item(), element(), map(*)) [143:37:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [146:81:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [146:81:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [131:51:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:process(node()*, map(*)) [88:9:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]
	templates:apply(node()+, function(*), map(*)?, map(*)?) [45:5:/root/eXist-db/./webapp/WEB-INF/data/expathrepo/shared-0.5.0/content/templates.xql]

If the $id_gap code is commented in the app.xql and we just use $idnext := (local:get-id(doc('/db/apps/app-ct/data/indices/pedb.xml')//tei:listPerson/tei:person[last()]/@xml:id) + 1), i.e. if we just use the next to the last available xml:id, then the cardinality error disappears. The issue with the newly added record not shown in persons.html unless cache-busting triks are used still remains, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment