はじめに
CAIを利用した開発を始めたばかりの方の中にはXQueryやXPathに馴染みのない方もいらっしゃると思います。そのような場合、Chat GPTなどの生成AIに教えてもらいながら実装を進める方針をお勧めします。
基本的な使い方を覚えておくと理解が早い、生成AIを有効活用しやすいと思いますので、この記事ではXQueryおよびXPathに関するサンプルコードを幅広く紹介したいと思います。
様々なXQuery+XPathのサンプル
なお、記載しているXQueryコードは全てこちらのページで動作確認できます。
基本的なfor文の使い方
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book
return $book
//実行結果
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
<books>
{
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book
return $book
}
</books>
//実行結果
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>
for $book at $index in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book
return
<book>
<index>{$index}</index>
<title>{string($book/title)}</title>
</book>
//実行結果
<book>
<index>1</index>
<title>Book 1</title>
</book>
<book>
<index>2</index>
<title>Book 2</title>
</book>
ノード選択
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book/title
return $book
//実行結果
<title>Book 1</title>
<title>Book 2</title>
for $book in
<books>
<book>
<title>Book 1</title>
<price>1000</price>
</book>
<book>
<title><price>777</price></title>
<price>1500</price>
</book>
</books>//price
return $book
//実行結果
<price>1000</price>
<price>777</price>
<price>1500</price>
for $book in
<books>
<book type="tech">
<title>Book 1</title>
</book>
<book type="language">
<title>Book 2</title>
</book>
</books>/book/@type
return
<type>{string($book)}</type>
//実行結果
<type>tech</type>
<type>language</type>
フィルタリング
for $book in
<books>
<book>
<title>Book 1</title>
<price>1000</price>
</book>
<book>
<title>Book 2</title>
<price>1500</price>
</book>
</books>/book[title='Book 2']/price
return
$book
//実行結果
<price>1500</price>
for $book in
<books>
<book type="tech">
<title>Book 1</title>
</book>
<book type="language">
<title>Book 2</title>
</book>
<book type="tech">
<title>Book 3</title>
</book>
</books>/book[contains(@type, 'te')]/title
return
$book
//実行結果
<title>Book 1</title>
<title>Book 3</title>
for $book in
<books>
<book type="tech">
<title>Book 1</title>
</book>
<book type="language">
<title>Book 2</title>
</book>
<book type="tech">
<title>Book 3</title>
</book>
</books>/book[contains(@type, 'te')]/title
return
$book
//実行結果
<title>Book 1</title>
<title>Book 3</title>
for $book in
<books>
<book type="tech">
<title>Book 1</title>
</book>
<book type="language">
<title>Book 2</title>
</book>
<book type="tech">
<title>Book 3</title>
</book>
</books>/book[matches(@type, '^t..h$')]/title
return
$book
//実行結果
<title>Book 1</title>
<title>Book 3</title>
for $book in
for $book in
<books>
<book type="tech">
<title>Book 1</title>
</book>
<book type="language">
<title>Book 2</title>
</book>
<book type="tech">
<title>Book 3</title>
</book>
</books>/book[string-length(@type) > 4]/title
return
$book
//実行結果
<title>Book 2</title>
for $book in
<books>
<book>
<title>Book 1</title>
<price>1000</price>
</book>
<book>
<title>Book 2</title>
<price>1500</price>
</book>
</books>/book[price+500<=1500]/title
return
$book
//実行結果
<title>Book 1</title>
for $book in
<books>
<book>
<title>Book 1</title>
<price>1000</price>
<discount>500</discount>
</book>
<book>
<title>Book 2</title>
<price>1500</price>
<discount>1100</discount>
</book>
</books>/book[price - discount >= 500]/title
return
$book
//実行結果
<title>Book 1</title>
for $book in
<books>
<book type="tech">
<title>Book 1</title>
<price>1000</price>
</book>
<book type="language">
<title>Book 2</title>
<price>1500</price>
</book>
<book type="tech">
<title>Book 3</title>
<price>2000</price>
</book>
</books>/book[price=1000 or @type="language"]/title
return
$book
//実行結果
<title>Book 1</title>
<title>Book 2</title>
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book[position()=1]/title
return
$book
//実行結果
<title>Book 1</title>
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book[2]/title
return
$book
//実行結果
<title>Book 2</title>
subsequence((
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
<book><title>Book 3</title></book>
<book><title>Book 4</title></book>
</books>/book
), 2, 2)/title
//実行結果
<title>Book 2</title>
<title>Book 3</title>
head(
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
<book><title>Book 3</title></book>
</books>/book
)
//実行結果
<book>
<title>Book 1</title>
</book>
tail(
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
<book><title>Book 3</title></book>
</books>/book
)
//実行結果
<book>
<title>Book 2</title>
</book>
<book>
<title>Book 3</title>
</book>
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book[last()]/title
return
$book
//実行結果
<title>Book 2</title>
letによる変数の使用
変数を使用する場合 $変数名 := 値 の形式でコードを記述します。
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book
let $varA := $book/title
return $varA
//実行結果
<title>Book 1</title>
<title>Book 2</title>
let $varA := (
for $book in
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book[title="Book 2"]
return
$book
)
return $varA
//実行結果
<book><title>Book 2</title></book>
whereによるフィルタリング
XPathによりフィルタリングする方法のほか、where句によりフィルタリングすることも可能です。例えば、次の3つのXQueryではいずれの方法も同じ条件を指定しており結果も同じです。
//XPathでフィルタリング
for $book in
<books>
<book><title>Book 1</title><price>'100'</price></book>
<book><title>Book 2</title><price>"200"</price></book>
<book><title>Book 3</title><price>'50'</price></book>
</books> /book[number(replace(replace(price, "'", ""), '"', "")) = 200]
return string($book/title)
//where句でフィルタリング
for $book in
<books>
<book><title>Book 1</title><price>'100'</price></book>
<book><title>Book 2</title><price>'200'</price></book>
<book><title>Book 3</title><price>'50'</price></book>
</books> /book
where number(replace(replace($book/price, "'", ""), '"', "")) = 200
return string($book/title)
//いったん変数化してwhere句でフィルタリング
for $book in
<books>
<book><title>Book 1</title><price>'100'</price></book>
<book><title>Book 2</title><price>'200'</price></book>
<book><title>Book 3</title><price>'50'</price></book>
</books> /book
let $varA := number(replace(replace($book/price, "'", ""), '"', ""))
where $varA = 200
return string($book/title)
個人の好みもよると思いますが、XPath内で複雑な条件式を指定した場合はコードの可読性が少し下がるようにも感じます。プロジェクトの開発規約作成時に、どのようなコーディングスタイルを推奨化するか検討しても良いかもしれませんね。
並び替え
for $book in
<books>
<book><price>1000</price></book>
<book><price>2000</price></book>
<book><price>1500</price></book>
</books>/book
order by $book/price ascending
return
$book
//実行結果
<book><price>1000</price></book>
<book><price>1500</price></book>
<book><price>2000</price></book>
for $book in
<books>
<book><price>1000</price></book>
<book><price>2000</price></book>
<book><price>1500</price></book>
</books>/book
order by $book/price descending
return
$book
//実行結果
<book><price>2000</price></book>
<book><price>1500</price></book>
<book><price>1000</price></book>
return
let $numbers := 1 to 5
return
<abc>
{
for $num in $numbers
return
element {'val' || $num} {$num}
}
</abc>
//実行結果
<abc>
<val1>1</val1><val2>2</val2><val3>3</val3><val4>4</val4><val5>5</val5>
</abc>
集合関数/集合演算の利用
count(
<books>
<book type="tech">
<title>Book 1</title>
<price>1000</price>
</book>
<book type="language">
<title>Book 2</title>
<price>1500</price>
</book>
</books>/book
)
//実行結果
2
sum(
<books>
<book><price>1000</price></book>
<book><price>1500</price></book>
</books>/book/price
)
//実行結果
2500
avg(
<books>
<book><price>1000</price></book>
<book><price>1500</price></book>
</books>/book/price
)
//実行結果
1250
let $bookA :=
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
</books>/book
let $bookB :=
<books>
<book><title>Book 2</title></book>
<book><title>Book 3</title></book>
</books>/book
return $bookA | $bookB
//実行結果
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
<book><title>Book 2</title></book>
<book><title>Book 3</title></book>
let $book :=
<books>
<book><title>Book 1</title></book>
<book><title>Book 2</title></book>
<book><title>Book 2</title></book>
<book><title>Book 3</title></book>
</books> /book
return
for $title in distinct-values($book/title)
return
<title>{$title}</title>
//実行結果
<title>Book 1</title>
<title>Book 2</title>
<title>Book 3</title>