Kibanaでドキュメントに格納されたURLを元に画像を表示する

この記事は[Elastic Stack (Elasticsearch) Advent Calendar 2022](https://qiita.com/advent-calendar/2022/elasticsearch)の3日目の記事です。

たまに「Kibanaでドキュメントに格納されたURLを元に画像を表示したりできないの？」と聞かれることがあるので、知っている方法を2つまとめてみました。

## 事前準備

いつものdocker-elkでElastic Stackの環境を立ち上げます。

```bash
git clone https://github.com/deviantony/docker-elk.git
cd docker-elk
docker-compose up -d
```

http://localhost:5601 にアクセスし、以下のユーザでログインします。

- Username
    - elastic
- Password
    - changeme

続いて環境が立ち上がったので動作確認に使うドキュメントを用意します。
今回はQiitaのタグ[Elasticsearch](https://qiita.com/tags/elasticsearch)、[Kibana](https://qiita.com/tags/kibana)、[Logstash]()、[Beats](https://qiita.com/tags/beats)のアイコン画像URLをフィールドの値として持ったドキュメントをElasticsearchに投入します。[^1]

[^1]: Qiita内部で使われるS3の画像であればQiita記事やデモから直リンクしても許されると信じて…

Kibanaの左側メニューから`Management` => `Dev Tools` とクリックし、Console画面に移動します。
その後、Console画面の左側に以下を入力し、再生ボタンをクリックしてデータ投入を実行します。

```js
POST _bulk
{ "index" : { "_index" : "url-img-test", "_id" : "1" } }
{  "timestamp": "2022-12-03T12:00:00", "image-url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/be95b2f9fda6a23b9cdc631feeb312c3ec82b50b/large.jpg" }
{ "index" : { "_index" : "url-img-test", "_id" : "2" } }
{  "timestamp": "2022-12-23T12:00:00", "image-url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/f4467212e523c3d34006b843d426d0f7826c9a0b/large.jpg" }
{ "index" : { "_index" : "url-img-test", "_id" : "3" } }
{  "timestamp": "2022-12-23T12:00:00", "image-url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/cc168a39c1bf0a89267f6264646c172eb8fed6bf/large.jpg" }
{ "index" : { "_index" : "url-img-test", "_id" : "4" } }
{  "timestamp": "2022-12-23T12:00:00", "image-url": "https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/fd087ba57a2d7373dc5ee551acd88db34e401119/large.jpg" }
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/0fdb6e63-60bb-9f29-af47-ebfa16e81f9c.png)

TSVBを使う方法では必ず時系列フィールドが必要になるため、時系列フィールドとして`timestamp`を持たせることに注意が必要です。

最後にKibanaのData Viewとして`url-img-test`を作成します。
ここについては細かい手順は省きますが、時系列フィールドとして`timestamp`を指定するのを忘れないようにしてください。

これで準備は終了です。

## TSVBのMarkdown機能を使う

新規のダッシュボードを作成し、追加するVisualizeとしてTSVBを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/5bc2d4d1-2f13-eded-63ef-b5c89f8be522.png)

画面中央の表示するグラフ種類としてMarkdownを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/b6b2a341-2825-bd4c-7225-75522a098a2b.png)

画面下部で`Panel options`を選択し、設定を以下のようにします。

- Data
    - Data view
        - url-img-test
            - 可視化対象のインデックス名
        - Time field
            - 可視化に使う時系列フィールド

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/e3f7780b-99ed-c6c3-e9c7-3114785ad006.png)

続いて`Data`タブをクリックし、以下のようにAggregationを設定します。

- Metrics
    - Aggregation
        - Top Hit
    - Field
        - image-url.keyword
    - Size
        - 10
            - 現在表示したい時間区間に複数のドキュメントが含まれる場合、何件の画像を同時に表示したいか
    - Group by
        - Terms
    - By
        - image-url.keyword
    - Top
        - 10
            - 先程のSizeと同じサイズを指定するのがポイント

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/743ce5e0-84f2-74c2-5b34-ea56430302a7.png)

最後に`Markdown`タブで以下のコードを入力します。

```
{{#each _all}}
![]({{ last.formatted }})
{{/each}}
```

すると自動反映され、こんな感じで画像が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/1108db13-e753-87c7-f8e7-2ab1eb44d4cd.png)

自動で横に整列され、横幅的に入りきらなくなると改行されます。（画像では4つ目で改行が入っている）

ここで画像が表示されない場合は右上の表示期間に表示したいデータが含まれていない可能性が高いです。期間を確認しましょう。

Kibanaのダッシュボードに埋め込むとこんな感じになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/c1eedd05-b229-6aaa-ef23-b2c585bbd8ee.png)

## Kibana CanvasのMarkdown機能を使う

同様のことがCanvasでも実現できます。

新規にCanvasのWorkpadを作成し、`Add element`から`Text`を選択してテキスト要素を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/65d3d1db-7ca5-8b1c-5d5b-0d91f9a1e865.png)


要素をクリックして右側に表示されるメニューの`Data`タブをクリックし、`Demo Data`ではなく`Elasticsearch Documents`に変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/a9628b0e-8379-6b89-6a86-a8b765a338cc.png)

`index`に表示したいindexを指定、`Fields`から画像URLが格納されているフィールドを選択し、Saveをクリックして反映します。

その後、`Display`タブを選択し、以下のコードに既存の内容を書き換えて`Apply`します。

```
{{#each rows}}
![]({{image-url}})
{{/each}}
```

すると、こんな感じに表示できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264679/f4b12372-ccd5-a12a-bb55-fe90bedb88ef.png)

表示に関しての制約は恐らくTSVBのパターンと同じで、基本は横に画像を並べて横幅が入りきらなくなったら改行が入り、縦に入りきらない時は画面外の下の方に行ってしまうようです。（これはTSVBのMarkdownも同じ。）

## おわりに

以上、Kibanaでドキュメントに格納されたURLを元に画像を表示する方法の紹介でした。
ダッシュボード上で絞り込んだドキュメントに関連する画像を表示するユースケースなどで有用です。

また、TSVBの方法とCanvasの方法のどちらもカスタムCSSを設定できる仕組みを持っているので、画像の表示に関して異なるサイズの画像のサイズ調整などはある程度カスタマイズすることも可能そうです。

ただし制約として今回の方法はKibanaとは別に画像を配置するためのWEBサーバが用意されていないと使えません。
画像を配置するためのWEBサーバが存在しない場合には、画像をBASE64化した文字列をドキュメントに格納して表示する方法もあります。詳しくはlogz.io社のKibana Tips記事を参照ください。今回の内容との組み合わせで実現できると思います。

https://logz.io/blog/kibana-hacks/

さて、[Elastic Stack (Elasticsearch) Advent Calendar 2022](https://qiita.com/advent-calendar/2022/elasticsearch)ですが、明日の記事はまだ誰が執筆するか決まっていないようです。

もし、この記事を読んでいるあなたがElastic Stackに関する小ネタをお持ちだったら、ぜひ投稿してみてはいかがでしょうか！