If statement that assigns a variable a string value - xquery

I am new to XQuery and struggling to find anything online that teaches me simple stuff like this. I am trying to just pass in 2 strings, where 1 string is returned as the tag and 1 string is returned as a new value. So far I have this, but am getting build errors.
declare function util:format-promo-tracking-element(
$fieldName as xs:string,
$stringValue as xs:string)
{
let $elementName := $fieldName
if($stringValue = '5.00') then (
let $stringValue := '4.05'
return
element {$elementName}
{
data($stringValue)
}
)
else()
};
I have tried moving the return statement to the end as well.

XQuery is an functional language so trying to assign a new value to an existing variable is not allowed.
I think you want something like this.
declare function util:format-promo-tracking-element(
$fieldName as xs:string,
$stringValue as xs:string)
{
let $newValue := if ($stringValue = '5.00') then '4.05' else $stringValue
return
element {$fieldName}
{
$newValue
}
};
There are quite a few basic examples on Wikibooks.

Related

Invoking an XQuery Updating function results in the error If any subexpression is updating, then all must be updating

My XML document contains a record of the movement of an object. The XML document consists of a series of observations. Each observation contains the lat/long location of the object, the lat/long of the observing sensor, and a date/time stamp.
<Track-History>
<Track-ID>XYZ</Track-ID>
<Observation>
<Target-Latitude>10.3</Target-Latitude>
<Target-Longitude>20.8</Target-Longitude>
<Observer-Latitude>40.0</Observer-Latitude>
<Observer-Longitude>50.0</Observer-Longitude>
<DateTime>20230202T071700.00</DateTime>
</Observation>
<Observation>
<Target-Latitude>15.1</Target-Latitude>
<Target-Longitude>25.2</Target-Longitude>
<Observer-Latitude>40.0</Observer-Latitude>
<Observer-Longitude>50.0</Observer-Longitude>
<DateTime>20230202T071800.00</DateTime>
</Observation>
</Track-History>
I want to fuzz the locations of the object by rounding the decimal lat and long values. So I created this updating function:
declare updating function f:fuzzPoint($lat as element(), $long as element())
{
replace node $lat with
element {name($lat)} {round(number(data($lat)))},
replace node $long with
element {name($long)} {round(number(data($long)))}
};
When I invoke that function:
{f:fuzzPoint($obs/Target-Latitude, $obs/Target-Longitude)}
I get this error message:
If any subexpression is updating, then all must be updating
Oxygen XML gives a red squiggly line under the function arguments, so apparently the arguments must be updating. Yes? If so, how to make the arguments updating?
Below is my complete XQuery Update program.
declare namespace f = "function";
declare variable $Track-History := doc('Track-History.xml');
declare variable $track-history-points := (for $i in $Track-History//Observation return [$i/Target-Latitude, $i/Target-Longitude]);
declare variable $AOR := (); (: should be a sequence of points, fake it for now :)
declare function f:isInside($points, $polygon) as xs:boolean
{
true()
};
declare updating function f:fuzzPoint($lat as element(), $long as element())
{
replace node $lat with
element {name($lat)} {round(number(data($lat)))},
replace node $long with
element {name($long)} {round(number(data($long)))}
};
if (f:isInside($track-history-points, $AOR)) then
for $obs in $Track-History//Observation return
replace node $obs with
<Observation>
{f:fuzzPoint($obs/Target-Latitude, $obs/Target-Longitude)}
{$obs/Observer-Latitude}
{$obs/Observer-Longitude}
{$obs/DateTime}
</Observation>
else
replace node $Track-History/* with
<Track-History/>
Inside an update expression – replace node $obs with ... – it’s not allowed to perform other updates. The code works if you modify f:fuzzPoint such that it returns the new element nodes:
declare function f:fuzzPoint($lat as element(), $long as element()) {
element { name($lat) } { round(number(data($lat))) },
element { name($long) } { round(number(data($long))) }
};
The updating keyword in the function declaration makes sense if you move the update operation inside the function body:
declare updating function f:replace($obs as element()) {
let $lat := $obs/Target-Latitude
let $long := $obs/Target-Longitude
return replace node $obs with <Observation>{
element { name($lat) } { round(number(data($lat))) },
element { name($long) } { round(number(data($long))) },
$obs/Observer-Latitude,
$obs/Observer-Longitude,
$obs/DateTime
}</Observation>
};
if (f:isInside($track-history-points, $AOR)) then
for $obs in $Track-History//Observation return f:replace($obs)
else
replace node $Track-History/* with
<Track-History/>
I wonder whether
declare namespace f = "function";
declare variable $Track-History := doc('Track-History.xml');
declare variable $track-history-points := (for $i in $Track-History//Observation return [$i/Target-Latitude, $i/Target-Longitude]);
declare variable $AOR := (); (: should be a sequence of points, fake it for now :)
declare function f:isInside($points, $polygon) as xs:boolean
{
true()
};
declare function f:fuzzValue($v as xs:double) as xs:double
{
round($v)
};
if (f:isInside($track-history-points, $AOR)) then
for $lv in $Track-History//Observation/(Target-Latitude, Target-Longitude)
return
replace value of node $lv
with f:fuzzValue($lv)
else
replace node $Track-History/* with
<Track-History/>
is all you want to do there.

XQuery - wrong indexes in substring after reverse-string function use

Im trying to implement base64 coding in a very simple way. In my approach (lets for a second put away whether its appropriate or not) I need to reverse strings and then concate them. After that this concated string is used in substring function. Strings are joined properly but when I use substring basex seems to lose it.
Funny thing is substring works for well for all indexes starting at 8. So substring($string, 1, 8) and higher gives correct output. But everything below that is messed up. Starting with one disappeared number: substring($string, 1, 7 (and below) ) results in 6 length string.
Moreover substring can start only with 1st or 0 index. Anything greater results in empty return.
declare variable $array := [];
declare function bs:encode
( $input as xs:string ) {
bs:integer-to-binary(string-to-codepoints($input), "", $array)
} ;
declare function bs:integer-to-binary
( $input as xs:integer*, $string as xs:string, $array as array(xs:string) ) {
let $strings :=
for $i in $input
return
if ($i != 0)
then if ($i mod 2 = 0)
then bs:integer-to-binary(xs:integer($i div 2), concat($string, 0), $array)
else bs:integer-to-binary(xs:integer($i div 2), concat($string, 1), $array)
else if ($i <= 0)
then array:append($array, $string)
return bs:check-if-eight($strings)
} ;
declare function bs:check-if-eight
( $strings as item()+ ) {
let $fullBinary :=
for $string in $strings
return if (string-length($string) < 8)
then bs:check-if-eight(concat($string, 0))
else $string (: add as private below :)
return bs:concat-strings($fullBinary)
} ;
declare function bs:concat-strings
( $strings as item()+ ) {
let $firstStringToConcat := functx:reverse-string($strings[position() = 1])
let $secondStringToConcat := functx:reverse-string($strings[position() = 2])
let $thirdStringToConcat := functx:reverse-string($strings[position() = 3])
let $concat :=
concat
($firstStringToConcat,
$secondStringToConcat,
$thirdStringToConcat)
(: this returns correct string of binary value for Cat word :)
return bs:divide-into-six($concat)
} ;
declare function bs:divide-into-six
( $binaryString as xs:string) {
let $sixBitString := substring($binaryString, 1, 6)
(: this should return 010000 instead i get 000100 which is not even in $binaryString at all :)
return $sixBitString
} ;
bs:encode("Cat")
I expect first six letters from string (010000) instead I get some random sequence I guess (00100). The whole module is meant to encode strings into base64 format but for now (the part i uploaded) should just throw first six bits for 'C'
Alright so I figured it out I guess.
First of all in function concat-strings I changed concat to fn:string-join. It allowed me to pass as an argument symbol that separates joined strings.
declare function bs:concat-strings ( $strings as item()+ ) {
let $firstStringToConcat := xs:string(functx:reverse-string($strings[position() = 1]))
let $secondStringToConcat := xs:string(functx:reverse-string($strings[position() = 2]))
let $thirdStringToConcat := xs:string(functx:reverse-string($strings[position() = 3]))
let $concat :=
****fn:string-join(****
($firstStringToConcat,
$secondStringToConcat,
$thirdStringToConcat),****'X'****)
return bs:divide-into-six($concat) } ;
I saw that my input looked like this:
XXXXXXXX01000011XXXXXXXXXXXXXXXXX01100001XXXXXXXXXXXXXXXXX01110100XXXXXXXX
Obviously it had to looping somewhere without clear for loop and as I novice to Xquery i must have been missed that. And indeed. I found it in check-if-eight function:
> declare function bs:check-if-eight ( $strings as item()+ ) {
> **let $fullBinary :=**
> for $string in $strings
> return if (string-length($string) < 8)
> then bs:check-if-eight(concat($string, 0))
> else $string (: add as private below :)
> **return bs:concat-strings($fullBinary)** } ;
Despite being above FOR keyword, $fullBinary variable was in a loop and produced empty spaces(?) and it was clearly shown when i used X as a separator.
DISCLAIMER: I thought about this before and used functx:trim but for some reason it doesnt work like I expected. So it might not for you too if having similar issue.
At this point it was clear that let $fullBinary cannot be bided in FLWR statement at least can't trigger concat-strings function. I changed it and now it produces only string and now im trying to figure out new sequence of running whole module but I think the main problem here is solved.

Handling undefined number of external variables

I'm using baseX in a REST environment and I'm quite stuck trying to run an .xq script with an undefined number of GET variables (could be 1 but could be 10)
I'd like to make my xq script generic about that and construct my query independently.
Is there a way to achieve that, playing with array or sending differently my variables, or I dunno how ?
here is my API call
http://basex:8984/rest/?run=WEB-INF/data/test.xq&$tag=p&value=sciences&tag2=p&value2=test&tag3=testdzq
here is my text.xq
declare variable $tag external;
declare variable $value external;
declare variable $tag2 external;
declare variable $value2 external;
<documents>
{for $doc in collection("testdb2")
where $doc//*[name() eq $tag]/text()[matches(., $value )]
and $doc//*[name() eq $tag2]/text()[matches(., $value2 )]
return <doc>{$doc//titleStmt/title/text()}</doc>
}
</documents>
Thanks !
found this here (see http-params function) https://www.balisage.net/Proceedings/vol18/print/Murray01/BalisageVol18-Murray01.html
(: BaseX example :)
(: In the controller ... :)
module namespace c = "http://balisage.net/ns/Bal2016murr0319/controller";
import module namespace request = "http://exquery.org/ns/request";
import module namespace item = "http://balisage.net/ns/Bal2016murr0319/views/item" at "views/item.xqm";
(:~ Returns a map containing the HTTP request parameters. :)
declare function c:http-params()
as map(*)
{
map:merge(
for $name in request:parameter-names()
let $value := request:parameter($name)
return map:entry($name, $value)
)
};
(:~ Calls the appropriate view, based on user input. :)
declare function c:get-view()
as element(html)
{
(: get HTTP request parameters :)
let $params := c:http-params()
return
if (map:get($params, "id")) then
(: the presence of "id" indicates that the user is requesting the item-level page for this unique identifier :)
(: call the item-level view :)
item:get-html($params)
else if ... (: call some other view :)
else if ... (: call some other view :)
else (: call the view for the home page ... :)
};

getting this error while migrating from xquery 2004 to xquery 1.0 version

//Invalid Xquery content:line 89, column 44: {http://www.w3.org/2005/xqt-errors}XPST0003 [{http://xmlns.oracle.com/xdk/xquery/error}XPST0003c]: Syntax error. "/ns1:produ" is unexpected
Xquery after migrating to 1.0 V
xquery version "1.0";
(: Caring Included Logic Reset/#actioncode :)
(: Caring Fix#358 :)
(: Included CR for Project Caring :)
(: Added a xf:escape-dot function as a part of Jan - Break Fix :)
(: Included changes for Project Caring :)
(: Included mapping for accountGroups - CUG Migration :)
(: Included changes for Project AggregatedAlerts :)
(:: pragma bea:global-element-parameter parameter="$manageServiceOrder" element="ns3:manageServiceOrder" location="../../schemas/pf.bil.BillingProvisioning_3.0.xsd" ::)
(:: pragma bea:global-element-return element="ns0:CreateOrderCustomerRequest" location="../../../../resources_3.0/util/BRMAdapter/interfaces/InfranetUtilityCreate.xsd" ::)
declare namespace ns2 = "http://vodafone.com.au/ebo/vha/ProvisioningAndFulfilmentComponents/V3.0";
declare namespace ns1 = "http://vodafone.com.au/ebo/vha/CommonComponents/V3.0";
declare namespace ns3 = "http://vodafone.com.au/pf/bil/ebm/BillingProvisioning/V3.0";
declare namespace ns0 = "http://www.vodafone.co.au/infranetUtilityCreate/schema";
declare namespace xf = "http://tempuri.org/pf.bil_3.0/resources/billingProvisioning/BRM/EBO.SyncSubscription.billingProvisioning_xForm_BRM.createOrder/";
declare namespace temp = "http://www.w3.org/2001/XMLSchema";
declare xqse function xf:formatDateTimes($dateTime as element(*)*) as xs:dateTime?
{
declare $dateTimeValue :=data($dateTime);
try
{
return value (xs:dateTime(replace(fn-bea:dateTime-to-string-with-format("yyyyMMddHHmmss",$dateTimeValue),"^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$","$1-$2-$3T$4:$5:$6")));
}
catch (* into $err, $msg)
{
return value (xs:dateTime(replace(fn-bea:date-to-string-with-format("yyyyMMdd",$dateTimeValue),"^(\d{4})(\d{2})(\d{2})$","$1-$2-$3T00:00:00")));
};
};
declare xqse function xf:isValidChargeShareAtribute( $chargeShareLines as element(*)) as xs:boolean?
{
try
{
iterate $attribute over $chargeShareLines/ns1:productService/ns1:attributes/ns1:attribute[fn:upper-case(ns1:name/text()) eq "CSG REFERENCE" or fn:upper-case(ns1:name/text()) eq "CSG SPONSORSHIP"]
{
if (
( ($attribute/ns1:value ne "" and $chargeShareLines/#actionCode eq "New")
or
$chargeShareLines/#actionCode eq "Modified"
or
($attribute/ns1:value ne "" and $chargeShareLines/#actionCode eq "Deleted")
)
) then return value (fn:boolean("true")) else return value( (fn:boolean("false")));
};
}
catch (* into $err, $msg)
{ return value (fn:boolean("false"));
};
};
declare function xf:lookupDVMWrapper($dvmName as xs:string, $sourceColumn as xs:string,$sourceValue as xs:string, $targetColumn as xs:string, $needAnException as xs:boolean)as element(*)
{
let $dvmres :=(fn-bea:lookupDVM($dvmName,$sourceColumn,$sourceValue,$targetColumn,'NO DATA FOUND'))
return
if ((($dvmres='') or ($dvmres='NO DATA FOUND')) and $needAnException)
then
<dvmresp>
<dvmexception>
{
concat($sourceValue,' for ',$sourceColumn,' in ',$dvmName,'.dvm', ' not found' )
}
</dvmexception>
</dvmresp>
else if((($dvmres='') or ($dvmres='NO DATA FOUND')) and not($needAnException))
then
<dvmresp></dvmresp>
else
<dvmresp>
{
$dvmres
}
</dvmresp>
};
declare function xf:formatDate( $dateTime as element(*)*) as xs:dateTime?
{
let $dateTimeValue :=data($dateTime)
return xs:dateTime(replace(fn-bea:date-to-string-with-format("yyyyMMdd",$dateTimeValue),"^(\d{4})(\d{2})(\d{2})$","$1-$2-$3T00:00:00"))
};
declare function xf:calculateVFPeriodEndDate($manageServiceOrder as element (*),$serviceLines as element(*))
as xs:dateTime? {
if ($serviceLines/#actionCode eq "Deleted" or $serviceLines/#actionCode eq "Suspended")
then xf:formatDateTimes(element{"billEffectiveDate"}{ $manageServiceOrder/ns3:serviceOrder/ns2:billingDetails/ns2:billEffectiveDate/text()})
else if (fn:string-length(data($serviceLines/ns1:productService/ns1:periodEndDate)) >0 and data($serviceLines/ns1:productService/ns1:periodEndDate) ne "")
then xf:formatDate( element { "periodEndDate" } {if(fn:contains(data($serviceLines/ns1:productService/ns1:periodEndDate),"T"))then
fn:substring-before(data($serviceLines/ns1:productService/ns1:periodEndDate),"T")
else
$serviceLines/ns1:productService/ns1:periodEndDate/text()})
else if (
for $expiryDate in $serviceLines/ns1:productService/ns1:attributes/ns1:attribute[ns1:name/text() eq "Expiry Date"]/ns1:value/text()
return
if (fn:string-length($expiryDate) >0) then xf:formatDateTimes( element { "expiryDate" } {$expiryDate}) else() )
then
(
for $expiryDate in $serviceLines/ns1:productService/ns1:attributes/ns1:attribute[fn:upper-case(ns1:name/text()) eq "EXPIRY DATE"]/ns1:value/text()
return xf:formatDateTimes( element { "expiryDate" } {$expiryDate})
)
else ()
};
There are several things here that aren't allowed in XQuery 1.0. The ones that stand out are:
declare $dateTimeValue :=data($dateTime);
You can't declare local variables like that. You need let $v := expr return XXX.
return value (xs:dateTime(...);
The keyword return value is wrong and should be removed.
iterate $attribute over $chargeShareLines
I've no idea what the iterate expression does, but it's not XQuery. Looks like some variant of a for expression, but I don't know how it differs.
try {
requires XQuery 3.0/3.1, and the syntax is slightly different from yours.

Recursive function returning boolean, with a for loop inside

My data is a binary tree, and will check through every child, returning true if it finds the data i want, if not, it keeps looking for it.
In some way i want to return the variable #exists or something.. Well anyone might have a solution for my problem. I was thinking something like this but i couldn't get it to work! (code-snippet)
declare function local:test($id as xs:integer, $topic as xs:integer) as xs:boolean {
let $exists := fn:false()
for $x in ...
return
if .. then
set exists to fn:true()
else
set exists to exists OR local:test($x,$topic)
return #exists in some way
};
This is a case for an XQuery quantified expression. Using that, your function translates to
declare function local:test($id as xs:integer, $topic as xs:integer) as xs:boolean
{
some $x in ...
satisfies
if (..) then
fn:true()
else
local:test($x,$topic)
};
As it was already mentioned XQuery is a functional language. You can't just set variable and return it. Your query can be though rewritten like:
declare function local:test($id as xs:integer, $topic as xs:integer) as xs:boolean {
exists(for $x in ...
where (: here is condition expression on $x :)
return $x)
};
Function exists(Expr) returns true if the value of Expr is not the empty sequence; otherwise, the function returns false.
In this case exists returns true if there is $x which meets specified condition.
You cannot change the values of variables in xquery.
Is your whole function not just this:
declare function local:test($topic as xs:integer) as xs:boolean {
... OR local:test($topic/...)
};

Resources