グルーピング

data.xml

<movies>
  <movie>
    <title>ミケの冒険</title>
    <cat>ミケ</cat>
    <cat>トラ</cat>
    <cat>クロ</cat>
  </movie>
  <movie>
    <title>タマの冒険</title>
    <cat>タマ</cat>
    <cat>ミケ</cat>
    <cat>トラ</cat>
  </movie>
</movies>

重複除去

fn:distinct-values(シーケンスのアイテム順は実装依存)を使って、バインド。

for $cat in fn:distinct-values(fn:doc("data.xml")/movies/movie/cat)

cat要素内容をテストしてtitle要素取得

fn:doc("data.xml")/movies/movie[cat=$cat]/title

クエリー式

上記のことをまとめる。

for $cat in fn:distinct-values(fn:doc("data.xml")/movies/movie/cat)
return
  <result cat="{$cat}">
    { fn:doc("data.xml")/movies/movie[cat=$cat]/title }
  </result>

結果XMLデータ

<result cat="ミケ">
  <title>ミケの冒険</title>
  <title>タマの冒険</title>
</result>
<result cat="トラ">
  <title>ミケの冒険</title>
  <title>タマの冒険</title>
</result>
<result cat="クロ">
  <title>ミケの冒険</title>
</result>
<result cat="タマ">
  <title>タマの冒険</title>
</result>

cat要素内容ごとにtitle要素がグルーピングされました。めでたし。

応用

カウントしてみる。ついでにlet節も使ってみよう。

let $movie := fn:doc("movies.xml")/movies/movie
for $cat in fn:distinct-values($movie/cat)
return
  <result cat="{$cat}" count="{fn:count($movie[cat=$cat])}">
    { $movie[cat=$cat]/title }
  </result>
<result cat="ミケ" count="2">
  <title>ミケの冒険</title>
  <title>タマの冒険</title>
</result>
<result cat="トラ" count="2">
  <title>ミケの冒険</title>
  <title>タマの冒険</title>
</result>
<result cat="クロ" count="1">
  <title>ミケの冒険</title>
</result>
<result cat="タマ" count="1">
  <title>タマの冒険</title>
</result>

それにしても、いちいちfn:doc()から書いてるの馬鹿らしいんですけど、ループ中のコンテキストとかどうなってんのこれ。ああ、教科書から探すのだるいなあ……。

ああ、そのためにlet節があるのか。早く言ってよもう。教科書の順序おかしいだろ。確かに「F」LWOR式だけども!