# トピック書き換え

多くのIoTデバイスは再設定やアップグレードに対応していないため、サブスクライブするトピックを変更することが困難です。この問題を解決するために、EMQXではトピック書き換え機能を導入しています。関連するルールを設定することで、EMQXは新しいメッセージを送信するクライアントや新しいトピックをサブスクライブするクライアントのサブスクライブトピックを自動的に書き換えます。

このトピック書き換え機能は、[保持メッセージ](./mqtt-retained-message.md)や[遅延パブリッシュ](./mqtt-delayed-publish.md)機能と組み合わせて利用できます。例えば、遅延パブリッシュを使用したい場合に、トピック書き換えを使ってメッセージを必要なトピックにリダイレクトすることが可能です。

::: tip
トピックの書き換えは、書き換え前に認可チェックが行われます。
:::

::: tip

トピック書き換えは、クライアントのサブスクライブ／アンサブスクライブ時に共有サブスクリプショントピックに対しては、実際のトピック部分にのみ適用されます。つまり、共有サブスクリプショントピックのプレフィックス `$share/<group-name>/` や `$queue` を除いた部分にのみ影響します。

例えば、クライアントが `$share/group/t/1` や `$queue/t/2` のような共有サブスクリプショントピックフィルターをサブスクライブ／アンサブスクライブする場合、EMQXは `$share/group/` や `$queue/` を無視して `t/1` や `t/2` の部分だけをマッチングおよび書き換えの対象とします。

共有サブスクリプションや `$queue` の詳細については、[共有サブスクリプション](./mqtt-shared-subscription.md)をご参照ください。

:::

## トピック書き換えルールの設定

EMQXのトピック書き換えルールは設定が必要です。複数のトピック書き換えルールを追加できます。ルール数に制限はありませんが、トピックを持つすべてのMQTTメッセージは再度ルールにマッチングされるため、高スループット環境ではルール数に比例してパフォーマンスオーバーヘッドが発生します。したがって、この機能は慎重に使用してください。

各トピックの書き換えルールのフォーマットは以下の通りです。

```bash
rewrite = [
  {
    action:       "all"
    source_topic: "x/#"
    dest_topic:   "x/y/z/$1"
    re:           "^x/y/(.+)$"
  }
]
```

各書き換えルールはフィルターと正規表現で構成されます。

書き換えルールは `publish`、`subscribe`、`all` の3種類に分かれます。`publish` ルールはPUBLISHメッセージのトピックにマッチし、`subscribe` ルールはSUBSCRIBEおよびUNSUBSCRIBEメッセージのトピックにマッチします。`all` ルールはPUBLISH、SUBSCRIBE、UNSUBSCRIBEメッセージのトピックすべてに有効です。

トピック書き換えが有効な状態で、PUBLISHメッセージなどのMQTTパケットを受信すると、EMQXはパケット内のトピックを使って設定ファイル内のルールのトピックフィルター部分と順にマッチングを試みます。マッチングに成功すると、正規表現を使ってトピック内の情報を抽出し、元のトピックをターゲット式に置き換えて新しいトピックを生成します。

ターゲット式では、正規表現から抽出した要素を `$N` 形式の変数で参照できます。ここで `$N` は正規表現で抽出したN番目の要素を意味し、例えば `$1` は最初の要素です。

また、ターゲット式では `${clientid}` をクライアントID、`${username}` をクライアントのユーザー名として使用できます。

なお、EMQXは設定ファイルの記述順にルールを読み込みます。複数のトピック書き換えルールのトピックフィルターに同時にマッチした場合は、最初にマッチしたルールでトピックを書き換えます。

ルール内の正規表現がMQTTパケットのトピックにマッチしない場合は書き換えに失敗し、他のルールは適用されません。したがって、MQTTパケットのトピックとトピック書き換えルールは慎重に設計する必要があります。

## 例

以下のトピック書き換えルールが設定ファイルに追加されているとします。

```bash
rewrite = [
  {
    action:       "all"
    source_topic: "y/+/z/#"
    dest_topic:   "y/z/$2"
    re:           "^y/(.+)/z/(.+)$"
  }
  {
    action:       "all"
    source_topic: "x/#"
    dest_topic:   "z/y/x/$1"
    re:           "^x/y/(.+)$"
  }
  {
    action:       "all"
    source_topic: "x/y/+"
    dest_topic:   "z/y/$1"
    re:           "^x/y/(\d+)$"
  }
]
```

このとき、以下の5つのトピックにサブスクライブするとします：`y/a/z/b`、`y/def`、`x/1/2`、`x/y/2`、`x/y/z`。

+ `y/def` はどのトピックフィルターにもマッチしないため、トピック書き換えは行われず、そのまま `y/def` トピックにサブスクライブします。  
+ `y/a/z/b` は `y/+/z/#` トピックフィルターにマッチするため、EMQXは最初のルールを適用します。正規表現で `[a、b]` の要素を抽出し、2番目の要素を `$2` に代入して `y/z/$2` に当てはめるため、実際には `y/z/b` トピックにサブスクライブします。  
+ `x/1/2` は `x/#` トピックフィルターにマッチするため、EMQXは2番目のルールを適用しますが、正規表現にマッチしないためトピック書き換えは行われず、実際には `x/1/2` トピックにサブスクライブします。  
+ `x/y/2` は `x/#` と `x/y/+` の2つのトピックフィルターに同時にマッチします。EMQXは設定ファイルを上から順に読み込むため、3番目のルールを優先的に適用します。正規表現により置換され、実際には `z/y/2` トピックにサブスクライブします。  
+ `x/y/z` も `x/#` と `x/y/+` の2つのトピックフィルターに同時にマッチしますが、3番目のルールの正規表現にマッチしないためトピック書き換えは行われず、実際には `x/y/z` トピックにサブスクライブします。なお、3番目の正規表現がマッチしなくても2番目のルールは適用されません。
