Hear is XML. I am trying to get Number of titles published by an author in a date range 15/02/2012 to 24/02/2012 order by highest to lowest (number of titles).
<entries>
<entry>
<id>1</id>
<published>23/02/2012</published>
<title>Title 1</title>
<content type="html">This is title one</content>
<author>
<name>Pankaj</name>
</author>
</entry>
<entry>
<id>2</id>
<published>22/02/2012</published>
<title>Title 2</title>
<content type="html">This is title two</content>
<author>
<name>Pankaj</name>
</author>
</entry>
<entry>
<id>3</id>
<published>21/02/2012</published>
<title>Title 3</title>
<content type="html">This is title three</content>
<author>
<name>Rob</name>
</author>
</entry>
<entry>
<id>4</id>
<published>20/02/2012</published>
<title>Title 4</title>
<content type="html">This is title four</content>
<author>
<name>Bob</name>
</author>
</entry>
<entry>
<id>5</id>
<published>19/02/2012</published>
<title>Title 1</title>
<content type="html">This is title five</content>
<author>
<name>Pankaj</name>
</author>
</entry>
I am trying to get output from xquery:
<?xml version="1.0" encoding="UTF-8"?>
<results>
<result>
<author>
<name>Pankaj</name>
</author>
<numberOfTitles>3</numberOfTitles>
</result>
<result>
<author>
<name>Rob</name>
</author>
<numberOfTitles>1</numberOfTitles>
</result>
<result>
<author>
<name>Bob</name>
</author>
<numberOfTitles>1</numberOfTitles>
</result>
Please help me..
This XQuery 1.0 solution is executable by any compliant XQuery 1.0 processor:
Note: No group by
and no distinct-values()
are used.
<results>
{
let $entries :=
/*/entry
[for $d in
xs:date(string-join(reverse(tokenize(published, '/')), '-'))
return
xs:date('2012-02-15') le $d and $d le xs:date('2012-02-24')
],
$vals := $entries/author/name
return
for $a in $vals[index-of($vals, .)[1]],
$cnt in count(index-of($vals, $a))
order by $cnt descending
return
<result>
<author>
{$a}
</author>
<numberOfTitles>
{count(index-of($vals, $a))}
</numberOfTitles>
</result>
}
</results>
when applied on the provided XML document:
<entries>
<entry>
<id>1</id>
<published>23/02/2012</published>
<title>Title 1</title>
<content type="html">This is title one</content>
<author>
<name>Pankaj</name>
</author>
</entry>
<entry>
<id>2</id>
<published>22/02/2012</published>
<title>Title 2</title>
<content type="html">This is title two</content>
<author>
<name>Pankaj</name>
</author>
</entry>
<entry>
<id>3</id>
<published>21/02/2012</published>
<title>Title 3</title>
<content type="html">This is title three</content>
<author>
<name>Rob</name>
</author>
</entry>
<entry>
<id>4</id>
<published>20/02/2012</published>
<title>Title 4</title>
<content type="html">This is title four</content>
<author>
<name>Bob</name>
</author>
</entry>
<entry>
<id>5</id>
<published>19/02/2012</published>
<title>Title 1</title>
<content type="html">This is title five</content>
<author>
<name>Pankaj</name>
</author>
</entry>
</entries>
produces the wanted, correct result:
<?xml version="1.0" encoding="UTF-8"?>
<results>
<result>
<author>
<name>Pankaj</name>
</author>
<numberOfTitles>3</numberOfTitles>
</result>
<result>
<author>
<name>Rob</name>
</author>
<numberOfTitles>1</numberOfTitles>
</result>
<result>
<author>
<name>Bob</name>
</author>
<numberOfTitles>1</numberOfTitles>
</result>
</results>
Here is a solution specific to MarkLogic, using maps to implement grouping efficiently. The input XML has been declared as $INPUT
, but you could replace that with a call to doc()
or any other accessor.
I also explored this topic in a blog post last year: http://blakeley.com/blogofile/archives/560/
element results {
let $m := map:map()
let $start := xs:date('2012-02-15')
let $stop := xs:date('2012-02-24')
let $group :=
for $entry in $INPUT/entry
let $key := $entry/author/name/string()
let $date := xs:date(xdmp:parse-yymmdd("dd/MM/yyyy", $entry/published))
where $date ge $start and $date le $stop
return map:put($m, $key, 1 + (map:get($m, $key), 0)[1])
for $key in map:keys($m)
let $count := map:get($m, $key)
order by $count
return element result {
element author { element name { $key }},
element numberOfTitles { $count } } }
Here's my go at a solution:
<results>{
for $entry in //entry
let $date := xs:date(string-join(reverse(tokenize($entry/published, '/')), '-')),
$author := $entry/author/string()
where xs:date('2012-02-15') le $date and $date le xs:date('2012-02-24')
group by $author
order by count($entry) descending
return <result>{
<author>
<name>{$author}</name>
</author>,
<numberOfTitles>{count($entry)}</numberOfTitles>
}</result>
}</results>
When executed with BaseX, it yields the correct result.
It uses XQuery 3.0 features like group by
, otherwise it would be more complicated. I don't know if MarkLogic supports that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With