sbtのプロジェクト作成からAkka Streamの触りまでをやってみた

  • scala version:2.12.3
  • sbt version:1.0.2

sbtプロジェクト作成する

sbt new sbt/scala-seed.g8 

name [Scala Seed Project]:と聞かれるのでプロジェクト名を入力します.

プロジェクトのディレクトリが作成されます.

build.sbtの依存関係にAkka Streamを追加する

import Dependencies._

lazy val root = (project in file(".")).
  settings(
    inThisBuild(List(
      organization := "com.example",
      scalaVersion := "2.12.3",
      version      := "0.1.0-SNAPSHOT"
    )),
    name := "Hello",
    // 以下のように変更(libraryDependenciesを変更)
    libraryDependencies ++= Seq(
        scalaTest % Test,
        "com.typesafe.akka" %% "akka-actor" % "2.5.6",
        "com.typesafe.akka" %% "akka-stream" % "2.5.6"
        )
  )

コードを変更する

プロジェクト作成時に作成されているHello.scalaを以下のように変更しました.

package example

import akka.stream._
import akka.stream.scaladsl._
import akka.actor.ActorSystem

object Hello extends Greeting with App {
  implicit val system = ActorSystem("QuickStart")
  implicit val materializer = ActorMaterializer()

  val source = Source[Int](1 to 5)
  val sink = Sink.foreach[Int](println)

  source
   .map(_ * 2)
   .runWith(sink)


  println(greeting)
}

trait Greeting {
  lazy val greeting: String = "hello"
}

以下のように出力されたら成功です.

hello
[debug]     Thread run-main-0 exited.
[debug] Waiting for thread QuickStart-akka.actor.default-dispatcher-2 to terminate.
2
4
6
8
10

参考にしました

adtech.cyberagent.io

Apache Solrで,(カンマ)の扱いを任意の処理にさせる

Apache Solrのコアを作成したあと,$SOLR/server/solr/birth_3gram.xml/conf/managed-schemaのstringsフィールドの設定を以下のように変更します.(今回はカンマが含まれる文字列をsolrにsplitさせて検索させる方法です)

<fieldType name="strings" class="solr.TextField" multiValued="true">
      <analyzer type="index">
          <tokenizer class="solr.PatternTokenizerFactory" pattern="\s*,\s*"/>
          <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
         <filter class="solr.LowerCaseFilterFactory"/>
     </analyzer>
     <analyzer type="query">
         <tokenizer class="solr.PatternTokenizerFactory" pattern="\s*,\s*"/>
         <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
         <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
         <filter class="solr.LowerCaseFilterFactory"/>
     </analyzer>
 </fieldType>

その後,Analysisで確認すると分割されていることがわかります.

f:id:MitubaEX:20171006020950p:plain

今回はindex, query両方共に適応していますが,どちらかだけしかいらない場合はその都度消します.

GOでのファイル読み取り(標準入力も)

以下のコードでできる

package main

import (
    "bufio"
    "fmt"
    "os"
)



func main() {
    // ファイル読み取り(引数でファイル名受け取り)
    fp, err := os.Open(os.Args[1])
    if err != nil {
        panic(err)
    }
    defer fp.Close()
    scanner := bufio.NewScanner(fp)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        panic(err)
    }

        // 標準入力読み取り
    s := bufio.NewScanner(os.Stdin)
    for s.Scan() {
        fmt.Println(s.Text())
    }
}

Dockerを用いてMySQLコンテナをCLIで立ち上げる方法

備忘録として置いておきます.

起動

docker run --tty -p 3306:3306 --detach --name=DB-mysql --env MYSQL_DATABASE=DB --env MYSQL_USER="user" --env MYSQL_PASSWORD="password" --env MYSQL_ROOT_PASSWORD="PASSWORD" mysql:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci 

データベース名はDB,コンテナ名はDB-mysqlとしています.

テーブル作成

docker exec --interactive DB-mysql mysql --host localhost --default-character-set=utf8mb4 --user=root --password=PASSWORD DB < data.sql

適当にdata.sqlというファイルを用意してください.

以下にdata.sqlの例を示しておきます.

USE DB;

CREATE TABLE users(
    user_id varchar(64) NOT NULL,
    password varchar(128) NOT NULL
);

あとは各アプリでlocalhost:3306/<database_name>にアクセスするとつながると思います.

まとめ

テスト用にDBが欲しい時などに手軽に作成ができるのが,良い点だと思いました.

おまけ

dbの情報をsqlファイルにエクスポートする

docker exec CONTAINER /usr/bin/mysqldump -u root --password=root DATABASE > backup.sql

CONTAINER, DATABASEなどは適宜変更をお願いします.

Docker composeでSpringとMySQLのコンテナを立ち上げ接続させた時に行ったこと

docker-compose.yml

docker-compose.ymlは以下のような感じになりました. とりあえずMySQLのホスト名はdbserverにし,データベースをdbとしました. そこにSpringがアクセスします.

version: "3.1"
services:
    dbserver:
        image: mysql
        ports:
            - "3306:3306"
        hostname: dbserver
        volumes:
            - ./mysql/volumes:/var/lib/mysql
        environment:
            MYSQL_DATABASE: db
            MYSQL_USER: user
            MYSQL_PASSWORD: password
            MYSQL_ROOT_PASSWORD: PASSWORD
    java:
        image: java
        ports:
         - "8080:8080"
        volumes:
         - ./spring:/usr/src/spring
        tty: true
        command:
            bash -c "cd /usr/src/spring && ./gradlew build && java -jar build/libs/spring-0.0.1-SNAPSHOT.jar"

Springの設定ファイル

Springの設定ファイル(src/main/resources/application.yml)は以下のようになりました.

spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url:  jdbc:mysql://dbserver:3306/db
    username: root
    password: PASSWORD
security:
    basic:
        enabled: false

これでSpringを起動するとつながりました. いろいろ調べましたが,バージョン3以降ではコンテナ同士のリンクが必要ないらしいです.

まとめ

hostnameで接続できるので,これは覚えといたほうが良いかもしれません.

Scalaのリストに要素を追加するときの処理時間について

コップ本を読んでいたらScalaのリストに要素を追加する処理に関する注意点が載っていました.

どういうものかというと:+より::でリストに要素を追加し,その後Listをreverseさせろというものでした.

よく分からんので試してみる.

以下,テストするために書いたコード

object Main extends App{
  // 普通の再帰
  // これだと10000で落ちる
  /*
  def addFirst(list: List[Int]): List[Int] =
    list match{
      case (head :: Nil) => List(head)
      case (head :: tail) => list.head :: addFirst(list.tail)
      case _ => list
    }
  def addLast(list: List[Int]): List[Int] =
    list match{
      case (head :: Nil) => List(head)
      case (head :: tail) =>  addFirst(list.tail) :+ list.head
      case _ => list
    }
  */

  // 末尾再帰
  def addFirst(list: List[Int], ansList: List[Int] = List.empty[Int]): List[Int] =
    if(list.size == 0) ansList
    else addFirst(list.tail, list.head :: ansList)

  def addLast(list: List[Int], ansList: List[Int] = List.empty[Int]): List[Int] =
    if(list.size == 0) ansList
    else addLast(list.tail, ansList :+ list.head)

  // 適宜長さを調節したリストを作成する
  val list = (0 until 10000).toList

  // 先頭に要素を追加していく場合
  val startFirst = System.currentTimeMillis
  addFirst(list).reverse
  println((System.currentTimeMillis - startFirst) + "msec")

  // 末尾に要素を追加していく場合
  val startLast = System.currentTimeMillis
  addLast(list)
  println((System.currentTimeMillis - startLast) + "msec")
}

結果

素数 先頭 末尾
1,000 14ms 26ms
10,000 138ms 749ms
100,000 20866ms 114019ms

素数が100,000で大幅に差をつけて先頭から入れる処理が勝ちました.

感想

:+,あんまり使う機会少なそう・・・