手軽に始めるFIWARE入門。データ連携基盤が簡単に作れます

この記事はFIWAREを始めたい人の為の入門となります。手軽に始められるように簡単な例を挙げながら基本的な機能を紹介していきます。

FIWAREについて検索して資料を読んでもピンとこなくて、どこから始めたらいいかわからない人の為に書いています。


# はじめに

## 大まかな説明

FIWAREって何なの？という説明は本当に難しいことです。私は仕事で使うことになったから勉強することになったが、最初はこれがわからないことが多くて困難でした。やっとわかってきた今だからこのように、自分の納得できる説明と始め方を記事に残すことにしました。

簡単に言うと、FIWAREとは「**APIを作るプラットフォーム**」です。「プラットフォーム」と言ってもピンとこないならもっと簡単に「APIを作る手段」や「APIを作るツール」と言ってもいいです。用はこれを使うことでAPIが作れること。

:::note info
APIの意味がよくわからない方もいるかと思いますが、ここでの説明は割愛して、前提の知識として進めていきます。**FIWAREはAPIを構築する為にある**ものだから、まずAPIの意味を理解してからにしましょう。
:::

APIを作る手段は色々ありますが、FIWAREはその一つに過ぎないです。APIも目的や機能によって色んな種類に分けられますが、FIWAREで作れるAPIは「データ連携基盤」というものです。

これも又説明必要な単語ですが、**データ連携基盤**は簡単にいうと**データを提供する者**と**データを使う者**を**連携**（つまりやり取りの仲介）するAPIのことです。

- **データを提供する者**はFIWAREのプラットフォームにデータを預かる
- **データを使う者**はそのプラットフォームからデータを取得する

ただしデータ連携基盤はただ連携する役目でありデータベース自体ではないので、データを保存するデータベースも別として準備する必要があります。

イメージはこんな感じです。
```plantuml
!include <edgy/edgy>
$brandFacet("データ提供者1", u1)
$brandFacet("データ提供者2", u3)
$identityFacet("データ利用者1", u2)
$identityFacet("データ利用者2", u4)

$organisationFacet(" ") {
	$organisation("データ連携基盤", fiware)
}

$productFacet("データベース", db)

$flowDown(u1, fiware, "提供")
$flowLeft(fiware, u2, "取得")
$flowDown(u3, fiware, "提供")
$flowRight(fiware, u4, "取得")

$flowDown(fiware, db, "保存")
$flow(db, fiware, "読込")
```

又、複数のデータ連携基盤を関連付けして管理することもできます。複雑になりますが、こういうイメージです。

```plantuml
!include <edgy/edgy>
$brandFacet("データ提供者1", u1)
$brandFacet("データ提供者2", u3)
$identityFacet("データ利用者1", u2)
$identityFacet("データ利用者2", u4)

$organisationFacet(" ", b1) {
	$organisation("データ連携基盤1", fiware1)
}

$organisationFacet(" ", b2) {
	$organisation("データ連携基盤2", fiware2)
}

$productFacet("データベース1", db1)
$productFacet("データベース2", db2)

$flowDown(u1, fiware1, "提供")
$flowLeft(fiware1, u2, "取得")
$flowDown(u3, fiware2, "提供")
$flowRight(fiware2, u4, "取得")

$flowDown(fiware1, db1, "保存")
$flow(db1, fiware1, "読込")
$flowDown(fiware2, db2, "保存")
$flow(db2, fiware2, "読込")
$linkRight(fiware1, fiware2, "連携")
```

普段APIはrails、django、springなどウェブアプリケーションフレームワークによって作られることが多いが、もし作りたいAPIがデータ連携基盤ならFIWAREを使う方が手軽に作れます。その為に特化されたプラットフォームだから。

それにFIWAREによって作られたデータ連携基盤の間の連携はレジストレーションという方法を通じて、やりやすいというメリットがあります。もしどこでもFIWAREを使っていたら標準化されて、色々単純になるでしょう。

だからFIWAREは推奨されて、今でも普及していきます。

## FIWARE Orionとは

FIWAREを始める人にとって混乱しやすいのは、FIWAREの概念の曖昧さです。

実はFIWAREとはただの一つのソフトウェアやフレームワークのことではなく、色んなコンポーネントからなされるものです。

- FIWARE Orion
- FIWARE Cygnus
- FIWARE Draco
- FIWARE QuantumLeap
- FIWARE Wirecloud
- などなど

このように色んなコンポーネントがあって、目的によってこれらを組み合わせることで望んだAPIを作れます。

```plantuml
!include <edgy/edgy>
$architectureFacet("［　Ｆ Ｉ Ｗ Ａ Ｒ Ｅ　］") {
	$task("Cygnus")
	$purpose("Orion")
	$task("Draco")
	$task("QuantumLeap")
	$task("Wirecloud")
	$task("その他……")
}

```

ただし殆どの場合、必ず使われるのはFIWARE Orionです。これはFIWAREの中心のコンポーネントとも言えます。

OrionはOrion Context Brokerとも呼ばれます。Brokerは「仲介人」という意味です。つまりOrionの役目はコンテキスト（データ、情報）のやり取りを仲介することです。

名前の由来は星座の[オリオン座](https://ja.wikipedia.org/wiki/オリオン座)でしょうね。CygnusやDracoなど他のコンポーネントの名前も星座の名前が多いです。私は天文学出身なのでよく馴染んでいる名前です。

でも今オリオンというと、丁度放送中のこのアニメ……。
![orion_rubik.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/b01958c5-bca4-413e-9ec5-13d44188e84c.jpeg)
……全然関係ない話ですみません。

OrionはFIWAREの基本機能であり、他はFIWAREの拡張機能、と認識してもいいと思います。

以下の説明はFIWARE Orionの説明となります。この記事は入門なので、他のコンポーネントのことはここで一切触れません。FIWAREの理解はまずFIWARE Orionの理解からにしましょう。

## 勉強の為の資料に関して

FIWARE Orionの勉強の為に、FIWAREコミュニティの公式ドキュメントやチュートリアルがあります。
- [FIWARE Orionドキュメント](https://fiware-orion.letsfiware.jp)
- [ホーム - FIWARE Step-by-Step for NGSI-v2](https://fiware-tutorials.letsfiware.jp/)

ただし私はこれを読んでも正直難しすぎて殆ど飲み込めませんでした。その後deepseekに聞いたりqiitaの記事など色んなところから情報を集めたりして整理して漸く理解できたのです。

FIWAREの入門は全然簡単なものではないのです。幸い現在ネットで関連の資料が豊富なので読み漁るのも手です。相性もあるからどの入門がいいかは一概には言えないでしょう。私のこの記事もその資料の一つになれたら嬉しいです。

## NGSI v2とは

本題に入る前にもう一つ理解しておいた方がいいのはNGSIのことです。**NGSI**とはデータを記述する方式の一つであり、**JSONの一種**です。

そのNGSIは昔からあって、その後改善が行われて**NGSI v2**になって今広く使われています。そしてこのNGSI v2はFIWAREのデータのやり取りに使われている標準の形式となっています。

NGSI v2についてこの記事はおすすめです。

https://qiita.com/NKGWM/items/603bc49da608a12d257d

ただし、すぐちゃんと覚える必要がないと思います。「こういう書き方」だと認識しておけばいいです。

ではここから本番に入ります。


# FIWARE Orionのサーバー構築と起動

FIWARE Orionを使い始める方法は色々あります。これも入門しようとする人が戸惑いやすいところの一つですね。ここで私が一番簡単だと思っている方法を採用します。それはdockerを使うという方法です。

ただしdockerの知識がなくても大丈夫です。dockerが難しいということは私も同感。私も今までdockerを使ったことありません。今回ただdockerをインストールして、コマンドを打つだけで終わりなので、本格的なdockerの理解がなくてもいいです。

dockerを使ったことないという人も多いと思いますが、やはりこれを使うと便利です。FIWARE Orionの起動はOSや環境に依存するので、手動で設定するのはかなり面倒です。そこでdockerを使うとたった2行のコマンドですぐ済みます。

まずは[dockerのインストール](https://www.docker.com/ja-jp/)から始めましょう。その後コマンドラインで`docker`を打ってエラーが出なければ準備完了です。

そしてコマンドラインでこれを実行したらFIWARE Orionのサーバーが起動するはずです。（ここでインターネットで設定用ファイルを取得することになるので、暫く時間がかかります）
```sh
docker run --name mongo600 -d mongo:6.0
docker run -d --name orionza --link mongo600:mongodb -p 1026:1026 telefonicaiot/fiware-orion -dbURI mongodb://mongo600
```

起動した後、ちゃんと動いているかブラウザでこのURLにアクセスして確認できます。
`http://localhost:1026/v2`

もしこのようなJSONが現れたら、成功です。
```json
{"entities_url":"/v2/entities","types_url":"/v2/types","subscriptions_url":"/v2/subscriptions","registrations_url":"/v2/registrations"}
```

若しくは、Pythonのrequestsを使って同様なことをすることもできます。
```python
import requests,json

url = "http://localhost:1026/v2"
res = requests.get(url)
print(res) # 応答コードを表示
# わかりやすいjson形式で表示
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
<Response [200]>
{
  "entities_url": "/v2/entities",
  "types_url": "/v2/types",
  "subscriptions_url": "/v2/subscriptions",
  "registrations_url": "/v2/registrations"
}
```

ここに表示されたのは、ただこれから使うAPIのルーティングの一部です。

URLに`v2`がありますが、これはNGSI v2を使っているからです。だからこれからアクセスするAPIのURLは全部`http://localhost:1026/v2`から始めます。

dockerの起動に使ったコマンドに関しては簡単に説明すると、1行目はmongodbのデータベースサーバーを立てる為です。FIWARE Orion自体はただの仲介するものであり、データベース自体ではないから、データを保存する為にmongodbが使われます。

そして2行目こそ本当にOrionのサーバーを立てるコマンドです。その中で`mongo600`はmongodbのサーバーにつけられた任意の名前であり、好きに変えればいいです。`orionza`という名前もです。

`1026`はデフォルトのポート番号であり、普段はそのまま使っていいです。今回はローカルでサーバーを作ったから`http://localhost:1026`はFIWARE Orionのサーバーとなります。

こうやって立てられたFIWARE Orionによって、これからデータベースと接続してデータを**閲覧**、**取得**、**追加**、**削除**、**修正**などできます。こんなイメージです。

```plantuml
!include <edgy/edgy>
$brandFacet("データ提供者", u1)
$identityFacet("データ利用者", u2)
$organisationFacet("FIWARE Orion", fiware)
$productFacet("mongodb", dba)

$flowDown(u1, fiware, "追加")
$flowDown(u1, fiware, "削除")
$flowDown(u1, fiware, "修正")
$flowDown(fiware, dba, "保存")
$flowUp(dba, fiware, "読込")
$flowUp(fiware, u2, "閲覧")
$flowUp(fiware, u2, "取得")
```


# 使い始める

## データを入れる

今のデータベースの中にまだ何のデータも入っていないので、まずはデータを入れることから始めます。データ追加は`http://localhost:1026/v2/entities`の`post`メソッドでできます。

ここから先「**エンティティ**」`entity` / `entities`という単語がよく出てきますが、これは簡単に説明できる概念ではないので、見ていって自分で理解するしかないと思いますが、さっくりいうと「エンティティ」とはFIWAREにおいて「取り扱うデータの塊」と認識してもいいでしょう。つまりデータは「エンティティ」という形で扱われます。具体的にどんなものなのか例を読んでいくとわかってくると思います。

この`http://localhost:1026/v2/entities`はその名の通りエンティティを扱う為のURLです。`post`でエンティティを1つ追加することになります。

```plantuml
!include <edgy/edgy>
$brandFacet("データ提供者", u1)
$organisationFacet("FIWARE Orion", fiware)
$productFacet("mongodb", dba)
$flowRight(u1, fiware, "post")
$flowRight(fiware, dba, "保存")
```

ただし`post`に使うデータNGSI v2形式のJSONです。ここで書きやすいようにPythonでやりますが、cURLなど他の方法でも勿論いいです。実は公式サイトの例などでも主にcURLを使っているのですが、正直わかりづらいです。

Pythonの方がJSONとほぼ同じ書き方ができて、NGSIを扱う時直感的でわかりやすいはずだと思うのでPythonで説明することにします。ここでただリクエストを作る手段としてPythonを使うだけでPythonは必須ではなく、自分の使いしやすい方法を選べばいいです。大事なのは**特定のURL**に**適切なデータ**でリクエストを送ることです。

では、次のコードは「駅」の簡単データを登録する例となります。
```python
import requests

url = "http://localhost:1026/v2/entities"
data_dic = {
    "id": "hakataeki",
    "type": "Eki",
    "namae": {"value": "博多駅"},
    "ido": {"value": 33.590000},
    "keido": {"value": 130.420611}
}
res = requests.post(url,json=data_dic)
print(res)
```

リクエストの結果として`201`が返されたら成功です。
```typescript
<Response [201]>
```

これでデータが登録されているはずです。

## エンティティのデータの構造に関して

今回の例で入れたデータについて簡単な説明をします。このデータの入れ方はNGSI v2の形式です。色んな属性を入れるが、特別なのは`id`と`type`で、他の属性は任意です。

`id`はそのエンティティを示す重複不可のものであり、`type`はそのエンティティが何のエンティティかを説明するものです。

FIWAREでは色んなエンティティを全部扱うが、どんなデータかを区別するのは`type`です。例えば今回は駅のデータを登録したいので`type`は`Eki`にします。会社のデータを入れたい時は`Kaisha`などの名前の`type`でもいいです。

`id`も`type`もどんな名前にするかは自由ですが、使える文字は英文字`a-z` `A-Z`と数字`0-9`と`-`、`_`、`.`、`:`のみです。`type`は大文字で始めるという習慣もありますが、必須ではありません。

他の属性は特定の`type`のエンティティが持つべき値を入れる為です。例えば今回は駅の「名前」と「緯度」と「軽度」3つ入れました。`id`も`type`と違って、書き方は`"属性名": {"value": "属性の値"}`という形になって面倒に見えますが、それは属性はただの値だけではなく、データ型やメタデータの情報も入れられるからです。これについて後でもっと詳しく説明します。


## データ追加の失敗例

データ登録は色んな原因によって失敗で終わる可能性があります。その時エラーメッセージが返されて原因をわかることができます。

例えば`id`に使ってはいけない文字が入っている場合です。
```python
import requests

url = "http://localhost:1026/v2/entities"
data_dic = {"id": "東京駅?"}
res = requests.post(url,json=data_dic)
print(res,res.text)
```

```typescript
<Response [400]> {"error":"BadRequest","description":"Invalid characters in entity id"}
```

こうやってデータ追加が成功かどうかわかりますね。

## 入れたデータを表示してみる

`http://localhost:1026/v2/entities`に普通のgetでアクセスしたら全てのエンティティが表示されます。

```plantuml
!include <edgy/edgy>
$identityFacet("データ利用者", u1)
$organisationFacet("FIWARE Orion", fiware)
$productFacet("mongodb", dba)
$flowRight(u1, fiware, "get")
$flowLeft(dba, fiware, "読込")
$flowLeft(fiware, u1, "取得")
```

今データを入れたから、ブラウザなどで`http://localhost:1026/v2/entities`にアクセスしてみたら入れられたデータが表示されるはずです。ここで綺麗で読みやすい形で表示する為にPythonを使います。
```python
import requests,json

url = "http://localhost:1026/v2/entities"
res = requests.get(url)
print(res)
# 取得できたデータ
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
# ヘッダー
print(json.dumps(dict(res.headers),indent=2,ensure_ascii=False))
```

```typescript
<Response [200]>
[
  {
    "id": "hakataeki",
    "type": "Eki",
    "ido": {
      "type": "Number",
      "value": 33.59,
      "metadata": {}
    },
    "keido": {
      "type": "Number",
      "value": 130.420611,
      "metadata": {}
    },
    "namae": {
      "type": "Text",
      "value": "博多駅",
      "metadata": {}
    }
  }
]
{
  "Date": "Sat, 8 Mar 2025 10:39:39 GMT",
  "Fiware-Correlator": "8387bc64-ef98-11ef-9ada-0242ac110003",
  "Content-Type": "application/json",
  "Content-Length": "202"
}
```

出てきたエンティティは最初に入れたそのものですが、少し余計な部分が入っていますね。それは各属性の`type`と`metadata`です。紛らわしいですが、属性の`type`はエンティティの`type`とは別物です。属性の`type`はどんなデータ型かを示すものです。エンティティ登録の時にわざわざ`type`を入れなくても`Text`か`Number`か適切に決められるから今回は`value`だけ入れました。

ただし実際に`type`の名前は絶対ではなく、自分が勝手に作った任意の`type`でも何でもいいです。それに基本的にFIWARE Orion自体では`type`が考慮されません。例えば文字列なのに`"type": "Number"`にしてもエラーが出ません。

だから属性の`type`はただ区別する為のものだと思ってもいいです。しかし一部のFIWAREの追加機能では`type`が使われることもあります。

`metadata`に関してはただの追加で入れられる情報です。なくてもいいので、今回はとりあえず使いません。詳しい説明も割愛します。


# 指定のIDのエンティティの処理

## 1つのエンティティの情報を表示

`entities/{エンティティID}`へ`get`したら指定のIDのエンティティを取得することになります。
```python
import requests,json

url = "http://localhost:1026/v2/entities/hakataeki"
res = requests.get(url)
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
{
  "id": "hakataeki",
  "type": "Eki",
  "ido": {
    "type": "Number",
    "value": 33.59,
    "metadata": {}
  },
  "keido": {
    "type": "Number",
    "value": 130.420611,
    "metadata": {}
  },
  "namae": {
    "type": "Text",
    "value": "博多駅",
    "metadata": {}
  }
}
```

このAPIには色んなパラメータを入れることで表示がかわるので紹介していきます。

## 表示の形式を調整

`options`というパラメータに`keyValues`を入れることで属性の表示は簡潔になります。つまり`type`と`metadata`が省略されます。これはよく使われて便利です。
```python
import requests,json

url = "http://localhost:1026/v2/entities/hakataeki"
param_dic = {"options": "keyValues"}
res = requests.get(url,params=param_dic)
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
{
  "id": "hakataeki",
  "type": "Eki",
  "ido": 33.59,
  "keido": 130.420611,
  "namae": "博多駅"
}
```

## 表示する属性を選ぶ
`attrs`パラメータに属性名を入れたらその他の属性が表示されなくなります。複数の属性が欲しい場合はカンマ`,`で挟みます。ただし`id`と`type`は指定しなくても常に表示されます。
```python
import requests,json

url = "http://localhost:1026/v2/entities/hakataeki"
param_dic = {"options": "keyValues", "attrs": "ido,keido"}
res = requests.get(url,params=param_dic)
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
{
  "id": "hakataeki",
  "type": "Eki",
  "ido": 33.59,
  "keido": 130.420611
}
```

## エンティティを削除

`entities/{エンティティID}`に`delete`メソッドを使ったらそのIDエンティティを消すことになります。これは1つのエンティティを削除する基本的な方法となります。
```python
import requests

url = "http://localhost:1026/v2/entities/hakataeki"
res = requests.delete(url)
print(res)
```
削除が成功したら`204`が返されます。
```typescript
<Response [204]>
```


# エンティティの属性の処理

## 属性を追加

`entities/{エンティティID}/attrs`で指定のIDのエンティティの属性を弄ることになります。まず`post`メソッドで新しい属性を追加することができます。

例えば新しいエンティティを追加してからそのエンティティに新しい属性を追加してみます。
```python
import requests,json

url = "http://localhost:1026/v2/entities"
data_dic = {
    "id": "hakataramen",
    "type": "Ramen",
    "namae": {"value": "博多ラーメン"}
}
res = requests.post(url,json=data_dic)

url = "http://localhost:1026/v2/entities/hakataramen/attrs"
data_dic = {"nedan": {"value": 290}}
res = requests.post(url,json=data_dic)

url = "http://localhost:1026/v2/entities/hakataramen"
res = requests.get(url,params={"options": "keyValues"})
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
{
  "id": "hakataramen",
  "type": "Ramen",
  "namae": "博多ラーメン",
  "nedan": 290
}
```

今回のエンティティは「ラーメン」です。まず属性は名前だけ入れたが、その後「値段」を追加しました。因みに[290円という値段は本当に存在します！](https://www.google.com/search?q=290円+博多ラーメン)。

尚この`entities/{エンティティID}/attrs`のpostは既存の名前の所属を入れても問題ありません。その場合属性が書き換えられます。複数入れることもできます。


## 属性の値を取得

`entities/{エンティティID}/attrs`の`get`メソッドで指定のIDのエンティティの全部の属性が見られます。ただしエンティティの`id`と`type`が表示されません。`"options": "keyValues"`で値だけ表示することもできます。
```python
import requests,json

url = "http://localhost:1026/v2/entities/hakataramen/attrs"
res = requests.get(url,params={"options": "keyValues"})
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
{
  "namae": "博多ラーメン",
  "nedan": 290
}
```

特定の属性だけ欲しい場合は`entities/{エンティティID}/attrs/{属性名}`でもいいです。
```python
import requests,json

url = "http://localhost:1026/v2/entities/hakataramen/attrs/nedan"
res = requests.get(url,params={"options": "keyValues"})
print(json.dumps(res.json()))
```

```typescript
{"nedan": 290}
```

## 属性を更新

### 一つだけの属性を更新
`entities/{エンティティID}/attrs/{属性名}`の`patch`メソッドで指定のIDのエンティティのその属性を更新することができます。
```python
import requests,json

data_dic = {"value": 440}
url = "http://localhost:1026/v2/entities/hakataramen/attrs/nedan"
res = requests.put(url,json=data_dic)

url = "http://localhost:1026/v2/entities/hakataramen/attrs"
res = requests.get(url,params={"options": "keyValues"})
# 更新した後の結果を見る
print(json.dumps(res.json(),ensure_ascii=False))
```

```typescript
{"namae": "博多ラーメン", "nedan": 440}
```

### 複数の属性を更新／追加

上で書きましたが、`entities/{エンティティID}/attrs`の`post`メソッドで既存の属性に使うこともできます。その場合は追加ではなく、更新となります。しかも同時に複数の属性の追加と更新ができます。

```python
import requests,json

url = "http://localhost:1026/v2/entities/hakataramen/attrs"
data_dic = {
    "nedan": {"value": 600},
    "kcal": {"value": 358}
}
res = requests.post(url,json=data_dic)

url = "http://localhost:1026/v2/entities/hakataramen/attrs"
res = requests.get(url,params={"options": "keyValues"})
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
{
  "kcal": 358,
  "namae": "博多ラーメン",
  "nedan": 600
}
```

## 属性を削除

`entities/{エンティティID}/attrs/{属性名}`の`delete`メソッドで指定の属性を削除することができます。
```python
import requests

url = "http://localhost:1026/v2/entities/hakataramen"
res = requests.delete(url)
print(res)
```

```typescript
<Response [204]>
```


# エンティティのタイプに関して

## 登録された全部のタイプを取得

上述でも説明しましたが、FIWAREにおいてエンティティの`type`はエンティティを区別する為の存在です。同時に色んなエンティティを入れることができるからどんなエンティティか区別することが重要となります。

今までどんな`type`のエンティティが登録されているか`http://localhost:1026/v2/types`で調べられます。（このAPIは読み取り専用なので`get`メソッドしかない）

これでその`type`のエンティティの属性まで表示されますが、`type`の名前だけ欲しい場合はパラメータに`{"options": "values"}`入れます。

## 違うタイプで同じIDを持つエンティティの問題

本来エンティティの`id`は重複不可であるべきですが、実は`type`が違ったら`id`が重複でも大丈夫です。だから実際に「`type`と`id`が同時に重複は不可」と言った方が正しいです。

試しに同じ`id`を持つ違う`type`のエンティティ2つ追加して`type`一蘭を見てみます。
```python
import requests,json

url = "http://localhost:1026/v2/entities"
data_dic = {
    "id": "kaizukaeki",
    "type": "NishitetsuEki",
    "namae": {"value": "貝塚駅"}
}
res = requests.post(url,json=data_dic)

data_dic = {
    "id": "kaizukaeki",
    "type": "ChikatetsuEki"
}
res = requests.post(url,json=data_dic)

url = "http://localhost:1026/v2/types"
res = requests.get(url)
print(json.dumps(res.json(),indent=2))

# タイプの名前だけ取得
res = requests.get(url,params={"options": "values"})
print(json.dumps(res.json()))
```

```typescript
[
  {
    "type": "ChikatetsuEki",
    "attrs": {},
    "count": 1
  },
  {
    "type": "NishitetsuEki",
    "attrs": {
      "namae": {
        "types": [
          "Text"
        ]
      }
    },
    "count": 1
  }
]
["ChikatetsuEki", "NishitetsuEki"]
```

このように同じ`kaizukaeki`という`id`を持つ違う`ChikatetsuEki`と`NishitetsuEki`という`type`のエンティティが問題なく登録されましたが、これはややこしい問題の原因になるかもしれないので、注意する必要があります。

まず、重複の`id`があると、`entities/{エンティティID}`でのアクセスがエラーになります。エラーコードは`409`であり、「矛盾がある」ということが示されます。
```python
import requests,json

url = "http://localhost:1026/v2/entities/kaizukaeki"
res = requests.get(url)
print(res,json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
<Response [409]> {
  "error": "TooManyResults",
  "description": "More than one matching entity. Please refine your query"
}
```

このように同じ`id`があってどっちなのかわからなくなるからです。その場合`id`だけでなく`type`までパラメータのとして指定する必要があります。
```python
import requests,json

url = "http://localhost:1026/v2/entities/kaizukaeki"
res = requests.get(url,params={"type": "ChikatetsuEki"})
print(res,json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
<Response [200]> {
  "id": "kaizukaeki",
  "type": "ChikatetsuEki"
}
```

こうやって指定のエンティティにアクセスすることができます。

削除する時も同様です。`type`を指定しないとエラーになります。
```python
import requests

res = requests.delete(url)
print(res) # ここでエラー
res = requests.delete(url,params={"type": "NishitetsuEki"})
print(res) # type指定したから問題ない
res = requests.delete(url,params={"type": "ChikatetsuEki"})
print(res)
```

```typescript
<Response [409]>
<Response [204]>
<Response [204]>
```

要するに、違う`type`のエンティティで同じ`id`を持つことがないような命名をしていたらエンティティを指定する時に`type`を気にしなくても`id`を指定するだけで十分なはずですが、重複の可能性があったらいつも`type`を指定しておいたら無難でしょう。


# 一気に処理

## 複数のエンティティを追加、又は更新

大きいデータを扱う時に何件もののデータを一つずつ扱うよりも、一気にいっぱい扱った方が効果的です。その為にFIWARE Orionでは`op/update`の`post`メソッドが使われます。

この方法は追加も更新も削除も同じ`post`メソッドを使いますが、`actionType`というパラメータで区別します。追加／更新の場合は`"actionType": "append"`とします。

使う例。
```python
import requests

url = "http://localhost:1026/v2/op/update"
data_dic = {
    "actionType": "append",
    "entities": [
        {
            "id": "chashumen",
            "type": "Ramen",
            "namae": {"value": "チャーシューメン"},
            "nedan": {"value": 900}
        },
        {
            "id": "chahan",
            "type": "Gohan",
            "namae": {"value": "チャーハン"},
            "nedan": {"value": 750}
        }
    ]
}
res = requests.post(url,json=data_dic)
print(res)
```

```typescript
<Response [204]>
```

これで2つのエンティティが一気に追加されます。追加したいエンティティのデータはこのように`"entities": [  ]`というリストの形で入れます。

今回まだ存在していないから追加となりましたが、指定の`id`のエンティティが既存の場合はその`id`のエンティティを書き換えることになります。

## 複数のエンティティの属性を更新

既存のエンティティの更新は`"actionType": "append"`の他に、`"actionType": "update"`も使えます。ただし`append`の場合が新しいエンティティに入れ替えられるのに対し、`update`はただ指定の属性を更新するだけとなります。指定されていない属性はそのまま。
```python
import requests,json

url = "http://localhost:1026/v2/op/update"
data_dic = {
    "actionType": "update",
    "entities": [
        {
            "id": "chashumen",
            "nedan": {"value": 950}
        },
        {
            "id": "chahan",
            "nedan": {"value": 800}
        }
    ]
}
res = requests.post(url,json=data_dic)

# 変更した後の結果を見てみる
url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"options": "keyValues"})
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
[
  {
    "id": "chashumen",
    "type": "Ramen",
    "namae": "チャーシューメン",
    "nedan": 950
  },
  {
    "id": "chahan",
    "type": "Gohan",
    "namae": "チャーハン",
    "nedan": 800
  }
]
```

## 複数のエンティティを削除

`"actionType": "delete"`で指定の複数の`id`のエンティティを削除することができます。

```python
import requests

url = "http://localhost:1026/v2/op/update"
data_dic = {
    "actionType": "delete",
    "entities" :[
        {"id": "chashumen"},
        {"id": "chahan"}
    ]
}
res = requests.post(url,json=data_dic)
print(res)
```

```typescript
<Response [204]>
```


# エンティティ取得のオプション

## 件数を取得

最初から説明した`http://localhost:1026/v2/entities`の`get`ですが、実は色んなパラメータによって表示の調整ができるので、ここで補足します。

例えば`"options": "count"`を入れたら応答のヘッダーの中にエンティティの数が表示されます。どれくらいあるか知りたい時に使うと便利です。

例えばいっぱいエンティティを追加して数を表示させてみます。今回その為にPythonの`for`ループを使いますが、詳しくは割愛します。要はこれで「x1, x2, x3, ..., x1003, x1004」という`id`を持つ1004個のエンティティを`op/update`によって追加します。
```python
import requests,json

url = "http://localhost:1026/v2/op/update"
entity_list = [{"id": f"x{i}", "type": "X"} for i in range(1,1005)]
data_dic = {
    "actionType": "append",
    "entities" : entity_list
}
res = requests.post(url,json=data_dic)

url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"options": "count"})
# ヘッダーを表示
print(json.dumps(dict(res.headers),indent=2))
```

```typescript
{
  "Date": "Sat, 8 Mar 2025 13:04:40 GMT",
  "Fiware-Correlator": "93d0daa2-f11d-11ef-86de-0242ac110003",
  "Fiware-Total-Count": "1004",
  "Content-Type": "application/json",
  "Content-Length": "472"
}
```

## 正規表現でIDやタイプから検索

一部の`id`の名前のパターンを持つエンティティだけ欲しい場合は`idPattern`というパラメータを使うことでできます。パターンは正規表現で書きます。


前の例において追加されたエンティティで例を挙げます。
```python
import requests,json

url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"idPattern": "x50.*"})
print(json.dumps(res.json()))
```

```typescript
[{"id": "x50", "type": "X"}, {"id": "x500", "type": "X"}, {"id": "x501", "type": "X"}, {"id": "x502", "type": "X"}, {"id": "x503", "type": "X"}, {"id": "x504", "type": "X"}, {"id": "x505", "type": "X"}, {"id": "x506", "type": "X"}, {"id": "x507", "type": "X"}, {"id": "x508", "type": "X"}, {"id": "x509", "type": "X"}]
```

同じように`type`の名前で絞りたい場合は`typePattern`が使われます。

## ページング

`limit`パラメータは表示されるエンティティの数を指定するものとなります。ただし既定値は20となっています。つまり普段特に指定しない限りどれくらい沢山エンティティを持っていても20しか表示されません。又、`limit`は最大1000でそれ以上入れても1000しか出てきません。

`offset`というパラメータで何番目から表示されるか指定することができます。`limit`と`offset`を一緒に使うことでページ分けでエンティティを表示することができます。

使う例。
```python
import requests,json

url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"limit": 4})
print(json.dumps(res.json()))
res = requests.get(url,params={"limit": 4, "offset": 8})
print(json.dumps(res.json()))
```

```typescript
[{"id": "x1", "type": "X"}, {"id": "x2", "type": "X"}, {"id": "x3", "type": "X"}, {"id": "x4", "type": "X"}]
[{"id": "x9", "type": "X"}, {"id": "x10", "type": "X"}, {"id": "x11", "type": "X"}, {"id": "x12", "type": "X"}]
```

尚`"options": "count"`を使った場合のヘッダーの中ではこの`limit`と関係なく常に実際の数が表示されます。

## ［幕間］エンティティを全部削除

全部エンティティを削除するという便利な方法はFIWARE Orionの中で準備されていないらしいですが、今まで勉強した知識を使って、APIの組み合わせによって実装のコードが書けるでしょう。例えばPythonではこんな感じで書けばいいです。
```python
import requests

while 1:
    url = "http://localhost:1026/v2/entities"
    # 最大の1000件でエンティティのIDを取得
    res = requests.get(url,params={"attrs": "id", "limit": 1000})
    entity_list = res.json()

    # エンティティが存在しない、又は何かのエラーが出た場合はもう終わりでいい
    if(res.status_code!=200 or len(entity_list)==0):
        break

    # 取得したエンティティのIDを入れて削除を行う
    url = "http://localhost:1026/v2/op/update"
    data_dic = {
        "actionType": "delete",
        "entities" : entity_list
    }
    res = requests.post(url,json=data_dic)
```

## 並べ替え

基本的に追加した順でエンティティが表示されますが、`orderBy`パラメータで順番の基準にしたい属性を指定することができます。

例えばランダムの`r`という属性を持つエンティティを追加して、その順で表示させてみます。
```python
import requests,random

url = "http://localhost:1026/v2/op/update"
entity_list = []
for i in range(1,6):
    entity = {
        "id": f"r{i}",
        "type": "R",
        "r": {"value": random.random()}
    }
    entity_list.append(entity)
data_dic = {
    "actionType": "append",
    "entities" : entity_list
}
res = requests.post(url,json=data_dic)

url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"orderBy": "r", "options": "keyValues"})
print(res.text)
# "!"を入れたら上からの順になる
res = requests.get(url,params={"orderBy": "!r", "options": "keyValues"})
print(res.text)
```

```typescript
[{"id":"r3","type":"R","r":0.022399299},{"id":"r2","type":"R","r":0.18084051},{"id":"r4","type":"R","r":0.325210582},{"id":"r1","type":"R","r":0.332001331},{"id":"r5","type":"R","r":0.746004846}]
[{"id":"r5","type":"R","r":0.746004846},{"id":"r1","type":"R","r":0.332001331},{"id":"r4","type":"R","r":0.325210582},{"id":"r2","type":"R","r":0.18084051},{"id":"r3","type":"R","r":0.022399299}]
```


## クエリーで検索

`q`というパラメータで指定の条件でエンティティを絞り込むことができます。使い方は色々ありますが、今回は例として数値の属性の値の範囲で絞り込むことを試してみます。
```python
import requests,json

# まずはテストに使うデータを追加
url = "http://localhost:1026/v2/op/update"
data_dic = {
    "actionType": "append",
    "entities" :[
        {
            "id": "kitsunedon",
            "type": "Donburi",
            "namae": {"value": "きつね丼"},
            "nedan": {"value": 650}
        },
        {
            "id": "tanindon",
            "type": "Donburi",
            "namae": {"value": "他人丼"},
            "nedan": {"value": 700}
        },
        {
            "id": "tentojidon",
            "type": "Donburi",
            "namae": {"value": "天とじ丼"},
            "nedan": {"value": 800}
        }
    ]
}
res = requests.post(url,json=data_dic)

# 検索
url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"q": "nedan<=700", "options": "values"})
print(json.dumps(res.json(),ensure_ascii=False))
```

```typescript
[["きつね丼", 650], ["他人丼", 700]]
```

ここで`nedan<=700`は要するに「値段は700以下」という条件となります。


# ［幕間］dockerの補足

## サーバーの停止、起動、削除

ここまであまり説明せずに最初からdockerで立てたFIWARE Orionサーバーでやっていたのですが、ここでこう少し補足します。

今起動して使用していたサーバーを止めるにはこのコマンドを打つのです。
```sh
docker stop orionza
docker stop mongo600
```

これは一時的な停止となって、もう一度起動したいならこれを打つといいです。
```sh
docker start mongo600
docker start orionza
```

永遠に消したい場合は`rm`を使います。ただしこれは`stop`した状態でないとエラーになります。
```sh
docker rm orionza
docker rm mongo600
```

これを使うとmongodbに保存されたエンティティのデータも削除されて取り戻すことができなくなるので注意です。

## docker-composeを使うという方法

今まで使っていた方法の他にもう一つの使い方をここで紹介しておきたいです。

まずこのようなymlファイルを作っておくのです。
```docker-compose.yml
services:
  orionza:
    image: telefonicaiot/fiware-orion
    ports:
      - "1026:1026"
    depends_on:
      - mongo600
    command: -dbURI mongodb://mongo600

  mongo600:
    image: mongo:6.0
    command: --nojournal
```

書き方については割愛しますが、簡単にいうとymlでサーバーに関する設定をここに書いたのです。そしてこのymlを使ってこのように実行したら上の方法と同じようにサーバーが建てられます。
```sh
docker compose -f docker-compose.yml up -d
```

サーバーを止めて消したい時はこれを打つのです。
```sh
docker compose -f docker-compose.yml down
```

この方法を使うと、作りたいサーバーの設定は一つのファイルに纏められて使いやすいです。今回はただFIWARE Orionとmongodbの2つしかないのですが、特に沢山のサーバーを同時にある場合この方法を使った方が便利だというのは明らかになるでしょう。

次に説明する機能もそうだから、予めこれを説明しておくことにしました。

# レジストレーション

## レジストレーションの概要

FIWAREの中で**レジストレーション**（registration）というのは、別のデータ連携基盤からデータを取得することで複数のデータベースを連携する機能です。

例えば2のデータベースmongo-aとmongo-bを管理する2つのデータ連携基盤orion-aとorion-bがあるとしましょう。とあるデータ利用者はmongo-bの中のデータが欲しい場合、普段ならorion-bに対して要求を送る必要がありますが、もしorion-aがorion-bに対してレジストレーションをしておいたら、データ利用者はorion-aに要求を送ることでそのデータを取得することができます。

イメージとしてはこんな感じです。

```plantuml
!include <edgy/edgy>
$identityFacet("データ利用者", u)

$architectureFacet("mongo-a自身のデータの場合", b1) {
	$organisation("orion-a", fiware_a)
    $productFacet("mongo-a", db_a)
}

$architectureFacet("mongo-bが持つデータの場合", b2) {
	$organisation("orion-b", fiware_b)
    $productFacet("mongo-b", db_b)
}

$flowDown(u, fiware_a, "データ要求")
$flowUp(fiware_a, u, "データ取得")

$linkDown(fiware_a, fiware_b, "レジストレーション")
$flowDown(fiware_a, fiware_b, "データ要求")
$flowUp(fiware_b, fiware_a, "データ取得")

$flowLeft(db_a, fiware_a, "データ読込")
$flowLeft(db_b, fiware_b, "データ読込")
```

このように、FIWAREサーバーがレジストレーションで繋がっているから一つのFIWAREサーバーに接続することで他のFIWAREサーバーのデータも入手できます。その為の仕組みです。

## 2つのサーバーの準備

レジストレーション機能を実際に使ってみる為にはまず2つのサーバーを立てます。今回はdocker-composeで準備します。このようなymlファイルを使います。
```docker-compose.yml
services:
  orion-a:
    image: telefonicaiot/fiware-orion
    ports:
      - "1026:1026"
    depends_on:
      - mongo-a
    command: -dbURI mongodb://mongo-a

  mongo-a:
    image: mongo:6.0
    command: --nojournal

  orion-b:
    image: telefonicaiot/fiware-orion
    ports:
      - "1027:1026"
    depends_on:
      - mongo-b
    command: -dbURI mongodb://mongo-b

  mongo-b:
    image: mongo:6.0
    command: --nojournal
```

そしてこれを実行したら準備完了です。
```sh
docker compose -f docker-compose.yml up -d
```

ここでorion-aはデータ利用者が直接にやり取りするサーバーとして、orion-bが対象のデータが保存されてレジストレーションを通じて接続するサーバーとします。

こうやって2つのサーバーは立てられますが、デフォルトの1026ポートは1つのサーバーにしか使えないので、ここで2つ目のサーバーは1027を使います。
`http://localhost:1027/v2`でアクセスできます。

このように複雑になりますが、本番では2つのサーバーはこのように同じパソコンではなく、ちゃんと物理的に違う場所にあるはずなので設定は又別に違うでしょう。

## レジストレーションを追加

準備が整ったら次は実際に使ってみます。レジストレーションを追加するには`http://localhost:1026/v2/registraions`の`post`メソッドを使います。

```python
import requests

url = "http://localhost:1026/v2/registrations"
data_dic = {
    "description": "ラーメンデータの連携",
    "dataProvided": {
        "entities": [
            {
                "idPattern": ".*",
                "type": "Ramen"
            }
        ],
        "attrs": ["namae"]
    },
    "provider": {
        "http": {
            "url": "http://orion-b:1026/v2"
        }
    }
}
res = requests.post(url,json=data_dic)
print(res)
```

```typescript
<Response [201]>
```

そしてデータをorion-bの方に入れておきます。
```python
import requests

url = "http://localhost:1027/v2/op/update"
data_dic = {
    "actionType": "append",
    "entities": [
        {
            "id": "tonkotsuramen",
            "type": "Ramen",
            "namae": {"value": "とんこつラーメン"},
            "nedan": {"value": 650}
        },
        {
            "id": "misoramen",
            "type": "Ramen",
            "namae": {"value": "みそラーメン"},
            "nedan": {"value": 750}
        }
    ]
}
res = requests.post(url,json=data_dic)
```

どのエンティティを要求するかを決めるのは`dataProvided`のところです。その中で`entities`で`type`と`id`を指定します。今回はある`type`のあらゆる`id`のエンティティが欲しいということで`"idPattern": ".*"`にします。そして`attrs`を入れることで取得したい属性だけ取ることができますが、これは省略できます。その場合全部の属性が取得されます。

そして`provider`でデータ元のサーバーのURLを指定します。

ただしここで注意して欲しいのは、データを追加する時に`http://localhost:1027/v2`というURLにアクセスするのに対し、レジストレーションの時は`"url": "http://orion-b:1026/v2"`と書いている、ということです。ややこしいですが、これはdockerを使っているからです。

実はどっちもorion-bサーバーを指すURLですが、`http://orion-b:1026`はdockerの間に使うURLです。レジストレーションで通じる時にorion-aとorion-bは勝手にやり取りするので、その時このURLを使うのです。それに対しデータを追加する時はそうではないので、普通に`http://localhost:1027/`を使います。

次は実際にデータ取得してみましょう。
```python
import requests,json

url = "http://localhost:1026/v2/entities"
res = requests.get(url,params={"type": "Ramen", "options": "keyValues"})
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
[
  {
    "id": "tonkotsuramen",
    "type": "Ramen",
    "namae": "とんこつラーメン"
  },
  {
    "id": "misoramen",
    "type": "Ramen",
    "namae": "みそラーメン"
  }
]
```

このように無事でorion-bのデータをorion-aから取得することができました。


## レジストレーションを全部表示

`registrations`の`get`メソッドで、追加したレジストレーションの詳細を全て表示することができます。
```python
import requests,json

url = "http://localhost:1026/v2/registrations"
res = requests.get(url)
print(json.dumps(res.json(),indent=2,ensure_ascii=False))
```

```typescript
[
  {
    "id": "67bef7f9a35f42af3e085d2f",
    "description": "ラーメンデータの連携",
    "dataProvided": {
      "entities": [
        {
          "idPattern": ".*",
          "type": "Ramen"
        }
      ],
      "attrs": [
        "namae"
      ]
    },
    "provider": {
      "http": {
        "url": "http://orion-b:1026/v2"
      },
      "supportedForwardingMode": "all",
      "legacyForwarding": false
    },
    "status": "active"
  }
]
```

## レジストレーションを削除

`registraions/{レジストレーションID}`の`delete`メソッドレジストレーションを消すことができますが、ここで`id`が必要です。しかし追加の時に`id`は勝手に決められたものなので、私達は`id`がわかるわけではないですね。だったらまず`id`を調べた上でやるしかないでしょう。

例えば全部のレジストレーションを削除したい場合はPythonでこうしたらいいでしょう。
```python
url = "http://localhost:1026/v2/registrations"
# レジストレーションの一蘭を取得
res = requests.get(url,params={"options": "keyValues"})
# IDだけ取って1つずつ削除する
for r in res.json():
    url = "http://localhost:1026/v2/registrations/"+r["id"]
    res = requests.delete(url)
```


# 終わりに

これでFIWAREの基本的な機能を一通り紹介できたと思います。それでもまだ触れていないところはいっぱいあります。全てはまだ始まったばかりです。

実はまだ沢山書きたい内容があるが、長すぎるので書くなら別の記事にします。サブスクリプションのこととか。

私は仕事でFIWAREを使うことになっているので、まだまだ勉強してFIWAREに関する記事を書いていきます。

もっと読みたい人は公式ドキュメントや下の載っている参考の記事や資料を読んでもいいと思います。



# 参考

### FIWARE関連
- [スマートシティを実現するFIWAREとは](https://tech.dentsusoken.com/entry/2021/12/20/スマートシティを実現するFIWAREとは)
- [FIWAREとは](https://qiita.com/lisa_t/items/d78d5a86b204ccefc907)
- [誰でもすぐに試せる初心者向けFIWARE学習コンテンツを作成した話](https://qiita.com/ryo-ma/items/a80e77de8c256d278ad6)
- [無知から学ぶFIWARE #1 ～ FIWAREってなんぞや？](https://qiita.com/waarrk/items/5f190c77fdda653e7619)
- [無知から学ぶFIWARE #2 ～ GEをひとつ動かしてみる](https://qiita.com/waarrk/items/9d2431c39043c158156e)
- [Fiware Orion Context Brokerに関する6つの誤解](https://qiita.com/MasaGon/items/d717fddb429aaa6d156a)
- [FIWARE エコシステムについて](https://qiita.com/ot-mikami/items/0b91622f3f58d6271c79)
- [PCで FIWARE ( Orion Context Broker ) を体験する、 MIRACLE LINUX 8.4 も試してみる](https://qiita.com/tagucchan/items/b53f82d05d28afc73b83)
- [IoTプラットフォームをOSSで手軽に構築する　その１](https://qiita.com/okinawa-openlab/items/a9325603723a4808b5d5)
- [メモ: FIWARE](https://qiita.com/tukiyo3/items/d0d393de6fe113af3276)
- [FIWARE Orion Context Broker コンテキスト情報管理 (Orion 3.6.0対応)](https://www.slideshare.net/slideshow/orion-context-broker-20220301-251267820/251267820)
- [データ利活用基盤サービス（FIWARE）アプリケーション開発ガイド](https://www.city.toyama.lg.jp/_res/projects/default_project/_page_/001/003/046/10_siryo.pdf)

その他に、一部の情報とサンプルコードはdeepseekに聞いて得られたものです。

### その他
- [RequestsモジュールのHTTPリクエストメソッドに必要な引数を理解せよ](https://qiita.com/moonwalkerpoday/items/0c31c35588df49a5ca57)
- [Python Requestsモジュールについて](https://qiita.com/hththt/items/14bfc2bf23192b020371)
- [Docker runでportオプション（-p 8080:8080）や-dオプションする意味〜初心者向けにコマンドの解説〜](https://qiita.com/hirokihello/items/69df88ba55125c2aed39)