[CAIレシピ] XQuery+XPathサンプル

## はじめに

CAIを利用した開発を始めたばかりの方の中にはXQueryやXPathに馴染みのない方もいらっしゃると思います。そのような場合、Chat GPTなどの生成AIに教えてもらいながら実装を進める方針をお勧めします。

基本的な使い方を覚えておくと理解が早い、生成AIを有効活用しやすいと思いますので、この記事ではXQueryおよびXPathに関するサンプルコードを幅広く紹介したいと思います。


## 様々なXQuery+XPathのサンプル

<table border="0">
  <tr>
    <td colspan="3"><a href="#基本的なfor文の使い方"><strong>■基本的なfor文の使い方■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#For1">入力値をそのまま出力</a></td>
    <td><a href="#For2">ルート要素を加えて出力</a></td>
    <td><a href="#For3">要素位置の取得と出力</a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#ノード選択"><strong>■ノード選択■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#Select1">パス指定</a></td>
    <td><a href="#Select2">指定要素全て</a></td>
    <td><a href="#Select3">属性指定</a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#フィルタリング"><strong>■フィルタリング■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#filter1">要素値の指定</a></td>
    <td><a href="#filter2">属性値の指定</a></td>
    <td><a href="#"></a></td>
  </tr>
  <tr>
    <td><a href="#filter3">部分文字フィルタリング</a></td>
    <td><a href="#filter4">正規表現フィルタリング</a></td>
    <td><a href="#filter5">文字列長フィルタリング</a></td>
  </tr>
  <tr>
    <td><a href="#filter6">演算結果による条件指定1</a></td>
    <td><a href="#filter7">演算結果による条件指定2</a></td>
    <td><a href="#filter8">複数条件の指定</a></td>
  </tr>
  <tr>
    <td><a href="#filter9">位置指定1</a></td>
    <td><a href="#filter10">位置指定2</a></td>
    <td><a href="#filter11">指定範囲の取得</a></td>
  </tr>
  <tr>
    <td><a href="#filter12">先頭要素の取得</a></td>
    <td><a href="#filter13">先頭要素の除外</a></td>
    <td><a href="#filter14">最後尾要素の取得</a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#letによる変数の使用"><strong>■letによる変数の使用■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#let1">基本的なletの使用</a></td>
    <td><a href="#let2">forと組み合わせたletの使用</a></td>
    <td><a href="#let3">XQuery実行結果の代入</a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#whereによるフィルタリング"><strong>■whereによるフィルタリング■</strong></a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#並び替え"><strong>■並び替え■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#orderby1">昇順(ASC)</a></td>
    <td><a href="#orderby2">降順(DESC)</a></td>
    <td><a href="#"></a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#return"><strong>■return■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#return1">returnのネスト記述</a></td>
    <td><a href="#"></a></td>
    <td><a href="#"></a></td>
  </tr>
  <tr>
    <td colspan="3"><a href="#集合関数集合演算の利用"><strong>■集合関数/集合演算の利用■</strong></a></td>
  </tr>
  <tr>
    <td><a href="#aggregate1">要素数の取得</a></td>
    <td><a href="#aggregate2">合計値の取得</a></td>
    <td><a href="#aggregate3">平均値の取得</a></td>
  </tr>
  <tr>
    <td><a href="#aggregate4">和集合の取得(UNION)</a></td>
    <td><a href="#aggregate5">重複排除</a></td>
    <td><a href="#"></a></td>
  </tr>
</table>

なお、記載しているXQueryコードは全て[こちらのページ](https://www.videlibri.de/cgi-bin/xidelcgi)で動作確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074553/409f319f-13aa-9438-b225-e7205aa844bf.png)



### 基本的なfor文の使い方


```XQuery:入力値をそのまま出力(for...in)<a name="For1"></a>
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>
```




```XQuery:入力値をそのまま出力(ルート要素も加える)<a name="For2"></a>
<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>
```



```XQuery:要素位置の取得と出力(for...at...in)<a name="For3"></a>
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>
```



### ノード選択

```XQuery:パス指定による取得(/要素名/要素名)<a name="Select1"></a>
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>
```


```XQuery:指定要素全てを取得(//要素名)<a name="Select2"></a>
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>
```

```XQuery:属性指定による取得(@属性名)<a name="Select3"></a>
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>
```

### フィルタリング

```XQuery:要素値によるフィルタリング([要素=値])<a name="filter1"></a>
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>
```

```XQuery:属性値によるフィルタリング([@属性=値])<a name="filter2"></a>
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>
```




```XQuery:部分文字フィルタリング([contains(要素or@属性, 条件)])<a name="filter3"></a>
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>
```


```XQuery:正規表現フィルタリング([matches(要素or@属性, 条件)])<a name="filter4"></a>
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>
```



```XQuery:文字列長フィルタリング([string-length(要素or@属性, 条件)])<a name="filter5"></a>
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>
```



```XQuery:演算結果によるフィルタリング1([条件式])<a name="filter6"></a>
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>
```



```XQuery:演算結果によるフィルタリング2([条件式])<a name="filter7"></a>
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>
```



```XQuery:複数条件指定([条件式 and 条件式] や [条件式 or 条件式])<a name="filter8"></a>
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>
```


```XQuery:位置指定([position()=条件])<a name="filter9"></a>
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>
```


```XQuery:位置指定([]内で条件を指定, positionと同様)<a name="filter10"></a>
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>
```






```XQuery:指定範囲の取得(subsequence)<a name="filter11"></a>
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>
```


```XQuery:先頭要素の取得(head)<a name="filter12"></a>
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>
```


```XQuery:先頭要素を除外した取得(tail)<a name="filter13"></a>
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>
```


```XQuery:最後尾要素の取得([last()])<a name="filter14"></a>
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による変数の使用

変数を使用する場合 __$変数名 := 値__ の形式でコードを記述します。

```XQuery:基本的なletの使用<a name="let1"></a>
let $varA := "ABC"
return $varA

//実行結果
ABC
```



```XQuery:forと組み合わせたletの使用<a name="let2"></a>
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>
```

```XQuery:XQuery実行結果の代入<a name="let3"></a>
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ではいずれの方法も同じ条件を指定しており結果も同じです。

```XQuery:置換・型変換を伴うフィルタリング<a name="ｘｘｘ"></a>
//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内で複雑な条件式を指定した場合はコードの可読性が少し下がるようにも感じます。プロジェクトの開発規約作成時に、どのようなコーディングスタイルを推奨化するか検討しても良いかもしれませんね。



### 並び替え


```XQuery:昇順(ASC)<a name="order1"></a>
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>
```



```XQuery:降順(DESC)<a name="order2"></a>
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


```XQuery:returnのネスト記述<a name="return1"></a>
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>
```



### 集合関数/集合演算の利用


```XQuery:要素数の取得(count)<a name="aggregate1"></a>
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
```




```XQuery:合計値の取得(sum)<a name="aggregate2"></a>
sum(
    <books>
      <book><price>1000</price></book>
      <book><price>1500</price></book>
    </books>/book/price
)

//実行結果
2500
```


```XQuery:平均値の取得(avg)<a name="aggregate3"></a>
avg(
    <books>
      <book><price>1000</price></book>
      <book><price>1500</price></book>
    </books>/book/price
)

//実行結果
1250
```


```XQuery:和集合(UNION)(|)<a name="aggregate4"></a>
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>
```

```XQuery:重複排除(distinct-values)<a name="aggregate5"></a>
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>
```





## 参照

- [Qiita - IICS CAIの実装レシピ - マスターページ](https://qiita.com/shunsuzu/items/3d1971ef6e3b993516aa)