2012-02-22 4 views
6

Услышьте XML. Я пытаюсь получить Кол-во названий, опубликованном автором в диапазоне дат 15/02/2012 по 24/02/2012 заказать по наивысшему наименьшему (количество названий).Группировка и подсчет в Xquery

<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> 

Я пытаюсь получить выход из 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> 

Пожалуйста, помогите мне ..

+0

Это зависит от используемой версии XQuery. Какой процессор/база XQuery должен выполнять этот запрос? – Shcheklein

+0

Im, использующий кислород (Saxon-PE Xquery9.2.0.6) для разработки. Наконец, я должен запустить этот запрос через XCC api на Marklogic. – Pankaj

ответ

4

Это XQuery 1,0 решение исполняемым любой совместимый процессор XQuery 1.0:

Примечание: Нет group by и не distinct-values() не используются.

<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> 

при нанесении на поставленном XML документа:

<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> 

производит разыскиваемого, правильный результат:

<?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> 
+0

Большое спасибо, это работает именно так, как я хочу. – Pankaj

+0

@PankajL: Добро пожаловать. –

2

Следующая должны работать в большинстве процессоров. В MarkLogic можно найти более эффективные запросы, но это поможет вам начать работу.

let $doc := <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> 

return 
<results> 
    { 
     for $author in distinct-values($doc/entry/author/name/string()) 
     return 
     <result><author> 
      <name>{$author}</name> 
      <numberOfTitles>{count($doc/entry[author/name/string() eq $author])} </numberOfTitles> 
     </author></result> 
    } 
</results> 
+0

Вы можете добавить ограничение даты к предикату в таких записях, как $ doc/entry [author/name/string() eq $ author и XXXX]; замените XXX логикой, которая анализирует формат даты, который у вас есть, и делает необходимые сравнения. –

+0

Это не фильтрует даты и не сортирует, не так ли? –

+0

Нах, я был ленив, но я сделал бы это, сделав что-то похожее на ваш ответ. Добавление другого бита в предикат для фильтрации в диапазоне дат, а затем добавление порядка по счету ($ doc/entry [author/name/string() eq $ author]) для сортировки. –

4

Вот мое движение в растворе:

<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> 

Запущенный с BaseX, он дает правильный результат.

Он использует XQuery 3.0 features like group by, иначе это будет сложнее. Я не знаю, поддерживает ли MarkLogic это.

+1

+1 вы можете попробовать решение в прямом эфире по адресу http://www.zorba-xquery.com/html/demo#1SSNNQ3DYQ1USnlxWN8c9+67KJA= – wcandillon

2

Вот другое решение, которое похоже на Лео Wörteler:

declare function local:FormatDate($origDate as xs:string) as xs:date 
    { 
     xs:date(string-join(reverse(tokenize($origDate, '/')), '-')) 
    }; 

<results> 
    { 
    for $author in distinct-values(/entries/entry/author/name) 
    let $startDate := xs:date('2012-02-15') 
    let $endDate := xs:date('2012-02-24') 
    order by count(/entries/entry[author/name=$author][$startDate <= local:FormatDate(published) and local:FormatDate(published) <= $endDate]) descending 
    return 
    <result> 
     <author> 
     <name>{$author}</name> 
     </author> 
     <numberOfTitles>{count(/entries/entry[author/name=$author][$startDate <= local:FormatDate(published) and local:FormatDate(published) <= $endDate])}</numberOfTitles> 
    </result> 
    } 
</results> 
4

Это решение, специфичное для MarkLogic, с использованием карт для эффективной реализации группировки. Входной XML объявлен как $INPUT, но вы можете заменить его на вызов doc() или любым другим аксессуаром.

в прошлом году я исследовал эту тему в блоге: 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 } } } 
2

+1 на карты на основе решения. Другие решения имеют count(/entry/author[$name=xx])статья или другие XPath, вложенные в FLWOR, что фактически является вложенным циклом.Вложенные циклы вызывают O (N^2) производительность, которая может быть прекрасной в тестировании, а затем замедляется после увеличения размера данных.