Redashで独自のQuery Runnerを作る

この記事はRedash Advent Calendar 2017 8日目の記事です。

qiita.com

独自のQuery Runnerを作る

いきなり元ネタを出しますが、Redashの開発者Arikさんによる、以下の投稿を見るとだいたい作り方の雰囲気がわかってきます。

discuss.redash.io

上記の記事を参考に、クエリの代わりに文字列を貼り付けると、その文字列をCSVのようにパースして結果を返すような、ちょっと変わったQuery Runnerを作成します。

開発環境

細かい開発環境構築方法の説明は割愛しますので、ご了承ください。

以下の公式ドキュメントに開発環境の構築手順が記載されているため、この手順に沿って、Docker上の開発環境が整っていることを前提に進めます。

Docker Based Developer Installation Guide · Redash Help Center

なお、使用しているRedashのバージョンは master ブランチの以下のコミット時点のもので、v4.x系に該当するものだと思います。

github.com

サンプルコード

以下のスクリプトredash/query_runner/csv_parser.py として保存します。

import csv as csv
import json

from redash.query_runner import BaseQueryRunner, register


class CsvParser(BaseQueryRunner):
    @classmethod
    def configuration_schema(cls):
        return {
            'type': 'object',
            'properties': {
                'delimiter': {
                    'type': 'string',
                    'title': 'Delimiter'
                }
            }
        }

    @classmethod
    def annotate_query(cls):
        return False

    def __init__(self, configuration):
        super(CsvParser, self).__init__(configuration)

    def test_connection(self):
        pass

    def run_query(self, query, user):
        data = {
            'columns': [],
            'rows': [],
        }

        delimiter = str(self.configuration.get('delimiter'))

        for row in csv.DictReader(query.strip().splitlines(), delimiter=delimiter):
            if len(data['columns']) == 0:
                for key in row.keys():
                    data['columns'].append({'name': key, 'friendly_name': key})

            data['rows'].append(row)

        return json.dumps(data), None


register(CsvParser)

コードの詳細は割愛しますが、クエリ文字列をCSVとしてパースし、Redashのクエリ結果の形式に準拠した形に整形します。

CSVの1行目はヘッダーとして扱うようにしています。

区切り文字はデータソースの設定として定義できるようになっており、デフォルトは,(半角カンマ)としています。

本来はカラムには string などの型を明示することも可能ですが、このスクリプトでは省略しています。

docker-compose.ymlの変更

独自のQuery Runnerを使用するため、 docker-compose.yml を編集します。

以下は docker-compose.yml の差分になります。

diff --git a/docker-compose.yml b/docker-compose.yml
index 536dd446..2f948d90 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,7 @@ services:
       REDASH_LOG_LEVEL: "INFO"
       REDASH_REDIS_URL: "redis://redis:6379/0"
       REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
+      REDASH_ADDITIONAL_QUERY_RUNNERS: "redash.query_runner.csv_parser"
   worker:
     build: .
     command: scheduler
@@ -31,6 +32,7 @@ services:
       REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
       QUEUES: "queries,scheduled_queries,celery"
       WORKERS_COUNT: 2
+      REDASH_ADDITIONAL_QUERY_RUNNERS: "redash.query_runner.csv_parser"
   redis:
     image: redis:3.0-alpine
     restart: always

環境変数 REDASH_ADDITIONAL_QUERY_RUNNERS で追加のQuery Runnerを指定できます。環境変数についてはAdvent Calendar 2日目の記事がとても参考になるのでおすすめです。

qiita.com

ここまでで独自Query Runnerを使うための準備は完了です。

動作確認

早速、 docker-compose up して動作確認します。

データベースの作成や管理ユーザの作成については済んでいるものとして進めます。

データソースの作成

データソースの作成画面でTypeのプルダウンをクリックすると、以下のように CsvParser が追加されています。

f:id:ariarijp:20171207195907p:plain

CsvParser を選択すると、以下のようなフォームが表示されます。

f:id:ariarijp:20171207200120p:plain

データソース名は CSV として、区切り文字はデフォルトで , となっているので、あえて :(半角コロン)にしてデータソースを保存します。

クエリの実行

クエリの作成画面に移動し、データソースとして CSV を選択ます。

クエリには以下のような半角コロン区切りの文字列を入力します。

name:ring_name:finishing_move:born_on
Kanji Inoki:Antonio Inoki:Enzuigiri:1943-02-20
Baba Shohei:Giant Baba:Big boot:1938-01-23

テストデータの内容については特に触れず、実行してみます。

f:id:ariarijp:20171207200851p:plain

ここまでの手順に問題がなければ、上のように文字列をパースしてクエリ結果として表示することができます。

カラムの並び順が入力したものと違っているのは、PythonのDictが挿入順を保持しないからでしょうか。Redashは現時点でPython2.7を使用していますが、Python3.6あたりで順序が保持されるようになるので、Python3対応されたら少し結果が変わると思います。

1日目の記事で私が紹介した Query Results データソースで別の結果と結合することもできたりして面白いかもしれませんね。

ariarijp.hatenablog.com

まとめ

この記事ではあまり実用性を考えずに独自のQuery Runnerを作成しましたが、Redashをチームや事業にフィットするようにカスタマイズしたいと考えられる場合、Query Runnerを作るというのは選択肢にいれてみてはいかがでしょうか。

明日は take4_k さんの「azure table storageのquery runner作ってみたので書きます」です。この記事がいい前振りになることを願っています。