fn:min function not working - xquery

Okay so I have this xml:
<?xml version="1.0" encoding="UTF-8" ?>
<dvdCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://teste1.org"
xsi:noNamespaceSchemaLocation="http://teste1.org dvds.xsd">
<dvd>
<id>1</id>
<title>Pulp Fiction</title>
<release-year>1994</release-year>
<director>Quentin Tarantino</director>
<actors>
< actor type="star" gender="male">John Travolta</actor>
<actor type="star" gender="female">Uma Thurman</actor>
<actor type="co-star" gender="male">Samuel L. Jackson</actor>
</actors>
<genres><genre>Crime</genre><genre>Drama</genre></genres>
</dvd>
<dvd>
<id>2</id>
<title>Green mile</title>
<release-year>1993</release-year>
<director>Quentin Tarantino</director>
<actors>
<actor type="star" gender="male">Tom hanks</actor>
</actors>
<genres><genre>Crime</genre></genres>
</dvd>
<dvd>
<id>3</id>
<title>Titanic</title>
<release-year>1999</release-year>
<director>Quentin Tarantino</director>
<actors>
<actor type="star" gender="male">Tom hanks</actor>
</actors>
<genres><genre>Crime</genre></genres>
</dvd>
</dvdCollection>
I'm trying to fetch the minimum release-year with Crime genre. This is my query
but for some reason its returning all the release years like this:
1994
1993
1999
I'm puting a sequence like this inside fn:min
<release-year xmlns="http://teste1.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1994</release-year>
<release-year xmlns="http://teste1.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1993</release-year>
<release-year xmlns="http://teste1.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1999</release-year>
I was given the impression fn:min would flatten my sequence, what am I'm doing wrong, can someone explain how this function works ?
query:
declare default element namespace "http://teste1.org";
let $dvds := fn:doc("dvdcollection.xml")//dvd
for $dvd in $dvds//genre[.="Crime"]
let $min := fn:min($dvd/../../release-year)
return $min
Thanks in advance!!

For everybody who has a similar mistake:
Okay so fn:min does what I tought it would, the problem was that I was applying fn:min to each element because of the for instruction. This is not what I wanted, I wanted to group a sequence of release-years, so it was pretty simple what I needed to to do!
declare default element namespace "http://teste1.org";
let $dvds := fn:doc("dvdcollection.xml")//dvd
**let** $dvd := $dvds//genre[.="Crime"]
let $min := fn:min($dvd/../../release-year)
return $min
Just replace that "for" for a "let" and the mistake is cleared.

Related

Exist-db Add node with XQUERY

I have a listPers.xml (TEI List containing persons, obviously ) . I want to write a function to update the listPers.xml
My function looks like this:
declare function app:addPerson($node as node(), $model as map(*)) {
let $person := "<person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></person>"
let $list := doc(concat($config:app-root, '/resources/listPers_test.xml'))
return
update insert $person into $list//tei:listPerson
};
And the listPerson.xml
looks more or less like a typical list with person-entries
I have a tei:header (here omitted) followed by
<text>
<body>
<listPerson xml:id="person">
<person xml:id="abbadie_jacques">
<persName ref="http://d-nb.info/gnd/100002307">
<forename>Jacques</forename>
<surname>Abbadie</surname>
</persName>
<note>Prediger der französisch-reformierten Gemeinde in <rs type="place" ref="#berlin">Berlin</rs>
</note>
</person>
</body>
</text>
</TEI>
(sorry for ruining indentions, it's just an excerpt )
I do not get an error, which means that my app:addPerson should be fine, right?
I want the listPers_test to look like this:
<text>
<body>
<listPerson xml:id="person">
<person xml:id="abbadie_jacques">
<persName ref="http://d-nb.info/gnd/100002307">
<forename>Jacques</forename>
<surname>Abbadie</surname>
</persName>
<note>Prediger der französisch-reformierten Gemeinde in <rs type="place" ref="#berlin">Berlin</rs>
</note>
</person>
<!-- here comes the output that I wish to have :-) -->
<person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></person>
</body>
</text>
</TEI>
In the long run, I aim for an html-form that allows users to input names etc., where ids are generated using sth like
to-lowercase(concat($surname, "_", $forename));
But I will not get into my questions regarding forms and xquery, as I have barely done a quick Google-trip regarding html forms and xquery!
Can anyone hint me at why I do not get the listPers_test.xml file updated with the second value? :-)
All the best and thanks in advance to everyone,
K
Alright, I have a solution for anyone interested in it:
My first snippet $person:= ... contains a STRING, not an element.Changing the line
let $person := "<person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></person>"
to this one actually solves the issue:
let $person := <tei:person xml:id=""><persName><forename>Albert</forename><surname>Test</surname></persName></tei:person>

How to insert a constructed XML nodes in XQuery?

I want to insert a node using below code but if i will rerun the code i don't want my node to be repeated twice-
let $doc := fn:doc("abc.xml")
(: abc.xml looks like--
<root>
<value1>somevalue</value1>
<value2>somevalue</value2>
<value3>somevalue</value3>
<value4>somevalue</value4>
<value5>Australia</value5>
<value6>India</value6>
<value7>USA</value7>
<value8>somevalue</value8>
<value9>somevalue</value9>
<value10>somevalue</value10>
</root> :)
let $element := element Root{
element A{"A"},
element B{"B"},
element C{"C"},
element D{"D"},
element E{"E"}
}
let $doc := xdmp:node-insert-after(doc("abc.xml")//value4, $element)
return doc("abc.xml")
Whenever i am running this query my ELEMENT is getting inserted after value4 but i want that if i am running this multiple times then my element should get inserted only once and not multiple times.
Example- If i am running this 2 times-
Actual Output-
<root>
<value1>somevalue</value1>
<value2>somevalue</value2>
<value3>somevalue</value3>
<value4>somevalue</value4>
<Root>
<A>A</A>
<B>B</B>
<C>C</C>
<D>D</D>
<E>E</E>
</Root>
<Root>
<A>A</A>
<B>B</B>
<C>C</C>
<D>D</D>
<E>E</E>
</Root>
<value5>Australia</value5>
<value6>India</value6>
<value7>USA</value7>
<value8>somevalue</value8>
<value9>somevalue</value9>
<value10>somevalue</value10>
</root>
Expected Output-
<root>
<value1>somevalue</value1>
<value2>somevalue</value2>
<value3>somevalue</value3>
<value4>somevalue</value4>
<Root>
<A>A</A>
<B>B</B>
<C>C</C>
<D>D</D>
<E>E</E>
</Root>
<value5>Australia</value5>
<value6>India</value6>
<value7>USA</value7>
<value8>somevalue</value8>
<value9>somevalue</value9>
<value10>somevalue</value10>
</root>
Any Suggestions ??
Before inserting the node, check that it's not already there:
if (empty(child::Root)) then xdmp:update....
Note that you are using xdmp:node-insert-after -- this will do what the function name says (insert after) each time its called. As commented, you can conditionally call xdmp:node-update instead. Alternatively you can use xquery directly:
doc("file.xml" )/root ! <root>{ ./*[ . << ./value4] , $element , $a/value4, ./*[. >> ./value4] }</root>
Note: the "<<" operator compares "document order" (position not value)

XQuery Replace Value With Conditional Failing

i am in the middle of creating xquery replace-value of node action in XQuery.
But seems this code is not working therefore the IF-ELSE statement is always going to else.
This is my code:
declare function local:replacing($contextData as element())
as element()*
{
copy $pipeline := $contextData/Handler/Data/*
modify(
if(not(empty(data($pipeline/Payload/sample/transactionType)))) then
replace value of node $pipeline/Payload/sample/transactionType with 'XXX' else (),
if(not(empty(data($pipeline/Payload/sample/revision)))) then
replace value of node $pipeline/Payload/sample/revision with 'XXX' else ()
)
return $pipeline
};
I try against this sample XML but the result is always not XXX when the field revision is having value. (this always goes to else statement)
let $result :=
<root>
<Handler>
<Data>
<root>
<Payload>
<sample>
<transactionType></transactionType>
<revision>123</revision>
<board>1</board>
<mission>1</mission>
<method>Manual</method>
<listOfBoard>
<board>
<type>small</type>
<amount>5054</amount>
<token>300</token>
</board>
</listOfBoard>
<pricing>300</pricing>
<playing>Wed</playing>
</sample>
</Payload>
</root>
</Data>
</Handler>
</root>
Current Result:
<root>
<Payload>
<sample>
<transactionType/>
<revision>123</revision>
<board>1</board>
<mission>1</mission>
<method>Manual</method>
<listOfBoard>
<board>
<type>small</type>
<amount>5054</amount>
<token>300</token>
</board>
</listOfBoard>
<pricing>300</pricing>
<playing>Wed</playing>
</sample>
</Payload>
</root>
Expected Result
<root>
<Payload>
<sample>
<transactionType/>
<revision>XXX</revision>
<board>1</board>
<mission>1</mission>
<method>Manual</method>
<listOfBoard>
<board>
<type>small</type>
<amount>5054</amount>
<token>300</token>
</board>
</listOfBoard>
<pricing>300</pricing>
<playing>Wed</playing>
</sample>
</Payload>
</root>
Any ideas for this?
Update
As recommended by Har below, i change the code into:
if(not(empty(data($pipeline/Payload/sample/transactionType)))) then
replace value of node $pipeline/Payload/sample/transactionType with 'XXX'
else (
if(not(empty(data($pipeline/Payload/sample/revision)))) then
replace value of node $pipeline/Payload/sample/revision with 'XXX' else ()
)
But seems the result is the same. It still goes to else () statement.
Any ideas?
Thank you before.
The problem was, empty() checks for empty sequence, so sequence containing one empty string is considered true by empty(). You can just pass data() result to if since empty has Effective Boolean Value of false :
if(data($pipeline/Payload/sample/transactionType)) then
replace value of node $pipeline/Payload/sample/transactionType with 'XXX' else (),
if(data($pipeline/Payload/sample/revision)) then
replace value of node $pipeline/Payload/sample/revision with 'XXX' else ()

Filtering by attribute

Below is a excerpt from a XML file with 65 lectures:
<?xml version="1.0" encoding="iso-8859-1" ?>
<university>
<lecture>
<class>English</class>
<hours>3</hours>
<pupils>30</pupils>
</lecture>
<lecture>
<class>Math</class>
<hours>4</hours>
<pupils>27</pupils
</lecture>
<lecture>
<class>Science</class>
<hours>2</hours>
<pupils>25</pupils>
</lecture>
</university>
I need a where clause that gives me a list of lectures with more pupils than an English lecture. However, not with the attribute "30" used, but calling the English's lecture attribute instead
E.g., I want to use a where clause with a condition like pupils > English.pupils, instead of pupils > 30.
(The "pupils > English.pupils" is just puesdo code as an example)
A where clause isn't strictly necessary, but to use one you would make it part of a for iterator:
let $lectures := doc("lectures.xml")/university/lecture
let $english-pupils := $lectures[class = "English"]/pupils/xs:integer(.)
for $lecture in $lectures
where ($lecture/pupils/xs:integer(.) gt $english-pupils)
return $lecture
You could also avoid the flwor altogether by using an XPath predicate.
let $lectures := doc("lectures.xml")/university/lecture
let $english-pupils := $lectures[class = "English"]/pupils/xs:integer(.)
return $lectures[pupils/xs:integer(.) gt $english-pupils]

XQuery: update insert attribute failed

test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>French Toast aaa</name>
<price>$5.95</price>
<description>Our famous Belgian Waffles with plenty of real maple syrup</description>
<calories>650</calories>
</food>
<food>
<name>French Toast</name>
<price>$4.50</price>
<description>Thick slices made from our homemade sourdough bread</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>$6.95</price>
<description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
<calories>950</calories>
</food>
</breakfast_menu>
test.xqy:
for $x in doc('test.xml')//*
return update insert attribute id {'abcd'} into $x
For each XML markup I add a new attribute.
The xqy file is pretty simple. And I got:
[XPST0003] Unexpected end of query: 'insert attribut...'.
Any help?
You're having two issues here:
misuse of the update statement and
missing node keyword.
The BaseX-specific update statement is only meant to be used with the copy/modify construct; you don't need it here. Then, the operator for inserting any kinds of nodes is always insert node $node [positional clause] into $target with an optional [positional clause]. Instead of a node variable $node, you can of course also use a node constructor like attribute id {'abcd'}.
The correct query is:
for $x in doc('test.xml')//*
return insert node attribute id {'abcd'} into $x

Resources