I am trying to find the departments with the most employees (and its number of employees) in the following xml document:
<organization>
<departements>
<dept id="d1">
<name>accounting</name>
</dept>
<dept id="d2">
<name>marketing</name>
</dept>
<dept id="d3">
<name>production</name>
</dept>
</departements>
<employees>
<emp id="e1">
<name>...</name>
<activities>
<activity ref="d1"/>
</activities>
</emp>
<emp id="e2">
<name>...</name>
<activities>
<activity ref="d2"/>
<activity ref="d3"/>
</activities>
</emp>
<emp id="e3">
<name>...</name>
<activities>
<activity ref="d3"/>
</activities>
</emp>
<emp id="e4">
<name>...</name>
<activities>
<activity ref="d2"/>
</activities>
</emp>
</employees>
</organization>
Here is what i have tried:
(for $dept in doc('emp_dept.xml') //dept
let $nbr := count(doc('emp_dept.xml')//activity[#ref = $dept/#id])
order by $nbr descending
return $dept ) [1]
The above query returns the following output:
<dept id="d2">
<name>marketing</name>
</dept>
I would like to get the following output:
<dept id="d2">
<name>marketing</name>
<employees>2</employees>
</dept>
<dept id="d3">
<name>production</name>
<employees>2</employees>
</dept>
You could create the dept elements you want to output, determine the highest number of employees, and then output the ones that match that max count.
I'm sure there is a more efficient way, but this is what first came to mind.
let $doc := doc('emp_dept.xml')
let $newDept := for $dept in $doc//dept
return
<dept>{$dept/#*,$dept/*,
<employees>{count($doc//emp[activities/activity/#ref=$dept/#id])}</employees>
}</dept>
let $maxEmployees := max($newDept/employees)
return
$newDept[employees=$maxEmployees]
Related
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>
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)
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 ()
I'm using BaseX, which supports XQuery 3.0.
Let's say I have an dataset like this, ordered on the value of <start>:
<element>
<start>1</start>
<end>2</end>
</element>
<element>
<start>2</start>
<end>4</end>
</element>
<element>
<start>5</start>
<end>6</end>
</element>
I want to connect these elements by their end and start values, and group connecting elements together:
<block>
<start>1</start>
<end>4</end>
</block>
<block>
<start>5</start>
<end>6</end>
</block>
In Oracle, we could do something like this with CONNECT BY PRIOR. How can we do this in XQuery?
You can implement this behavior using a tumbling window, which lets you group based on conditions over element spans. Sliding and tumbling windows require XQuery 3.0, which is supported by BaseX.
let $items := (
<element>
<start>1</start>
<end>2</end>
</element>,
<element>
<start>2</start>
<end>4</end>
</element>,
<element>
<start>5</start>
<end>6</end>
</element>
)
for tumbling window $window in $items
start $start when fn:true()
only end $end next $next when not(
$end/end eq $next/start
)
return <block>{
$start/start,
$end/end
}</block>
I have the following code:
<Table><ColumnHeaders>
<ColumnHeader name='Title' type='xs:string' />
<ColumnHeader name='EventDate' type='xs:date' />
<ColumnHeader name='today' type='xs:date' />
<ColumnHeader name='day-week' type='xs:integer' />
<ColumnHeader name='dur-end' type='xs:integer' />
<ColumnHeader name='start-date' type='xs:date' />
<ColumnHeader name='end-date' type='xs:date' />
</ColumnHeaders>
<Data>{let $today := fn:current-date()
let $day-week := functx:day-of-week($today)
let $dur-end := 6-$day-week
let $start-date := xs:date($today)-(xs:dayTimeDuration('P1D')*$day-week)
let $end-date := xs:date($today)+(xs:dayTimeDuration('P1D')*$dur-end)
for $ancestor0 in $queryresponse//*:row
where ($ancestor0/#ows_EventDate)>=$start-date
return<Row>
<Title>{fn:data($ancestor0/#ows_Title)}</Title>
<EventDate>{fn:data($ancestor0/#ows_EventDate)}</EventDate>
<today>{fn:data($today)}</today>
<day-week>{fn:data($day-week)}</day-week>
<dur-end>{fn:data($dur-end)}</dur-end>
<start-date>{fn:data($start-date)}</start-date>
<end-date>{fn:data($end-date)}</end-date>
</Row>}
</Data></Table>
after adding the where clause in the "for statement", the xquery failed with the following error:
ZorbaDynamicException failed. Zorba engine encountered a dynamic error.
Error type: XQuery Engine error. Passed item (of type UNTYPED_ATOMIC) is not castable to passed target type (xs:date).
can someone please help me in fixing this issue?
The value resulting from $ancestor0/#ows_EventDate is not castable to a date. You can use the parse-date function contained in Zorba's datetime module (http://www.zorba.io/documentation/2.9/modules/www.zorba-xquery.com_modules_datetime.html) to convert the string into a date.
For example,
d:parse-date($ancestor0/#/#ows_EventDate/text(), "%D")
if your date is formatted as MM/DD/YY.