Sample Solutions for the Exercises of Lab 5 ==================================================================================== These are possible ways to express the queries of Lab 5. There are also many equivalent solutions. ------------------------------------------------------------------------------------ 1. Return a list of elements, containing elements and , ordered alphabetically by population. {for $c in doc("countries.xml")//country order by string($c/@name) return {$c/@name} {$c/@population} } ------------------------------------------------------------------------------------ 2. Return a similar list, ordered by population in descending order. {for $c in doc("countries.xml")//country order by number($c/@population) descending return {$c/@name} {$c/@population} } ------------------------------------------------------------------------------------ 3. Return a similar list, again ordered by population in descending order, with an additional attribute rank, which indicates the position of the country on the list. Try two approaches, one using the construct for $var at $pos in and another one without. Note that you may have to use the casting functions number and string. Approach 1: ----------- {let $cs := (for $c in doc("countries.xml")//country order by number($c/@population) descending return $c) for $c0 in $cs let $pop := $c0/@population let $pos := count(doc("countries.xml")//country[@population >= $c0/@population]) return {attribute position {$pos}, attribute test {$pop}, element name {string($c0/@name)} } } Note: we need the casting function number(.) because the free version of Saxon is not schema-aware. Clearly, for a large data set, our implementation of the numbering scheme would be inefficient. Instead, we can use the built-in construct "at $pos", as in Approach 2, below. Approach 2: ----------- {let $cs := (for $c in doc("countries.xml")//country order by number($c/@population) descending return $c) for $c0 at $pos in $cs return {attribute position {$pos}, attribute population {$c0/@population}, element name {string($c0/@name)} } } ------------------------------------------------------------------------------------ 4. Return a list of city elements, containing the name of the city, such that each city has an attribute population and another attribute country. The cities are returned according to their population, in descending order. let $doc := doc("countries.xml") for $city in $doc//city order by number($city/population) descending return element city{ attribute country {$city/../@name}, attribute population {$city/population}, string($city/name) } ------------------------------------------------------------------------------------ 5. Restructure the document by listing countries according to population, cities within each country according to population, and languages within each country according to percentage. Try to reuse as much as possible existing elements and attributes instead of creating new ones. (Creation of nodes is usually a costly operation in XQuery engines.) let $doc := doc("countries.xml") let $cs := $doc//country return element countries { for $c in $cs order by number($c/@population) descending return element country { $c/@*, for $city in $c/city order by number($city/population) descending return $city, for $l in $c/language order by number($l/@percentage) descending return $l } } ------------------------------------------------------------------------------------ 6. Add to each country element new attributes that record the number of cities in the country and the number of languages spoken in the country. let $doc := doc("countries.xml") let $cs := $doc//country return element countries { for $c in $cs order by number($c/@population) descending return element country { $c/@*, attribute numberofcities {count($c/city)}, attribute numberoflangs {count($c/language)}, for $city in $c/city order by number($city/population) descending return $city, for $l in $c/language order by number($l/@percentage) descending return $l } } ------------------------------------------------------------------------------------ 7. Return an element with a list of elements, alphabetically sorted, where each language from the document occurs exactly once. let $doc := doc("countries.xml") let $ls := distinct-values($doc//language) return {for $l in $ls order by $l return {attribute name {$l}} } ------------------------------------------------------------------------------------ 8. Modify the previous query so that you return only those languages that are spoken by at least 10 percent of the population in those countries where they occur. let $doc := doc("countries.xml") let $ls := distinct-values($doc//language) return {for $l in $ls where every $lan in $doc//country/language[.=$l] satisfies $lan/@percentage >= 10 order by $l return {attribute name {$l}} } ------------------------------------------------------------------------------------ 9. Extend query number 7 so that each language element contains a list of country elements, such that the language is spoken in the country. let $doc := doc("countries.xml") let $ls := distinct-values($doc//language) let $cs := $doc//country return {for $l in $ls order by $l return {attribute name {$l}} {for $c in $cs[language=$l] order by $c/@name return {$c/@name} } } ------------------------------------------------------------------------------------ 10. Extend the previous query so that you return with every country element below a language the number of speakers of the language in that country. You may again need a casting function (xs:int casts an argument as an integer, whenever that is possible; xs is the default prefix for the XML Schema namespace). let $doc := doc("countries.xml") let $ls := distinct-values($doc//language) let $cs := $doc//country return {for $l in $ls order by $l return {attribute name {$l}} {for $c in $cs[language=$l] order by $c/@name return {$c/@name} {attribute speakers {xs:int(($c/@population div 100) * $c/language[.=$l]/@percentage) }} } }