RからRedashにクエリを投げるDBI対応のパッケージRedashrを作りました。

つくりました。まだ完成してないけど。

タイトルには「DBI対応の」と断言していますが、テストはまだ通ってないのでDBIに完全に準拠してるのか、そもそもそれは可能なのか、わかりません。。が、もう実装するの疲れたのでフライングで紹介します。

インストール

install_github()でインストールできます(CRANに上げるにはまだまだ時間かかりそう)

devtools::install_github("yutannihilation/Redashr")

使い方

Redashrのコネクションをつくるにはbase_urlapi_keydata_source_nameを指定します。Redashに登録されているデータソースがひとつだけならdata_source_nameは省略可能です。

以下の例で使っているのは、PostgreSQLのデータベースを唯一のデータソースとするRedashです。警告は出ますがdata_source_nameは省略して問題ないです。

library(Redashr)

redash_url <- "http://example.com/redash"
api_key <- "xxxxxxxxxx"

drv <- Redash()
conn <- dbConnect(drv, base_url = redash_url, api_key = api_key)
#> Warning: Using test as data source for now, but please provide
#> data_source_name.
#> Loading required package: RPostgreSQL
#> Loading required package: DBI

Redashrは、データソースに合わせて必要なパッケージを読み込みます。ここではPostgreSQLにクエリを投げるためにRPostgreSQLパッケージが読み込まれています。(このパッケージはあらかじめインストールしておく必要があります)

ちなみに、PostgreSQL用のパッケージで言うとRPostgresパッケージの方がナウいんですけど、ちゃんとDBへの接続がないと使えないので諦めました。

さて、DBIではコネクションに対してクエリを投げるための関数がいくつか用意されています。

  • dbSendQuery(): 結果ではなくDBIResultオブジェクトを返す。このオブジェクトをdbFetch()すると結果が得られる。
  • dbSendStatement(): 結果は返ってこない。INSERTとかUPDATEとかデータ操作系のクエリを投げるときはこれ。
  • dbGetQuery(): 結果がdata.frameとして返ってくる。

すぐに結果が欲しいなら使いたいのはdbGetQuery()でしょう。こんな感じです。

dbGetQuery(conn, "SELECT 1")
#> # A tibble: 1 x 1
#>   `?column?`
#>        <int>
#> 1          1

dplyrから使う(まだ実装中)

まだ実装中なんですけど、簡単な例なら動きます。Rのdata.frameをcopy_to()でコピーしてみましょう。

library(dplyr, warn.conflicts = FALSE)

copy_to(conn, iris)

dbGetQuery()でなにかクエリを投げてみるとちゃんとデータがコピーできていることがわかります。

dbGetQuery(conn, "SELECT COUNT(*) FROM iris;")
#> # A tibble: 1 x 1
#>   count
#>   <int>
#> 1   150

dplyrではtbl()を使うと、データベースのデータをdata.frameと同じように操作することができます。

redash_iris <- tbl(conn, "iris")

中身を覗いてみます。ちゃんとirisですね。(表示するだけでもデータベースへのアクセスが発生するので重いテーブルの場合は注意)。

redash_iris
#> # Source:   table<iris> [?? x 5]
#> # Database: RedashConnection
#>    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#>           <dbl>       <dbl>        <dbl>       <dbl>   <chr>
#>  1          5.1         3.5          1.4         0.2  setosa
#>  2          4.9         3.0          1.4         0.2  setosa
#>  3          4.7         3.2          1.3         0.2  setosa
#>  4          4.6         3.1          1.5         0.2  setosa
#>  5          5.0         3.6          1.4         0.2  setosa
#>  6          5.4         3.9          1.7         0.4  setosa
#>  7          4.6         3.4          1.4         0.3  setosa
#>  8          5.0         3.4          1.5         0.2  setosa
#>  9          4.4         2.9          1.4         0.2  setosa
#> 10          4.9         3.1          1.5         0.1  setosa
#> # ... with more rows

これを%>%を使ってデータ操作していくことができます。

redash_iris %>% 
  select(Sepal.Length, Sepal.Width, Species) %>%
  group_by(Species) %>%
  summarise(x = sum(Sepal.Length))
#> # Source:   lazy query [?? x 2]
#> # Database: RedashConnection
#>      Species     x
#>        <chr> <dbl>
#> 1  virginica 329.4
#> 2     setosa 250.3
#> 3 versicolor 296.8

ということで便利っぽいんですが、dplyrからちゃんと使えるようにするにはSQLの変換用メソッドを実装する必要があります。この辺がまだできていないので、まだ複雑なクエリだとうまく動きません。たぶん。

sql_render() calls an SQL generation function (sql_select(), sql_join(), sql_subquery(), sql_semijoin() etc) to produce the actual SQL. Each of these functions is a generic, taking the connection as an argument, so that the details can be customised for different databases.
(https://cran.r-project.org/web/packages/dbplyr/vignettes/sql-translation.html)

Connections

RStudio Connectionsを読んで、とりあえず「New connection」のところにRedashが出てくるようにはしたんですけど、

f:id:yutannihilation:20171013210107p:plain:w450 f:id:yutannihilation:20171013210136p:plain:w450

新しくコネクションを開いたらタブに表示するようにする方(Connections Contract)はやることが多くてちょっとまだできていません。

TODO

  • DBItestのテストを通す
  • dplyrに対応する
  • Connectionsタブに対応する

最後に

ということでできていないことだらけなんですけど、たぶん単純にdbGetQuery()を使うだけなら問題なく使えると思います。Redashから結果を抜いてきてRに入れるのに苦労してという人は使ってみてください。

使ってて変なところを見つけたり要望を閃いたりすれば、Twitter、このブログのコメント欄、GitHubのissuesあたりでお知らせください。