astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

KotlinでJVMのWebApplication開発は楽になるのだろうか。検証してみている。

f:id:astamuse:20170314235457p:plain

どうも、えいやです。
またお鉢が回ってきたので、担当させていただきます。

前回はJava8で楽になったか試そうとしたけど、中途半端で完成しませんでしたという記事を書きました。
これはひどい、というご意見ですが、ごもっともですね。

今回も似た感じで、Kotlinで楽しようとした感じの中途半端な記事です。

kotlinlang.org

大体のことがそんな感じなので、大抵のことを誰か代わりにやってくれないかなぁと思っています。僕より役に立つ人が沢山いるといいな。

さて、今回は、WebアプリケーションにBetter JavaとしてKotlinを使う検討をやってみているという話です。
検討途中ですし、大して詳細に調べたわけでもないので、適当に聞き流してください。

弊社では、開発言語に特に縛りはないものの、Java、ScalaGroovyと言ったJVM言語で開発している事が多いです。
そういうわけで、しばらく前に1.0がリリースされ、やや安定期に入ってきた新しいJVM言語であるKotlinの評価を始めてみました。
ScalaもGroovyも15~12年前に開発されてから、最近はわりと安定、普及してきたのはそりゃいいんだけど、そろそろ新しめの言語にも触れておかないとね。

なお、Kotlinの開発元は、最近JVM向けの有償IDEで(たぶん)一番人気*1IntelliJ IDEAを作っているJet Brains社です。

まず、Kotlinという言語の特徴ですが、JVM上で動くバイトコードにコンパイル可能、静的型付け、オブジェクト指向という点ではJavaと同じです。
さらに、Kotlinには型推論、宣言側変性(ジェネクリスの変性を宣言可能)、高階関数、ラムダ式(最近のJavaにはあるけど)、移譲の宣言、Null安全、スマートキャストなどの便利機能がついています。

Kotlinの文法や詳細は、公式ページに任せます。また、いろいろな人が日本語で解説してくださっています*2*3ので、そちらを見てください。
この記事では、弊社が開発しているWebアプリケーションで、どの程度使えるのかを検証してみた内容について述べます。

なお、2017年03月現在で、世間様でのKotlinの利用状況としてはAndroidの開発でやや人気が出てきたというところのようです。

今回は、Kotlinの採用可能性について、以下の点で調べています。

1)KotlinおよびKotlin製のWebApplicationFWでシステム構築・運用が可能か
2)アプリケーションのコード全体をKotlinに置き換えることが可能か
3)2)のコードの一部をKotlinに置き換えることが可能か
4)既存のアプリケーションから利用するライブラリを作成可能か

1)KotlinおよびKotlin製のWebApplicationFWでシステム構築・運用が可能か

検証の対象として、Kotlin製のWebApplicationFWで一番安心できそうなJetBrain社製の「Ktor」を検証してみました。

結果としては、個人的には簡単なWebApplicationを作るだけならイケるんじゃないか、という感想なのですが、コレをチームメンバーに使えというのは酷という感じです。

Ktorは、最小の作業で素早くKtolinでWebアプリケーションを作るフレームワークと自身の紹介に書いてある通り、初見の印象としてはNode.jsのExpressに似たコンパクトなルーティングと処理を記述する、非常にコンパクトな機能を提供しているイメージです。

ドキュメントがほぼ整備されていないので、Ktor本体のソースコードとサンプルのソースコードを頼りに一通り試してみました。
公式のサンプルからそう離れたことはやっていませんし、この記事が出来上がる頃にはまた変わっていると思うので、試したコードは載せません。

なお、ドキュメントのGetting Startedで紹介してあるコードは僕が検証している段階では動かなかったのですが、今確認したところ動くコードに変わっているようです。

現時点でのバーションは0.3ですので、現時点ではかなり多くの不備があるため、採用するとなるとOSS開発に協力しつつシステム構築を進める事になります。
それはそれで面白いとは思いますが、すぐに導入を決めるのは難しそうです。

他のWebApplicationも一応検証してみたのですが、まともに動かすのに時間がかかるもの、更新止まっているものばかりでちょっと。。。

数年前に期待されていたKaraWasabi*4は開発が止まっている、か停滞しているみたいです。
なお、KtorはKaraやWasabiにインスパイアされているらしいので、それらの後継と言えるので、今一番安心できる開発体制なのではないでしょうか。

なお、WebApplicationというとサーバサイドって感じですが、KotlinはJavaScriptにトランスパイルする事ができるのでそういうFWもあるみたい*5です。検証してませんが。

2)アプリケーションのコード全体をKotlinに置き換えることが可能か

今のJava製のアプリケーションFWをそのまま使ってということですね。

まず、前提としてKotlinのコードはバイトコードにビルドされますし、Javaのライブラリもそのまま使うことが出来ます。なので、可能性ということであれば絶対に可能です。
あとはそれで開発が楽になったり、いいことがあるかということです。あと、既存コードからKotlinへの一挙置換となると現状のテストコードだけでは検証が難しそう、とかは一旦考えません。

弊社でJavaのプロジェクトと一部のScalaのプロジェクトでは、自社製のJavaWebApplicatrionFWを使用していますので、その仕様に則って、WebApplicationを作ったとしましょう。
検証のポイントは、FWの仕様が、Kotlinとミスマッチだったりしないか、ということです。
さらに、既存コードからの置き換えということで、KotlinのJava->Kotlin変換機能でどの程度楽にできるか、ということも検証してみます。

検証

次のプロジェクトを変換してみます。

github.com

このプロジェクトは、弊社製のWebApplicationFrameworkのAsta4Dで作成されており、簡単なHandlerとSnippetで構成されています。

本当はDB周りも試したかったのですが、この記事のためには時間切れです。

もし実行してみたかったら、次のコマンドで実行可能です。

git clone https://github.com/aya-eiya/java2kt1
cd java2kt1
git checkout JavaSmaple 
./gradlew clean appRun

起動にはGrettyを使っています。

localhost:8080で以下の画面を見ることが出来ます。

f:id:astamuse:20170314224025p:plain

上から、path/helloに対して引数なしGet、引数(name)ありGet、Postを行います。
path/helloからの返りはJsonで、Getに対しては{“message”:“hello ○○”}を返し、Postに対しては{“name”:“○○”}を返します。

Postで送ったNameはSessionに保存され、引数なしGetの返却値の一部およびPath/の一番下の入力フォームの初期値として使用されます。

さて、Kotlinに変換をしていきます。

まず、InteliJのJava->Kotlin変換機能を使ってみます。メニューからは Code -> Convert Java File to Kotlin Fileで選べます。

f:id:astamuse:20170314225034p:plain

変換後のソースはビルドが通りません。

どうやらNull許容のところで型違反と、valで初期化するように変換した値に代入をしているようです。

単純な方法でコンパイルが通るように修正したコードではアプリが起動しません。

どうやらNull安全の機能が問題で、Null許容型とNull不可型で型が違うため、Springの設定ファイルに書いてあるBeanの作成に失敗しているようです。
型を合わせてみるとこのエラーはなくなりました。

次はFactoryとして作っていたクラスで、staticなinstance()メソッドが実装されています。
このクラスのBeanの設定でfactory-methodにinstanceを指定していましたが、Kotlinではクラスにstaticなメンバを持つことが出来ないため、JavaからはCompanionオブジェクトを通じて呼ばなければなりません。
なのでFactory.CompanionをBeanに登録して、factory-beanにそのIDを指定します。

次は、UrlRuleがInitialize出来ない、というエラー。。。

その後、30分ほど色々試してみるものの、どこかしらでエラーとなって起動できませんでした。

結論として、大きなプロジェクトを変換するのはちょっと辛そう。ということになってしました。。。

しかしながら、時間さえかければ変換はそんなに苦労はしないんじゃないかなという感想も持ちました。
作業の慣れの問題でどうにかなるんじゃないかと。

3)2)のコードの一部をKotlinに置き換えることが可能か

1)の途中の状態から一部をJavaに戻したりしながら検証してみました。
Beanに登録するようなものでなければ違和感なくKotlinに変換して利用が出来ます。

ただ、この境界をどうするのか、というのが難しく、混ぜて使うのはちょっとつらいんじゃないかな。
という感想です。

4)既存のアプリケーションから利用するライブラリを作成可能か

これは検証前は一番大丈夫なパターンだと思ったのですが、ライブラリの対象によってはBeanに登録するだの、利用シーンを限定できないこともあってかなりつらいです。
一番つらいパターンかもしれません。

簡単な機能のライブラリでは問題はでてこないのかもしれませんが、これも何をKotlinで作って良くて、何がダメなのかの境界を設定するのが辛いと思いました。

一旦の結論

  • Kotlinで書くのはそんなに辛くないが、FWなどエコシステムがまだ発展途上。
  • 現行のWebApplicationのコードをKotlinに変換するのは辛い。
  • 一部をKotlinで書き始める場合も、落とし穴だらけの可能性が高い。

今回とは逆に、Javaで作ったものをKotlinで使うのは、Null安全などの対応は別にすれば問題は少ないです。
なので、業務で使う場合、WebApplicationよりもバッチとか、それこそ今流行り始めているAndroidで使うのが、まだ安全かな、という結論です。

今回、検証とは別に習熟のためにKotlinで色々書いてみたのですが、Scalaと同じく、言語の持つ機能が多いので覚えることが多い印象です。JVM言語ではないのですが、シンプルなGo言語なんかと比べると習得に要する時間が長いかも。

Javaもそれなりに多いんだけど、経験年数長い人やドキュメントが多いので。同じ理由でだいぶ長くなってきたScalaもその辺をクリアしてきた感じありますね。

良い言語だと思うんだけど、普及率や自分たちの領域を考えると、まだチームメンバーに強要はできないかなー。

という感じで、今回も中途半端な感じで終わります。

なお、既にKotlinマスターで強烈にKotlinをプッシュしてくれる方が来ていただけると事情も変わると思います。
Javaを全部Kotlin化するって言っても、僕はことさら止めはしないので。

今回の記事は以上となります。
お疲れ様でした。

BootstrapとVueでタブを作ってみる、の巻

2016/09/30に2.0がリリースされてから、現在2017/03/05まででダウンロード数約3倍の増加をみせるVue.js。 f:id:astamuse:20170308132520p:plain

npmtrends(2016/09/25 32,043 → 2017/03/05 109,774)

そろそろ無視出来ない勢いなので、とりあえず触ってみることにしました。 ドキュメントもしっかり日本語化されてるので英検4級の私にはありがたい。←ここ重要

1. 下準備

vue-cliを使ったほうがかなり楽そうなんですが、めんど(ry

ファーストステップなのでVue.jsのみでやってみたいと思います。

またCSS書くのもめんど(ry

久しぶりにbootstrap触りたくなったのでそれ使います。

ということでCodePenだけで準備完了です。すばらしい。pugも使える。CodePenありがとう。

2. とりあえずhtmlを組んでいく

元テンプレートはこちら

div.container
  h1 vue tab sample
  ul(class="nav nav-tabs" role="tablist")
    li(class="nav-item" role="presentation")
      a(href="#home" class="nav-link active" role="tab") ホーム
    li(class="nav-item" role="presentation")
      a(href="#test" class="nav-link" role="tab") テスト
  
  div.tab-content
    div(id="home" role="tabpanel")
      h2 HOME
      p ホームタブコンテンツ
    div(id="test" role="tabpanel")
      h2 TEST
      p テストタブコンテンツ




まず <slot>を使ってhtml側で管理する内容と、vue側で管理する内容を分離していきましょう。ここでは、テキスト内容をhtml、そのほかのものはvue側で管理させるようにしました。 (サンプルではわかりやすいようにnameをつけてますが、なくてokです)

div#v-root.container
  h1 vue tab sample
  tabs
    div(slot="tabContents")
      tab(name="home" text="ホーム" v-bind:selected="true")
        div(slot="tabinner")
          h2 HOME
          p ホームタブコンテンツ
        
      tab(name="test" text="テスト")
        div(slot="tabinner")
          h2 TEST
          p テストタブコンテンツ


Vue.component('tabs', {
  template: `
    <div>
      <ul class="nav nav-tabs" role="tablist">
        <li class="nav-item" role="presentation">
          <a href="#home" class="nav-link" role="tab">ホーム</a>
        </li>
        <li class="nav-item" role="presentation">
          <a href="#test" class="nav-link" role="tab">テスト</a>
        </li>
      </ul>
      <div class="tab-contents">
        <slot name="tabContents"></slot>
      </div>
    </div>
  `
});
Vue.component('tab', {
    template: `
    <div :id="'#' + this.name" role="tabpanel"><slot name="tabinner"></slot></div>
    `,
    props: {
        name: { required: true },
    },
});
new Vue({
    el: '#v-root'
});



See the Pen Vue tab sample 03 by 35n139e (@35n139e) on CodePen.

3. Vue実装開始

tab実装

タブコンテンツを軸にタブリストを生成する方法で実装していきます。 html側でid名やタブのテキストを管理するので、propsにtext(テキスト)とname(id名)、selectedでデフォルトで開くタブを選べるようにしました。

tab(name="home" text="ホーム" v-bind:selected="true")
  div(slot="tabinner")
    h2 HOME
    p ホームタブコンテンツ
  
tab(name="test" text="テスト")
  div(slot="tabinner")
    h2 TEST
    p テストタブコンテンツ


Vue.component('tab', {
  template: `
    <div :id="'#' + this.name" role="tabpanel" v-if="isActive">
      <slot name="tabinner"></slot>
    </div>
  `,
  data() {
    return {
      isActive: false
    };
  },
  props: {
    text: { required: true },
    name: { required: true },
    selected: { default: false }
  },
  mounted() {
    if(location.hash != ""){
      const url = location.hash;
      this.isActive = (url == '#' + this.name);
    }else{
      this.isActive = this.selected;
    }
  }
});

tabs実装

つぎにタブリストの生成です。

Vue.component('tabs', {
  template: `
    <div>
      <ul class="nav nav-tabs" role="tablist">
        <li v-for="tab in tabs" class="nav-item" role="presentation">
          <a :href="'#' + tab.name" @click.prevent="selectTab(tab)" role="tab" class="nav-link" :class="{ 'active': tab.isActive }">{{ tab.text }}</a>
        </li>
      </ul>
      <div class="tab-content">
        <slot name="tabwrap"></slot>
      </div>
    </div>

    `,
    data() {
        return { tabs: [] };
    },
    created() {
        this.tabs = this.$children;
    },
    methods: {
      selectTab(selectedTab) {
        this.tabs.forEach(function(tab){
          tab.isActive = (tab.name == selectedTab.name);
        });
      }
    }
});

v-forでタブコンテンツの中を見に行かせます。

<li v-for="tab in tabs" class="nav-item" role="presentation">

タブリストの中身はそれぞれtabのpropsを参照させます。タブのアクティブ化は'v-bind:class'で行います。

<a :href="'#' + tab.name" @click.prevent="selectTab(tab)" role="tab" class="nav-link" :class="{ 'active': tab.isActive }">{{ tab.text }}</a>


タブリストをクリックしたときの関数をmethodsに記述。 それぞれのtab.nameとクリックしたtab.name(selectedTab.name)にアクティブフラグを立てさせます。

methods: {
  selectTab(selectedTab) {
    this.tabs.forEach(function(tab){
      tab.isActive = (tab.name == selectedTab.name);
    });
  }
}

完成

tabsとtabを合わせるとあら不思議。タブの完成です。

See the Pen Vue tab sample 04 by 35n139e (@35n139e) on CodePen.

4. <transition>で味付け

最後に<transition>を使って味付けしていきます。

animationのcssはAnimate.cssを利用させてもらいます。

下記のようにカスタムすれば<transition>でAnimate.cssを直接使うことができます。

<transition
  name="animateCSS"
  enter-active-class="animated fadeIn"
  leave-active-class="hide"
>
  <div :id="'#' + this.name" role="tabpanel" v-if="isActive">
    <slot name="tabinner"></slot>
  </div>
</transition>

これでアニメーションさせたいコンテンツをラッピングして終了です。簡単!キレイ!

See the Pen Vue tab sample 3 by 35n139e (@35n139e) on CodePen.

5. 最後に

Vue.jsはガイドもしっかりしてるし、学習コストも低めなので「Jqueryしか書いたことないよ」って方もぜひ触ってみていただきたいフレームワークです。

私もお試しで触ってみてハマった口で、今週の月曜日に弊社の採用サイトをVue.jsで作り直しました。ぜひそちらのほうも見ていただければと思います。

参照・参考元

Spark 2.0 で Spark SQLを試す

こんにちは!Spark大好きな朴です。

本日はSpark 2.0で大幅の改善が行われてたSpark SQLについて書こうと思います。
弊社ではCDHのバージョンアップをこまめに行うことでSpark,HBaseなどのプロダクトの最新バージョンを常に試せる環境を作っております。

Spark 2.0についても先日弊社福田のもう待てない、Spark2.0の導入と実践にも書いてたとおり もう使えるようになりました。

ということで少し乗り遅れた感もありますが、本日はSpark 2.0でSpark SQLの実力を試したいと思います。

Spark 2.0でSpark SQLの主な変更点は以下の3つ

  • SparkSession
  • 性能改善
  • サポートするSQLが増えた

本日は上記3つの改善について触れてみたいと思います。

【変更その1】 SparkSQLのニューエントリポイントSparkSession

Spark 1.6まではユーザは以下のようにSqlContext(SparkSQL)とHiveContext(HiveQL)を使い分ける必要がありました。

// Spark SQL
val sc = new SparkContext(spConf)
val sqlContext = new SQLContext(sc)
sqlContext.sql("select * from hoge").show()

// HiveQL
val hiveContext = new org.apache.spark.sql.hive.HiveContext(sc)
hiveContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
hiveContext.sql("LOAD DATA LOCAL INPATH 'temp/kv1.txt' INTO TABLE src")
hiveContext.sql("FROM src SELECT key, value").collect().foreach(println)


Spark 2.0では以下のようSparkSessionでSparkSQL,HiveQL両方書けるようになりました。

val spark = SparkSession.builder()
      .master("yarn")
      .appName("Test")
      .getOrCreate()

// Spark SQL
spark.sql("select * from hoge").show()

// HiveQL
spark.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
spark.sql("LOAD DATA LOCAL INPATH 'temp/kv1.txt' INTO TABLE src")
spark.sql("FROM src SELECT key, value").collect().foreach(println)

【変更その2】 性能面での改善

SQLおよびDataFrame関連の処理にてwhole stage code generationの新技術を使うことでおよそ2-10倍の性能改善を実現したそうです。
リリースノートの原文はこちら

それでは実際どうなのかちょっと試してみたいと思います。

以下同じ条件でSpark 1.6 と Spark 2.0でいくつか簡単なSQLを試してどれぐらい実行スピードが違うかを実験してみます。

データソースはwikipediaのデータを使います。 こちらのリンクよりenwiki-20170201-abstract.xmlをダウンロードします。 https://dumps.wikimedia.org/enwiki/20170201/

上記データは英語wikiの概要とタイトル、urlを含めたxmlになります。

<doc>
<title>Wikipedia: Anarchism</title>
<url>https://en.wikipedia.org/wiki/Anarchism</url>
<abstract>Anarchism is a political philosophy that advocates self-governed societies based on voluntary institutions.</abstract>
<links>
 <sublink linktype="nav"><anchor>External links</anchor><link>https://en.wikipedia.org/wiki/Anarchism#External_links</link></sublink>
</links>
</doc>
<doc>
 ..
 ..
</doc>


  • 前準備として上記のデータをhdfsにアップします。
hadoop fs -put enwiki-20170201-abstract.xml temp/


  • 以下のコマンドでspark2-shellを起動します。
spark2-shell --master yarn \
 --driver-memory 2G --executor-memory 4G  \
 --packages com.databricks:spark-xml_2.10:0.4.1 \
 --conf spark.serializer="org.apache.spark.serializer.KryoSerializer"

※注 spark2.0ではspark-xmlのバージョンを0.4.1以上に指定する必要があります。

  • XMLをDataFrameに変換します。
scala> val enwiki=spark.read.format("com.databricks.spark.xml").option("rowTag", "doc").option("inferSchema", "true").load("temp/enwiki-20170201-abstract.xml")
scala> enwiki.printSchema
root
 |-- abstract: string (nullable = true)
 |-- links: string (nullable = true)
 |-- title: string (nullable = true)
 |-- url: string (nullable = true)

scala> enwiki.createOrReplaceTempView("enwiki")


  • 実行時間計測用の関数を定義します。
scala > def showTiming[T](proc: => T): T = {
    val start=System.nanoTime()
    val res = proc
    val end = System.nanoTime()
    val esplatedSec = java.util.concurrent.TimeUnit.SECONDS.convert((end-start), java.util.concurrent.TimeUnit.NANOSECONDS)
    println("Time elapsed: " + esplatedSec + " secs")
    res
}


性能検証1 件数count

Spark 2.0

scala> showTiming{ enwiki.count() }
Time elapsed: 116 secs
res4: Long = 5328417

結果でました。実行時間は116秒、件数は532万件ちょっとありました。

同じ処理をSpark 1.6で実行してみた結果


scala> showTiming{enwiki.count()}
Time elapsed: 146 secs
res4: Long = 5328417

こちらは実行時間が30秒増えてますね。 劇的ではありませんが、DataFrameの件数カウントではSpark2.0のほうが早いことが分かります。

性能検証2 like検索

Spark 2.0

scala> showTiming{ spark.sql("select count(*) from enwiki where abstract like '%Robot%'").show() }
+--------+
|count(1)|
+--------+
|     769|
+--------+

Time elapsed: 100 secs

Spark 2.0でlike検索の実行時間は116秒、件数は769件ありました

同じ処理をSpark 1.6で実行してみた結果


scala> showTiming{ sqlContext.sql("select count(*) from enwiki where abstract like '%Robot%'").show() }
+---+
|_c0|
+---+
|769|
+---+

Time elapsed: 143 secs

こちらは実行時間が43秒増えてますね。countと同じくSpark2.0のほうが性能的には上ですね

性能検証3 join処理

join系の処理を試してみたいと思います。 enwikiとjoinするためのデータを準備するためにsimplewiki-20170201-abstract.xmlを 以下のリンクよりダウンロードします。 https://dumps.wikimedia.org/enwiki/20170201/

データの構造はenwikiと同じでurlが違うだけです。

<doc>
<title>Wikipedia: April</title>
<url>https://simple.wikipedia.org/wiki/April</url>
<abstract>April is the fourth month of the year, and comes between March and May. It is one of four months to have 30 days.</abstract>
<links>
<sublink linktype="nav"><anchor>The Month</anchor><link>https://simple.wikipedia.org/wiki/April#The_Month</link></sublink>
</links>
</doc>
<doc>
..
..
</doc>


  • データをhdfsに上げます
hadoop fs -put simplewiki-20170201-abstract.xml temp/


  • simplewikiのデータをロードしてDataFrameに変換します。

scala> val simplewiki=spark.read.format("com.databricks.spark.xml").option("rowTag", "doc").option("inferSchema", "true").load("temp/simplewiki-20170201-abstract.xml")

scala> simplewiki.printSchema
root
 |-- abstract: string (nullable = true)
 |-- links: string (nullable = true)
 |-- title: string (nullable = true)
 |-- url: string (nullable = true)

scala> simplewiki.createOrReplaceTempView("simplewiki")


  • 下記のSQLでenwikiとsimplewikiで同じタイトルのデータ同士でinner joinをします。

Spark 2.0
scala> showTiming{spark.sql("select count(*) from enwiki en inner join simplewiki simple on (en.title = simple.title)").show()}
+--------+
|count(1)|
+--------+
|  107550|
+--------+

Time elapsed: 128 secs

Spark 2.0では128秒かかりました。

Spark 1.6

scala> showTiming{sqlContext.sql("select count(*) from enwiki en inner join simplewiki simple on (en.title = simple.title)").show()}
+------+
|   _c0|
+------+
|107550|
+------+

Time elapsed: 152 secs

Spark 1.6では152秒となり、Spark2.0より遅いことが分かります。

とりあえず、以上で簡単にcount,like検索,joinでSpark 2.0とSpark 1.6でSQLの性能検証を実施してみました。 どの処理もSpark 2.0で性能が改善されてることが分かりました。

【変更その3】 SQL2003をサポート、99個のTPC-DS クエリサポート

主には以下可能になりました。

  • ANSI-SQLとHive QLの両方をサポートするネイティブSQLパーサー
  • DDLが書けるようになった
  • Subqueryが書けるようになった
    • in,exists,not in, not existsなどなど(これは嬉しいー)
  • View canonicalization support (viewが使えるようになった?)

それではサブクエリ関連ちょっと触ってみたいと思います。

  • 給料を管理するテーブル(DataFrame)を定義
scala> val df1 = spark.createDataFrame(Array(("1001",1000),("1002",2000),("1003",1300),("1004",1500),("1005",1600),("1006",1700),("1007",1600),("1008",1700),("1009",1000),("1010",1900)))
df1: org.apache.spark.sql.DataFrame = [_1: string, _2: int]

scala> val salary=df1.withColumnRenamed("_1", "staffId").withColumnRenamed("_2", "salary")
salary: org.apache.spark.sql.DataFrame = [staffId: string, salary: int]

scala> salary.show()
+-------+------+
|staffId|salary|
+-------+------+
|   1001|  1000|
|   1002|  2000|
|   1003|  1300|
|   1004|  1500|
|   1005|  1600|
|   1006|  1700|
|   1007|  1600|
|   1008|  1700|
|   1009|  1000|
|   1010|  1900|
+-------+------+


  • 社員情報を管理するテーブル(DataFrame)を定義

scala> val df2 = spark.createDataFrame(Array(("1001","Staff1","Marketing"),("1002","Staff2","Marketing"),("1003","Staff3","Marketing"),("1004","Staff4","Sales"),("1005","Staff5","Sales"),("1006","Staff6","Sales"),("1007","Staff7","Sales"),("1008","Staff8","Development"),("1009","Staff9","Development"),("1010","Staff10","Development")))
df2: org.apache.spark.sql.DataFrame = [_1: string, _2: string ... 1 more field]

scala> val depart=df2.withColumnRenamed("_1", "staffId").withColumnRenamed("_2", "staffName").withColumnRenamed("_3", "depart")
depart: org.apache.spark.sql.DataFrame = [staffId: string, staffName: string ... 1 more field]

scala> depart.show
+-------+---------+-----------+
|staffId|staffName|     depart|
+-------+---------+-----------+
|   1001|   Staff1|  Marketing|
|   1002|   Staff2|  Marketing|
|   1003|   Staff3|  Marketing|
|   1004|   Staff4|      Sales|
|   1005|   Staff5|      Sales|
|   1006|   Staff6|      Sales|
|   1007|   Staff7|      Sales|
|   1008|   Staff8|Development|
|   1009|   Staff9|Development|
|   1010|  Staff10|Development|
+-------+---------+-----------+


  • (not) existsを試してみる
scala> salary.createOrReplaceTempView("salary")

scala> depart.createOrReplaceTempView("depart")

scala> spark.sql("select * from salary s where exists (select 1 from depart d where s.staffId = d.staffId and d.depart = 'Marketing')").show()

+-------+------+
|staffId|salary|
+-------+------+
|   1001|  1000|
|   1002|  2000|
|   1003|  1300|
+-------+------+

scala> spark.sql("select * from salary s where not exists (select 1 from depart d where s.staffId = d.staffId and d.depart = 'Marketing')").show()
+-------+------+
|staffId|salary|
+-------+------+
|   1004|  1500|
|   1005|  1600|
|   1006|  1700|
|   1007|  1600|
|   1008|  1700|
|   1009|  1000|
|   1010|  1900|
+-------+------+


  • (not) inを試してみる

scala> spark.sql("select * from salary s where staffId in (select staffId from depart d where d.depart = 'Marketing')").show()
+-------+------+
|staffId|salary|
+-------+------+
|   1001|  1000|
|   1002|  2000|
|   1003|  1300|
+-------+------+

scala> spark.sql("select * from salary s where staffId not in (select staffId from depart d where d.depart = 'Marketing')").show()
+-------+------+
|staffId|salary|
+-------+------+
|   1004|  1500|
|   1005|  1600|
|   1006|  1700|
|   1007|  1600|
|   1008|  1700|
|   1009|  1000|
|   1010|  1900|
+-------+------+

エラーなく実行できました!

終わりに

去年(2016年)の12月にSpark2.1がリリースされて、Spark2系も開発が活発に進められてる印象があります。 本日はSpark2系のSQL面での主な改善点を書きましたが、上記以外にもたくさんの改善が行われてますので、 これから既存のSparkジョブをSpark2系向けに移行しながらその良さを実感したいと思います。

ではまた!

Copyright © astamuse company, ltd. all rights reserved.