Population data xquery - xquery

For the XML data below I am trying to get the output shown here: I.e I want to see the names of the countries having a population greater than 20000 , with the conditions that the number of cities displayed should only be for those with a population more than 3500. Also, For some countries the city is within a province.
<result>
<country name="B">
<num_cities>3</num_cities>
</country>
<country name="C">
<num_cities>2</num_cities>
</country>
</result>
---------------------------This is the XML data----------------------
<country id="1" name="A" population="12000">
<name>A</name>
<city id="c1" country="1">
<name>T1</name>
<population>5000</population>
</city>
<city id="c2" country="1">
<name>T2</name>
<population>3000</population>
</city>
<city id="c3" country="1">
<name>T3</name>
<population>4000</population>
</city>
</country>
<country id="3" name="B" population="80000">
<name>B</name>
<city id="c4" country="2">
<name>T4</name>
<population>6000</population>
</city>
<city id="c5" country="2">
<name>T5</name>
<population>2000</population>
</city>
<city id="c6" country="2">
<name>T6</name>
<population>60000</population>
</city>
<city id="c7" country="2">
<name>T7</name>
<population>12000</population>
</city>
</country>
<country id="3" name="C" population="68000">
<name>C</name>
<province>P1</province>
<city id="c8" country="3">
<name>T8</name>
<population>51000</population>
</city>
<city id="c9" country="3">
<name>T9</name>
<population>3000</population>
</city>
<city id="c10" country="3">
<name>T10</name>
<population>14000</population>
</city>
</country>
I wrote this xquery but i don't know how to exclude the cities having a population > 3500. I might not have written to code correctly either...Please assist.
for $c in doc("abc")//country
let $city:= count($c/city/name)
let $citypr:= count($c/province/city/name)
where $c/#population>1000000
return
<result>
<country name='{data($c/name) }'></country>
<num_of_cities>
{
if (exists ($c/city/name)) then
$city
else
$citypr
}
</num_of_cities>
</result>

Some hints:
Don't bother with where clauses where you can filter down earlier, in a specifier retrieving your content.
If you want to have only one <result>, rather than one per datum, you need to start it before the FLWOR expression; whatever you 'return' will be returned once per item.
This is an example of something closer:
<result>{
for $large-country in doc("abc")//country[#population > 20000]
let $large-cities := $country/city[population > 3500]
return
<country name="{$large-country/#name}">
<num_cities>{count($large-cities)}</num_cities>
</country>
}</result>

Related

separate elements with tags in variable [Xquery]

First of all, I'm totally new in Xquery and I am sorry my problem is very stupid :/ .
This is just a part of my XML :
<books>
<book id="b1">
<title>Set theory and the continuum problem</title>
<category>Mathematics</category>
<location>
<area>hall1</area>
<case>1</case>
<shelf>2</shelf>
</location>
<description>A lucid, elegant, and complete survey of set theory.</description>
<history>
<borrowed by="m4"/>
<borrowed by="m2" until="2018-04-05"/>
</history>
</book>
<book id="b2">
<title>Computational Complexity</title>
<isbn>978-0201-530-827</isbn>
<category>Computer Science</category>
<location>
<area>hall1</area>
<case>3</case>
<shelf>3</shelf>
</location>
<description>.</description>
</book>
<book id="b3">
<title>To mock a mockingbird</title>
<isbn>1-292-09761-2</isbn>
<category>Logic</category>
<category>Mathematics</category>
<location>
<area>hall1</area>
<case>1</case>
<shelf>3</shelf>
</location>
<description>.</description>
</book>
</books>
<libraryArea floor="1" name="hall1">
<bookcases>
<woodenCase id="3" shelves="5"/>
<woodenCase id="2" shelves="5"/>
<steelCase id="1" shelves="5"/>
</bookcases>
</libraryArea>
<libraryArea name="hall2">
<bookcases>
<lockedCase id="1" shelves="9"/>
<steelCase id="4" shelves="3"/>
<lockedCase id="3" shelves="2"/>
<steelCase id="2" shelves="4"/>
</bookcases>
</libraryArea>
<libraryArea name="hall3">
<bookcases>
<woodenCase id="2" shelves="3"/>
<steelCase id="1" shelves="8"/>
</bookcases>
</libraryArea>
<libraryArea name="archive">
<bookcases>
<lockedCase id="1" shelves="1"/>
</bookcases>
</libraryArea>
</library>
I want to list specific book titles in their own tags ''
Something like this:
<specialSteelCases>
<area name="hall1">
<steelCase id="1">
<bookCount>2</bookCount>
<book>Set theory and the continuum problem</book>
<book>To mock a mockingbird</book>
<shelves>5</shelves>
</steelCase>
</area>
</specialSteelCases>
But I get this, all book titles in just one book tag:
< book > Set theory and the continuum problemTo mock a mockingbird < / book >
Is there a way I can separate them apart, so each book title have their own
This is my Xquery:
<specialSteelCases>
{
for $a in //libraryArea
where $a/bookcases[count(woodenCase) > 1 ]
order by $a/#id
return
<area name="{$a/#name}">
{
for $s in $a//bookcases/steelCase
let $bbNew := (//books/book[location/case=$s/#id][location/area=$a/#name])
return
<steelCase id="{$s/#id}">
<bookCount>***code***</bookCount>
<book>{$bbNew/title/text()}</book>
<shelves>***code**</shelves>
</steelCase>
}
</area>
}
</specialSteelCases>
Use a let clause to select the relevant books and then count them and additionally with a nested for expression output the titles:
<specialSteelCases>
{
for $a in //libraryArea
where $a/bookcases[count(woodenCase) > 1 ]
order by $a/#id
return
<area name="{$a/#name}">
{
let $books := //books/book[location/case=$a//bookcases/steelCase/#id][location/area=$a/#name]
return
<steelCase id="{$a//bookcases/steelCase/#id}">
<bookCount>{count($books)}</bookCount>
{
for $title in $books/title
return
<book>{data($title)}</book>
}
</steelCase>
}
</area>
}
</specialSteelCases>
https://xqueryfiddle.liberty-development.net/jyyiVhw/1
If you can move to XQuery 3 and simply grouping:
for $book in //books/book
group by $case := //library/libraryArea[#name = $book/location/area]/bookcases/*[#id = $book/location/case]/local-name()
return
element {$case } {
<bookCount>{count($book)}</bookCount>
,
$book/title ! <book>{data()}</book>
}
https://xqueryfiddle.liberty-development.net/jyyiVhw

How access nested child attributes from a list in Adobe AEM using sightly?

I am trying to access a list of items having 'title' and 'url' in them. I want to access the 'item' or 'url' but not sure how to.
The child items are accessible but with:
${child} // prints like this {"title":"Hello","url":"www.hello.com"}
but ${child.url} or ${child['url'} doesn't print anything.
This is my html:
<div data-sly-use.model="au.com.nbnco.website.model.components.Links">
<h6>${properties.linksTitle # context="html"}</h6>
<ul data-sly-list.child="${properties.links}">
<li> ${child.url}</li> // not printing anything
<li> ${child.['url']}</li> // not printing anything
<li> ${child}</li> // prints like this {"title":"Hello","url":"www.hello.com"}
</ul>
</div>
And this my dialog.xml.
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="cq:Dialog"
width="640"
height="480"
xtype="dialog">
<items
jcr:primaryType="cq:Widget"
xtype="tabpanel">
<items jcr:primaryType="cq:WidgetCollection">
<configurations
jcr:primaryType="cq:Panel"
title="Configuration">
<items jcr:primaryType="cq:WidgetCollection">
<links_title
jcr:primaryType="nt:unstructured"
fieldLabel="Links Title"
name="./linksTitle"
defaultValue="Links"
xtype="textfield"/>
<links
jcr:primaryType="cq:Widget"
name="./links"
title="Links"
xtype="multifield">
<fieldConfig
jcr:primaryType="cq:Widget"
border="true"
layout="form"
padding="5px"
xtype="multi-field-panel">
<items jcr:primaryType="cq:WidgetCollection">
<title
jcr:primaryType="cq:Widget"
dName="title"
fieldLabel="Title"
xtype="textfield"/>
<url
jcr:primaryType="cq:Widget"
dName="url"
fieldLabel="Url"
xtype="textfield"/>
</items>
</fieldConfig>
</links>
</items>
</configurations>
</items>
</items>
</jcr:root>
You should be able to access properties url and title like this:
<ul data-sly-list.child="${properties.links}">
<li> ${child.properties.url}</li>
<li> ${child.properties.title}</li>
</ul>
very similar to how you accessed your other custom property "links" of currentPage
It looks like the links property is stored as multiple JSON strings. HTL/Sightly does not parse into JSON strings. You will need a use-api object to parse the JSON and output the properties.
HTL doesn't parse your objects. You can use a JS helper function to parse your elements.
<sly data-sly-list.jsonLinks="${properties.links}">
<ul data-sly-use.parsedLinks="${'../parseMyJson.js' # element=jsonLinks}">
<li>${parsedLinks.title}</li>
<li>${parsedLinks.url}</li>
</ul>
</sly>
And in the parent folder for example, create a parseMyJson.js with :
use(function () {
var element = this.element;
if (element) {
element = JSON.parse(element);
}
return element;
});

XQuery Recursive Function Call for Inner Tag

I'm trying to prepare an XML file to parse it JSON and its context is such as:
<user_manual>
<embed-language_part id="SL14686180">
<language_part id="1" role="-" lang="de">
<embed-user_manual_part id="1">
<user_manual_part id="1" role="-" document-type="IU">
<embed-chapter id="1">
<?ecls-start-embedded-resource resource="ecls_bio_becls_a3_a30660983"?>
<chapter id="1" role="-" toctitle="yes" footrowtitle="no" type="security">
<embed-title_module id="1">
<title_module id="1" role="-">
<title id="1">Sicherheits- und Warnhinweise</title>
</title_module>
</embed-title_module>
<embed-section id="1">
<section id="1" footrowtitle="no" role="-" toctitle="yes">
<embed-section id="2">
<section id="2">
<embed-title_module id="2">
<title_module id="2" role="-">
<title id="2">Eisschale</title>
</title_module>
</embed-title_module>
</section>
</embed-section>
<embed-title_module id="3">
<title_module id="31" role="-">
<title id="3">Bevor Sie das Gerat in Betrieb nehmen</title>
</title_module>
</embed-title_module>
</section>
</embed-section>
</chapter>
</embed-chapter>
</user_manual_part>
</embed-user_manual_part>
</language_part>
</embed-language_part>
</user_manual>
I wrote an XQuery script regarding to my expectations first (assume that $doc is document, $matnr is 22333),
declare variable $doc external;
declare variable $matnr external;
<dmContainer>{
for $language in $doc/user_manual/embed-language_part/language_part
let $lang_code := data($language/#lang)
for $embed_chapter in $language/embed-user_manual_part/user_manual_part/embed-chapter
let $objectid := data($embed_chapter/processing-instruction('ecls-start-embedded-resource'))[1]
let $fileattr := string($objectid)
let $filename := translate(substring-after($objectid,'resource='),'"','')
let $postfix := substring(tokenize($filename,'_')[last()], 2)
let $name := concat($matnr, '_', $postfix)
return (element {$lang_code} {
attribute title {data($embed_chapter/chapter/embed-title_module/title_module/title)},
attribute language {$lang_code},
attribute name {$name},
for $section in $embed_chapter/chapter/embed-section/section
return <section title="{data($section/embed-title_module/title_module/title)}"></section>
})
}</dmContainer>
This returns:
<dmContainer>
<de title="Sicherheits- und Warnhinweise" language="de" name="223333_30660983">
<section title="Bevor Sie das Gerat in Betrieb nehmen" />
</de>
</dmContainer>
Return contains the chapter element and its first section's title for the JSON but I have to add this one to all sections (the sections included by sections too).
According to the input XML the sections can have another sections (one or more) recursively. You can look the example by searching it deeply. The question is that how i can add these sections to my output with a proper recursive way(i mean not just the child level one level two children are included too) , i searched for some examples recursive functions of XQuery but i couldn't get any one.
Expected output:
<dmContainer>
<de title="Sicherheits- und Warnhinweise" language="de" name="223333_30660983">
<section title="Bevor Sie das Gerat in Betrieb nehmen">
<section title="Eisschale"/>
</section>
</de>
</dmContainer>
How can I get all sections?
If you just want all sections in that chapter (in document order), go with the descendant-or-self-step, abbreviated //.
for $section in $embed_chapter/chapter//embed-section/section
return <section title="{data($section/embed-title_module/title_module/title)}"
If document order doesn't work out for you (eg., first the title of the current section, then all subsections on the current level, no matter if they actually precede the title), you will have to write your own function traversing the tree: a function that gets the current section, returns the title, and recursively calls itself for the direct subsections (if there are any).

How do I take returned XML and insert specific elements into columns in SQL Server using VB?

I have a SQL Server database, and I need to populate it with returned xml from an api call.
This is the xml code that's returned(not in a file):
<petfinder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://api.petfinder.com/schemas/0.9/petfinder.xsd">
<header>
<version>0.1</version>
<timestamp>2013-04-08T14:52:23Z</timestamp>
<status>
<code>100</code>
<message/>
</status>
</header>
<lastOffset>25</lastOffset>
<pets>
<pet>
<id>18589607</id>
<shelterId>OK98</shelterId>
<shelterPetId>11C-0015</shelterPetId>
<name>Sam</name>
<animal>Cat</animal>
<breeds>
<breed>Domestic Short Hair</breed>
<breed>Tabby</breed>
</breeds>
<mix>yes</mix>
<age>Adult</age>
<sex>M</sex>
<size>XL</size>
<options>
<option>altered</option>
<option>hasShots</option>
<option>housebroken</option>
</options>
<description>
<![CDATA[
<div>This guy loves the camera. Look at him pose and show off! Sam is about 5 years old and is a cream Tabby. He is good with other cats and is house trained. He has turquoise eyes and is a sweet sweet cat. Sam loves to be the right hand man and assist you on any task you may have. Sammy is not the type of cat that likes to be held but will sit right next to you for some rubbing and head butting. Our adoption fee is $100 for dogs and $75 for cats. This adoption fee includes the spay or neutering and rabies shot. </div>
]]>
</description>
<lastUpdate>2012-07-24T14:50:17Z</lastUpdate>
<status>A</status>
<media>
<photos>
<photo id="1" size="x">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-1-x.jpg
</photo>
<photo id="1" size="fpm">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-1-fpm.jpg
</photo>
<photo id="1" size="pn">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-1-pn.jpg
</photo>
<photo id="1" size="pnt">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-1-pnt.jpg
</photo>
<photo id="1" size="t">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-1-t.jpg
</photo>
<photo id="2" size="x">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-2-x.jpg
</photo>
<photo id="2" size="fpm">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-2-fpm.jpg
</photo>
<photo id="2" size="pn">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-2-pn.jpg
</photo>
<photo id="2" size="pnt">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-2-pnt.jpg
</photo>
<photo id="2" size="t">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-2-t.jpg
</photo>
<photo id="3" size="x">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-3-x.jpg
</photo>
<photo id="3" size="fpm">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-3-fpm.jpg
</photo>
<photo id="3" size="pn">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-3-pn.jpg
</photo>
<photo id="3" size="pnt">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-3-pnt.jpg
</photo>
<photo id="3" size="t">
http://photos.petfinder.com/photos/US/OK/OK98/18589607/OK98.18589607-3-t.jpg
</photo>
</photos>
</media>
<contact>
<address1>714 Martin Luther King Jr Ave</address1>
<address2/>
<city>Duncan</city>
<state>OK</state>
<zip>73533</zip>
<phone/>
<fax/>
<email/>
</contact>
</pet>
...
More specifically, I need to take the nodes for ID, name, animal, description, and several others, and insert them into their respectful columns in my database.
And it must repeat this for each "pet" node that these are all in.
Can I do this in VB.net without saving a file, just as an xml string?
Please help, I've been stuck on this for days.
Assuming you have your XML structure in a variable (or stored procedure parameter) of type XML, you can do something like this:
CREATE PROCEDURE dbo.InsertXmlData
#XmlData XML
AS BEGIN
INSERT INTO dbo.YourTable(ID, PetName, Animal, Description)
SELECT
ID = Pet.value('(id)[1]', 'int'),
PetName = Pet.value('(name)[1]', 'varchar(50)'),
Animal = Pet.value('(animal)[1]', 'varchar(50)'),
[Description] = Pet.value('(description)[1]', 'varchar(500)')
FROM
#XmlData.nodes('/petfinder/pets/pet') AS xTBL(Pet)
END
That gives you the info in those nodes as a set of rows and columns which you can easily insert into a SQL Server table. So now you just need to find a way to call this stored procedure from your VB.NET code and pass in the XML into the #XmlData parameter
Here's an example of how you can extract the data for each pet from the XML using XPath and the XmlDocument class:
Dim doc As XmlDocument = New XmlDocument()
doc.LoadXml(xmlString)
For Each pet As XmlNode In doc.SelectNodes("/petfinder/pets/pet")
Dim id As String = pet.SelectSingleNode("id").InnerText
Dim name As String = pet.SelectSingleNode("name").InnerText
' ...
Next
I'm assuming you know how to save the data to your SQL database from there.

Average of calculated averages

I've got a big XML file containing data concerning a Hotel chain. Per week, per day, per hotel they keep some data for their report:
<Report week="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="report.xsd">
<Days>
<Day id="1" naam="Monday">
<Hotels>
<Hotel id="1" naam="Bonotel Antwerp">
<Stays>
<Stay id="1">
<Room id="1" number="1"/>
<Roomtype id="1" naam="oneperson" price="20.00"/>
<Period id="1" naam="lowseason" price="20.00"/>
<Formula id="1" naam="roomandbreakfast" price="20.00"/>
<Facilities>
<Facility id="1" naam="swimming" price="5.00"/>
<Facility id="2" naam="golf" price="20.00"/>
</Facilities>
<Guest id="1" naam="John Williams"/>
</Stay>
<Stay id="2">
<Room id="2" number="2"/>
<Roomtype id="1" naam="oneperson" price="20.00"/>
<Period id="1" naam="lowseason" price="20.00"/>
<Formula id="1" naam="roomandbreakfast" price="20.00"/>
<Facilities>
<Facility id="2" naam="golf" price="20.00"/>
<Facility id="3" naam="minibar" price="10.00"/>
</Facilities>
<Guest id="2" naam="Ray Kurzweil"/>
</Stay>
<Stay id="3">
<Room id="3" number="3"/>
<Roomtype id="2" naam="twoperson" price="40.00"/>
<Period id="1" naam="lowseason" price="20.00"/>
<Formula id="2" naam="halfpension" price="30.00"/>
<Facilities>
<Facility id="4" naam="tennis" price="20.00"/>
<Facility id="4" naam="tennis" price="20.00"/>
</Facilities>
<Guest id="3" naam="Stephen Hawking"/>
</Stay>
</Stays>
</Hotel>
(: ... Other Hotels ... :)
</Hotels>
</Day>
(: ... Other Days ... :)
</Days>
</Report>
Using XQuery, I have to calculate what the average guests spends. This is the sum of the Roomtype price, the Periode price, the Formula price and the sum of the facilities. I came up with this XQuery:
xquery version "1.0";
<ReportResult week="1">
{
for $x in (1 to 7)
return
for $stays in doc("report.xml")//Report/Days/Day[#id=$x]/Hotels/Hotel[#id=1]/Stays
let $average := avg(
for $v in $stays/Stay
return sum($v/Roomtype/#price) + sum($v/Facilities/Facility/#prijs) + sum($v/Formula/#price) + sum($v/Period/#price)
)
return
<AverageSpending hotel="Bonotel Antwerpen" day="{data($x)}">
{data(round-half-to-even($average, 2))}
</AverageSpending>
}
</Reportresult>
This produces the result I expect:
<Reportresult week="1">
<Averagespending hotel="Bonotel Antwerpen" day="1">101.67</AverageSpending>
<Averagespending hotel="Bonotel Antwerpen" day="2">321.67</AverageSpending>
(: ... etc... :)
<Averagespending hotel="Bonotel Antwerpen" day="2">255</AverageSpending>
</Reportresult>
However, I also would like to calculate the total average. So the sum of all averages divided by the 7 days to output something like
<TotalAverage>198,67</TotalAverage>
But I'm having trouble calculating this Total Average. In Java I would use a total variable and increment it with the calculated average each loop but obviously that doesn't work here. How would I be able to do this with XQuery? Thanks.
By example you can assign to a variable the first part of your request and calculate the total average on it and concat the both results :
xquery version "1.0";
<ReportResult week="1">
{ let $averages :=
for $x in (1 to 7)
return
for $stays in doc("report.xml")//Report/Days/Day[#id=$x]/Hotels/Hotel[#id=1]/Stays
let $average := avg(
for $v in $stays/Stay
return sum($v/Roomtype/#price) + sum($v/Facilities/Facility/#prijs) + sum($v/Formula/#price) + sum($v/Period/#price)
)
return
<AverageSpending hotel="Bonotel Antwerpen" day="{data($x)}">
{data(round-half-to-even($average, 2))}
</AverageSpending>
return ($averages,<TotalAverage>{avg($averages)}</TotalAverage>)
</Reportresult>

Resources