XQuery: using global var in function - xquery

I need to use a counter to remember how many node I have dealed with. So I defined a global var $classCounter. For some unknown reasons, I get an error from zorba:
test.xqy>:15,9: error [zerr:XSST0004]: "local:owlClassNameBuilerHelper": function declared nonsequential but has sequential body
I really don't understand what this error means. How to implement a global counter in XQuery?
The whole xqy file is:
declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
declare namespace owl="http://www.w3.org/2002/07/owl#";
declare namespace xsd="http://www.w3.org/2001/XMLSchema#";
declare namespace rdfs="http://www.w3.org/2000/01/rdf-schema#";
import module namespace functx="http://www.functx.com";
declare variable $srcDoc:="test_xsd.xml"; (:need to adjust the input XSD file here:)
declare variable $defaultXMLNS:="http://www.test.com#";
declare variable $defaultXMLBase:=$defaultXMLNS;
declare variable $classCounter:=0;
declare function local:owlClassNameBuilerHelper($pnode as node()*)
as xs:string?
{
$classCounter:=classCounter+1;
let $tmp:=""
return
(
"haha"
(:if(functx:if-empty($pnode/#name, "-1")!="-1") (:if the name attr doesn't exist:)
then data($pnode/ancestor::element[1]/#name) (:get the name attr of first ancestor named element:)
else data($pnode/#name):)
)
};
element rdf:RDF
{
namespace {""} {$defaultXMLNS},
namespace {"owl"} {"http://www.w3.org/2002/07/owl#"},
namespace {"xsd"} {"http://www.w3.org/2001/XMLSchema#"},
namespace {"rdfs"} {"http://www.w3.org/2000/01/rdf-schema#"},
attribute xml:base {$defaultXMLBase}
}
command line:
zorba -i -f -q test.xqy

I need to use a counter to remember how many node I have dealed with.
Firstly, XQuery is a functional programming language. That's a completely different processing model: you can't "remember" what you have "dealt with", because there is no memory and no time dimension. Functions are mathematical functions, they can't have side-effects like updating global variables.
Now, the error message suggests to me that the particular XQuery processor you are using (Zorba) has extensions that allow you to depart from the pure functional programming model; but you are using the extensions incorrectly. In particular, if you want a function to have side-effects then you must declare the function as such. You'll have to look in the Zorba documentation for how to do that, because there is no standard.

Related

Is it possible to pass a uri parameter with xi:include in exist-db?

I have an exist-db .xql page where I am wrapping three other .xql pages via xi:include:
(:==========
Declare namespaces
==========:)
declare namespace mwjl = "http://minorworksoflydgate.net/mwjl";
declare namespace m = "http://minorworksofjohnlydgate.net/model";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare namespace xi = "http://www.w3.org/2001/XInclude";
(:==========
Declare global variables to path
==========:)
declare variable $exist:root as xs:string :=
request:get-parameter("exist:root", "xmldb:exist:///db/apps");
declare variable $exist:controller as xs:string :=
request:get-parameter("exist:controller", "/mwjl-app");
declare variable $path-to-data as xs:string :=
$exist:root || $exist:controller || '/data';
declare variable $docPath as xs:string := "paratext/Lydgate_Main.xml";
(:===
Declare variable
===:)
declare variable $work := doc("/db/apps/mwjl-app/data/" || $docPath);
declare variable $TEI as element(tei:TEI)+ := $work/tei:TEI;
<m:page>
<xi:include href="/db/apps/mwjl-app/modules/header.xql"/>
<xi:include href="/db/apps/mwjl-app/modules/body.xql"/>
<xi:include href="/db/apps/mwjl-app/modules/footer.xql"/>
</m:page>
I would like to pass the variable $docPath to the three xi:include's, but when I format the xi:include as follows (for the footer, for example): <xi:include href="/db/apps/mwjl-app/modules/footer.xql?docPath={$docPath}"/>it ceases to produce results. If put a request:get-parameter statement on the target .xqls and include a fallback (so something like declare variable $docPath as xs:string := request:get-parameter('docPath','fallback') on the target xqls) it generates the fallback fine, so it's obvious to me that the function works. I'm just not forming my href correctly in the xi:include statements in the requesting code. But everything I've read suggests this should be how it's formed and I cannot for the life of me figure out where I'm going wrong. Am I mistaken that this is possible in eXist-db, and if I'm not how do I format the xi:include statement?
I would suggest applying the fn:encode-for-uri() function to the $docPath variable:
<xi:include href="/db/apps/mwjl-app/modules/footer.xql?docPath={encode-for-uri($docPath)}"/>
This function encodes any reserved characters in the supplied string by replacing them with their percent-encoded form. So paratext/Lydgate_Main.xml becomes paratext%2FLydgate_Main.xml.
If this doesn't do the trick (sorry, I haven't tested it), I'd suggest adding logging to the target queries, e.g.:
util:log("INFO", "docPath: " || request:get-parameter('docPath', 'no docPath parameter received'))
... which will be visible in your exist.log file when you next call your main query. (And then, if you could add a comment to this reply with, I'll update my suggestion accordingly.)

eXist-db - include template results in mismatched cardinality, rejection as binary resource

Environment: eXist-db 4.2.1 , XQuery 3.1
I have built a number functions in eXist which output HTML results fine as functions on their own (when I pass the parameters directly). As an example, this little function (document:doc-sidebar-sub4 in /modules/document.xql):
module namespace document="/db/apps/fooapp/modules/document";
declare namespace templates="http://exist-db.org/xquery/templates";
import module namespace common="/db/apps/fooapp/modules/common" at "/db/apps/fooapp/modules/common.xql";
declare function document:doc-sidebar-sub4(
$node as node(),
$model as map(*),
$currentdoc as xs:string)
{
let $sidebar :=
(<div class="sidebar-sub-4">
<p><span class="en">To cite this document:</span></p>
<p>{common:cite-doc($currentdoc,"html")}</p>
</div>)
return $sidebar
};
Produces a little snippet of HTML derived from document foo-doc_0099.xml (by calling another function):
<div class="sidebar-sub-4">
<p><span class="en">To cite this document:</span></p>
<p><span>Joe Smith. <i>Digital Edition of some old manuscript</i>.
http://www.foosite.fr/foo-doc_0099.xml. Retrieved 2018-10-14.</span>
</p>
</div>
I am now trying to include this snippet in my main document.html (with document hard coded in the parameter - eventually to be got from HTTP request) with this div:
<div data-template="templates:include"
data-template-with="document:doc-sidebar-sub4"
data-template-currentdoc="foo-doc_0099.xml"/>
But it produces the following error:
exerr:ERROR XPTY0004: The actual cardinality for parameter 3 does not match the cardinality declared in the function's signature: templates:include($node as node(), $model as map, $path as xs:string) item()*. Expected cardinality: exactly one, got 0. [at line 219, column 14, source: /Users/foo/Library/Application Support/org.exist/expathrepo/shared-0.4.2/content/templates.xql]
Another version of the same template call produces a different error:
<div data-template="templates:include"
data-template-with="document:doc-sidebar-sub4"
data-template-path="modules/document.xql"
data-template-currentdoc="foo-doc_0099.xml"/>
Error:
err:FODC0005 exerr:ERROR Document /db/apps/fooapp/modules/document.xql is a binary resource, not an XML document. Please consider using the function util:binary-doc() to retrieve a reference to it. [at line 436, column 27, source: /Users/foou/Library/Application Support/org.exist/expathrepo/shared-0.4.2/content/templates.xql]
I tried to follow the demos, various tutorials, and answers including this, but can't seem to identify the problem.
Another version, different error. This call
<div data-template="document:doc-sidebar-sub4" data-template-path="modules/document.xql":>
Produces:
templates:NotFound No template function found for call document:doc-sidebar-sub4 [at line 189, column 85, source: /Users/foo/Library/Application Support/org.exist/expathrepo/shared-0.4.2/content/templates.xql]
Many thanks in advance for any help for this learner.
Edit: added adjustments from #joewiz, other errors is returned
Functions that you call via eXist's HTML templating facility must have two required parameters: $node as node(), $model as map(*), before any named parameters like your $currentdoc parameter. To fix your problem, you need to add these two parameters to your function signature, as follows:
declare function document:doc-sidebar-sub4(
$node as node(),
$model as map(*),
$currentdoc as xs:string
)
{
let $sidebar :=
(<div class="sidebar-sub-4">
<p><span class="en">To cite this document:</span></p>
<p>{common:cite-doc($currentdoc,"html")}</p>
</div>)
return $sidebar
};
The section of the Templating documentation on "Templating Functions" at https://exist-db.org/exist/apps/doc/templating#D3.19 explains what these two required parameters are for.
Putting a full answer here for future search engine indexing. The errors being produced were:
exerr:ERROR XPTY0004: The actual cardinality for parameter 3 does not match the cardinality declared in the function's signature
err:FODC0005 exerr:ERROR Document ..... is a binary resource, not an XML document.
templates:NotFound No template function found for call
To solve this, the function looks like this:
module namespace document="/db/apps/fooapp/modules/document";
declare namespace templates="http://exist-db.org/xquery/templates";
import module namespace common="/db/apps/fooapp/modules/common" at "/db/apps/fooapp/modules/common.xql";
declare function document:doc-sidebar-sub4(
$node as node(),
$model as map(*),
$currentdoc as xs:string)
{
let $sidebar :=
(<div class="sidebar-sub-4">
<p><span class="en">To cite this document:</span></p>
<p>{common:cite-doc($currentdoc,"html")}</p>
</div>)
return $sidebar
};
The template call is quite simple (includes parameter if required):
<div data-template="document:doc-sidebar-sub4" data-template-currentdoc="foo-doc.xml"/>
But for this to work, the module needs to be imported into the module view.xql:
import module namespace document="/db/apps/fooapp/modules/document" at "/db/apps/fooapp/modules/document.xql";

XQuery Nested loop error

I'm using OSM 7.2.0.3 and I have cartridge with an Order Recognition Rule, with its Order Data Rule (inside Transformation tab)
In the ODR I have this XQuery code:
declare namespace im="http://xxx";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare variable $order := fn:root(.)/im:Order;
<_root>
<Order>
{
for $moli in $order/MainOrderLineItem
return {
<OrderLineItem>
{$moli/LineItemAttributeInfo/LineItemAttribute}
</OrderLineItem>
{
for $oli in $moli/OrderLineItem
return
<OrderLineItem>
{$oli/LineItemAttributeInfo/LineItemAttribute}
</OrderLineItem>
}
}
}
</Order>
</_root>
There's no compile error in OSM, but on runtime I get:
Invalid Order Specification Fault
Order data expression failed due to oracle.communications.ordermanagement.rule.XMLRuleException
I run the OSM by submitting an XML through Web Service.
Thanks a lot for your replies.
The returned XML should actually be in parentheses instead of braces. There also needs to be a comma between the first returned OrderLineItem element and the FLWOR expression instead of wrapping it in braces:
declare namespace im="http://xxx";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare variable $order := fn:root(.)/im:Order;
<_root>
<Order>{
for $moli in $order/MainOrderLineItem
return (
<OrderLineItem>
{$moli/LineItemAttributeInfo/LineItemAttribute}
</OrderLineItem>,
for $oli in $moli/OrderLineItem
return
<OrderLineItem>
{$oli/LineItemAttributeInfo/LineItemAttribute}
</OrderLineItem>
)
}</Order>
</_root>

XQuery node is sequence

Is is-node-in-sequence-deep-equal in XQuery? I'm wondering because I've seen the function at xqueryfunctions.com, but I'm not being able to use it.
That function is part of the FunctX XQuery library. There are two methods to use this function:
You download the whole library (select the download which corresponds to your version of XQuery), save it in the same directory as your XQuery program/file and then import the module in your XQuery file, e.g.:
import module namespace functx = "http://www.functx.com" at "functx-1.0-doc-2007-01.xq";
(: Insert your code here and call the is-node-in-sequence-deep-equal function as seen below :)
functx:is-node-in-sequence-deep-equal($node, $seq)
Instead of downloading the whole library with all functions you can also simply copy paste the specific function which you need as shown on the page you linked to:
declare namespace functx = "http://www.functx.com";
declare function functx:is-node-in-sequence-deep-equal
( $node as node()? ,
$seq as node()* ) as xs:boolean {
some $nodeInSeq in $seq satisfies deep-equal($nodeInSeq,$node)
} ;
(: Insert your code here and call the 'is-node-in-sequence-deep-equal' function as seen below :)
functx:is-node-in-sequence-deep-equal($node, $seq)
In both examples you simply replace $node and $seq with your variables.

To remove the node but keep the value inside intact through XQuery

I have a content.xml modelled as below
<root>
<childnode>
Some text here
</childnode>
</root>
I am trying to remove the <childnode> and update the content.xml with only the value of it
so the output looks like
<root>
Some Text here
</root>
I wrote a function to perform this but anytime I run it it gives me error as "unexpected token: modify". I was thinking of a way to accomplish this without using functx functions.
xquery version "1.0";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace file="http://exist-db.org/xquery/file";
declare namespace system="http://exist-db.org/xquery/system";
declare namespace util="http://exist-db.org/xquery/util";
declare namespace response="http://exist-db.org/xquery/response";
declare function local:contentUpdate() {
let $root := collection('/lib/repository/content')//root/childNode
let $rmChild := for $child in $root
modify
(
return rename node $child as ''
)
};
local:updateTitle()
Thanks in advance
There are multiple problems with your query:
Updating functions must be declared as updating.
You're calling another function than you defined (probably you didn't notice as there still have been syntax errors).
Rename node expects some element (or processing instruction, attribute) as target, the empty string is not allowed.
At least BaseX doesn't allow updating statements when defining code as XQuery 1.0. Maybe exist doesn't care about this, try adding it if you need to know.
You do not want to rename, but replace all <childnode />s with its contents, use replace node.
This code fixes all these problems:
declare updating function local:contentUpdate() {
let $root := collection('/lib/repository/content')
return
for $i in $root//childnode
return
replace node $i with $i/data()
};
local:contentUpdate()
eXist-db's XQuery Update syntax is documented at http://exist-db.org/exist/update_ext.xml. Note that this syntax predates the release of the XQuery Update Facility 1.0, so the syntax is different and remains unique to eXist-db.
The way to do what you want in eXist-db is as follows:
xquery version "1.0";
declare function local:contentUpdate() {
let $root := doc('/db/lib/repository/content/content.xml')/root
return
update value $root with $root/string()
};
local:contentUpdate()
The primary changes, compared to your original code, are:
Inserted the eXist-db syntax for your update
Prepended '/db' to your collection name, as /db is the root of the database in eXist-db; replaced the collection() call with a doc() call, since you stated you were operating on a single file, content.xml
Changed //root to /root, since "root" is the root element, so the // (descendant-or-self) axis is extraneous
Replaced updateTitle() with the actual name of the function, contentUpdate
Removed the extraneous namespace declarations
For more on why I used $root/string(), see http://community.marklogic.com/blog/text-is-a-code-smell.

Resources