How to declare array in xpath query using oracle - oracle

I am trying to create an array in XPath query using oracle, but it is treating as a string, below is the code snippet, can someone please help me where I am doing wrong.
XMLQUERY('
let $vals :=
if (count($Cntnt/emp/emp_content/emp_part)=1) then
("1")
else if (count($Cntnt/emp/emp_content/emp_part)=2) then
("00","01","10","11")
else if (count($Cntnt/emp/emp_content/emp_part)==3) then
("111","110","101","100","011","010","001","000")
else()
for $val in $vals
return concat ($val,"")
'
PASSING emp_xml AS "Cntnt" RETURNING CONTENT).getStringVal() as tst.

Related

eXist-DB / XQuery StringValue cannot be cast to AnyURIValue (using compression:zip)

In eXist 4.4/XQuery 3.1, I am building a function to compress a number of xml files into a zip using compression:zip.
I have one function which collects all the URIs for the documents to be compressed, schedule:get-document-uris-for-zip(xmlid as xs:string). This function returns lists of documents like the following:
/db/apps/deheresi/data/MS609-0001.xml
/db/apps/deheresi/data/MS609-0002.xml
/db/apps/deheresi/data/MS609-0003.xml
/db/apps/deheresi/data/MS609-0004.xml
/db/apps/deheresi/data/MS609-0005.xml
/db/apps/deheresi/data/MS609-0006.xml
/db/apps/deheresi/data/MS609-0007.xml
/db/apps/deheresi/data/MS609-0008.xml
/db/apps/deheresi/data/MS609-0009.xml
/db/apps/deheresi/data/MS609-0010.xml
This function is called by the compression function as follows
declare function schedule:create-zip-by-batch()
{
let $batch := doc(concat($globalvar:URIdocuments,"document_collections.xml"))
for $entry in $batch//collection[#compile="y"]
let $zipobject := compression:zip(schedule:get-document-uris-for-zip($entry/string(#xml:id)),false())
let $zipstore := xmldb:store("/db/apps/deheresi/documents",
"MS609_tei.zip",
$zipobject)
return $zipstore
};
This is throwing a cast error as follows, but I can't identify how to resolve this...
org.exist.xquery.value.StringValue cannot be cast to org.exist.xquery.value.AnyURIValue
Many thanks in advance.
Edit - I'm adding here the part of the function schedule:get-document-uris-for-zip(xmlid as xs:string) which outputs the list of URIs. The URIs are built through string concatenation:
(: get names of documents which meet criteria :)
let $list := xmldb:get-child-resources("/db/apps/deheresi/data")[starts-with(., $y/string(#filename)) and ends-with(., $y/string(#ext))]
(: create URI for each document :)
return
for $n in $list
return concat("/db/apps/deheresi/data/",$n)
You're right to find this function a bit confusing. The (eXist-specific) compression:zip() function $sources parameter is typed as if it is quite flexible way, as xs:anyType()+. But really it is quite strict about the two types of item it accepts: a sequence of URIs (i.e., of type xs:anyURI), or a sequence of <entry> elements:
<entry name="filename.ext"
type="collection|uri|binary|xml|text"
method="deflate|store"
>data</entry>
See https://exist-db.org/exist/apps/fundocs/view.html?uri=http://exist-db.org/xquery/compression#zip.2.
The problem with your code is that you are passing strings in your $sources parameter, and have not cast these strings as xs:anyURI.
Here is sample working code:
xquery version "3.1";
let $prepare :=
(
xmldb:create-collection("/db", "test"),
xmldb:store("/db/test", "test.xml", <test/>)
)
let $zip := compression:zip("/db/test/test.xml" cast as xs:anyURI, false())
return
xmldb:store("/db/test", "test.zip", $zip)

getting the max value of 3 tags with xquery (oracle)

i have to combine some xml documents with xquery on a oracle 10.2 db.
therefore i need to get the max value of some tags:
i've tried this:
fn:max(( $p/col_1, $p/col_3 ))
the xml input (variable $p) is:
<xml_snippet>
<col_1>123</col_1>
<col_2>123</col_2>
<col_3>123</col_3>
</xml_snippet>
but i've got an error message
ORA-19112: error raised during evaluation: oracle.xquery.XQException: FORG0001: invalid value for cast/constructor
how can i get the maximum value only of col_1 and col_3?
Could you try GREATEST instead of MAX?
I'm not familiar with XQuery in Oracle, but, given the error message, I would suggest that you cast the values to integers:
let $values :=
for $x in ( $p/col_1, $p/col_3 )
return xs:integer($x)
return fn:max($values)

xquery parameter query

I'm trying to query an exist-db with xquery by taking parameters from the URL and building up seach parameters
xquery version "1.0";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare option exist:serialize "method=xml media-type=text/xml omit-xml-declaration=no indent=yes";
let $param1:= request:get-parameter("param1",'0')
let $person :=
if($param1 = '0')
then "'*'"
else concat('contributions/person/#val="',$param1,'"')
return
<xml>
{
for $x in subsequence(//foo/bar[$person],1,3)
return $x
}
</xml>
The code above shows that I get the parameter from the url $param1.
variable $person checks to see if there was a parameter and based on that creates a query parameter. This variable works fine, from testing it prints out either '*' for no param or
contributions/person/#val='hello, world'
When I run the query it prints out as if the value is '*'. In the for $x part, can I pass a variable like that? I've tried putting concat($person,'') with the same results. Hardcoding the full path gives me the results I'm looking for, but I'm looking to create something more dynamic.
To note: there is only one variable, $person, but there will be others once I get it to work
I think ideally you would avoid dynamic string evaluation. In this example, some pretty simple reorganization would solve the problem without it:
<xml>
{
for $x in subsequence(//foo/bar[
if ($param1 = '0')
then *
else (contributions/person/#val = $param1)
],1,3)
return $x
}
</xml>
However, you can use eval(), but keep in mind there are security risks:
<xml>
{
for $x in subsequence(eval(
concat('//foo/bar[',$person,']')
),1,3)
return $x
}
</xml>

Passing a subpath as a variable (type of node) or either eval() an string containing a relative XPATH

I want to succed with following functionality, and it works if you harcoded it:
declare function local:sort($collection as node()*, $filter as xs:SUBPATH?) as node()*{
for $element in $collection
order by
if ($filter) then ($element/$filter) (: OR SOME KIND OF fn:eval($filter) IF WE DEFINED $filter AS AN xs:string :)
else ($element/name()) (: Default :)
descending
return $element
};
And it could be called like that:
for $element in local:sort(doc('data')/Data/*,'/#myAttr')
return $element
or
for $element in local:sort(doc('data')/Data/*,'/subnode/subnode/name()')
return $element
or
for $element in local:sort(doc('data')/Data/*,()) (: This is the default of function = own elmentĀ“s name :)
return $element
Mx problem is passing the subpath. Either I need to know some kind of way to send a relative XPATH as an argument and type of node, or I need some kind of eval to pass from xs:string to a runtime valid code
Any help?
You could consider (a) generating a query in which the filter is hard-coded, or (b) using an eval() function specific to your XQuery vendor, or (c) using XQuery 3.0 higher-order functions if your chosen XQuery engine supports them yet.
I ended up doing the following, thanks to this idea How can I solve this autoncremental var case in XQUERY 1.0 FLOWR?:
(: This is a workaround solution as xquery:eval() is not working with var bindings until BaseX 7.3 version :)
(: It evals $context node + literal subpath with a pattern of '(/)lit/lit/lit/#attr' or '(/)lit/lit/lit' representing (/)lit/lit/lit/name(). If subpath null, it returns $context/name().:)
declare function u:eval-path($context as node()*, $subnodes as xs:string*) as item()* {
if(empty($subnodes)) then $context/name()
else(
if (count($subnodes) eq 1) then ( (: Last Element :)
if (starts-with($subnodes[1],'#')) then $context/#*[name()=substring-after($subnodes[1],'#')]
else $context/*[name()=$subnodes[1]]/name()
)
else if ($subnodes[1] eq '') then u:eval-path($context, $subnodes[position() gt 1])
else u:eval-path($context/*[name()=$subnodes[1]],$subnodes[position() gt 1])
)
};
(: Sorts the given collection by given criteria, which should be a pattern '(/)lit/lit/lit/#attr' or '(/)lit/lit/lit' representing (/)lit/lit/lit/name() :)
(: If criteria is null, everything is ordered by $elements/name(). Theres no way to filter intermediate nodes as in /lit/*[name()='X']/lit :)
declare function u:sort($collection as node()*, $criteria as xs:string?) as node()*{
for $element in $collection
order by u:eval-path($element,tokenize($criteria,'/'))
ascending
return $element
};

SoapUI property transfer and xpath/xquery number formatting

I have this property-transfer in SoapUI:
declare namespace soapEnv="http://schemas.xmlsoap.org/soap/envelope/";
//soapEnv:Body/LoginResponse/baseSequenceId
and lets say it returns 123456. But I want 123457 (what I get +1)
I tried this:
declare namespace soapEnv="http://schemas.xmlsoap.org/soap/envelope/";
//soapEnv:Body/LoginResponse/baseSequenceId + 1
but I get 123457.0 as a result. I tried some reformatting methods I found, but most possibly I did not use them in the correct way. I am quite new at this stuff.
I also tried this (with xquery):
declare namespace soapEnv="http://schemas.xmlsoap.org/soap/envelope/";
let $x := //soapEnv:Body/LoginResponse/baseSequenceId
return $x
and tried several things with $x but everything I tried ended up with null or InvocationTargetException.
Any help is appreciated !
Thanks a lot for your suggestions, although I couldn't make them work :(
Maybe there is something wrong with my SoapUI because all xpath functions return null..
I made it work with groovy:
groovyUtils = new com.eviware.soapui.support.GroovyUtils( context )
loginResponse = groovyUtils.getXmlHolder("Login#Response")
loginResponse.declareNamespace( "soapEnv", "http://schemas.xmlsoap.org/soap/envelope/" )
sessionIdStr = loginResponse.getNodeValue( "//soapEnv:Body/LoginResponse/sessionId" )
baseSequenceIdStr = loginResponse.getNodeValue( "//soapEnv:Body/LoginResponse/baseSequenceId" )
sequenceIdStr = (baseSequenceIdStr.toInteger() + 1).toString()
createRequest = groovyUtils.getXmlHolder("Create#Request")
createRequest.declareNamespace( "soapEnv", "http://schemas.xmlsoap.org/soap/envelope/" )
createRequest.setNodeValue( "//soapEnv:Header/SessionId", sessionIdStr )
createRequest.setNodeValue( "//soapEnv:Header/TransactionId", baseSequenceIdStr )
createRequest.setNodeValue( "//soapEnv:Header/SequenceId", sequenceIdStr )
createRequest.updateProperty()
Note that if the value of //soapEnv:Body/LoginResponse/baseSequenceId + 1 is an integer, XPath should not put in a decimal point when converting it to a string.
But maybe in this case XPath is returning a number, and it's SoapUI that's converting it to a string, and using a decimal point.
I would first try (updated):
string(//soapEnv:Body/LoginResponse/baseSequenceId + 1)
This is to force the conversion to string to happen within XPath, so that SoapUI won't have a chance to do anything funny with a numeric value.
Alternatively, you could try
floor(//soapEnv:Body/LoginResponse/baseSequenceId + 1)
or even
string(floor(...))
If that doesn't work, you could try
substring-before(//soapEnv:Body/LoginResponse/baseSequenceId + 1, '.')
It's not very elegant, but it might work.

Resources