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で大幅に差をつけて先頭から入れる処理が勝ちました.

感想

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

ACM-ICPC 2017 国内予選に参加した話

7/14(金)15:00ぐらいから席についてプリンターの用意をし始めて,コーラを買ってきてスタンバイ. 一緒に出たのは同じ研究室のPくんとSくん.

16:30に開始した

問題解き解きタイム

とりあえず僕とSくんでB問題に目を通す. B問題ざっくりと考察していた.とりあえず完全一致見るよねぇーとか言っていた. その間にPくん,A問題AC.

速い

その後SくんがPくんにB問題の内容を教えている間に僕がC問題を読む. C問題読んでどうしよっかな〜って考えていたら,Sくんが帰還して一緒に考える. とりあえず全探索しとこうという話になる.Pくんにぶん投げ.

D問題をとりあえず読んで見る.DPかなぁって思いつつ考察していたが,やり方いまいちわかっておらずPくんにそのままぶん投げた.

後はひたすら読める問題から読みまくっていた.D問題よりG問題の方が簡単じゃね?って思いつつ読んでいて,Pくんに投げたかったが時間切れ.

結果は3完75位でした.

まとめ

Pくんにぶん投げすぎ.

Play FrameworkでJSONを触ってみる

POSTリクエストとしてJsonを受け取り,そのJsonをencodeしたりdecodeしたりしてみた

conf/routesを変更

POST    /json                       controllers.JSONController.jsonParse 

コントローラーの記述

package controllers

import javax.inject.Inject

import play.api.mvc.{Action, Controller}

import play.api.libs.json._

// caseクラスを定義
case class Person(username: String, password: String)
// コンパニオンオブジェクトを定義
object Person {
  implicit def jsonWrites = Json.writes[Person]
  implicit def jsonReads = Json.reads[Person]
}

/**
  * Created by mituba on 2017/06/30.
  */
class JSONController @Inject() extends Controller {
  def jsonParse = Action { request =>
    // getRequestJson
    val params : Option[JsValue] = request.body.asJson
    val json = params.get

    // encode
    val result: JsResult[Person] = json.validate[Person]
    val person: Person = result.get
    println(person.username + " " + person.password)

    // decode
    val decodeJson: JsValue = Json.toJson(person)
    println(decodeJson.toString())

    Ok(decodeJson.toString())
  }
}

curlでPOSTする

curl -H "Content-Type: application/json" -X POST -d '{"username":"xyz","password":"xyz"}' http://localhost:9000/json

無事Jsonを受け取って,encode,decodeできました.

参考

以下のサイトを参考にしてもらいました.

qiita.com

play frameworkを使ってみる

activatorをインストー

brew install typesafe-activator 

プロジェクトの作成

activator new myapp play-scala 

myappというディレクトリができました.

myappに移動して,sbt compileコンパイルsbt runで実行します.

http://localhost:9000/にアクセスすると,play frameworkの画面がでてきました.

とりあえずプロジェクト作成時に出てきたソースコードを見てみる.

conf/routes

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# An example controller showing a sample home page
GET     /                           controllers.HomeController.index
# An example controller showing how to use dependency injection
GET     /count                      controllers.CountController.count
# An example controller showing how to write asynchronous code
GET     /message                    controllers.AsyncController.message

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.versioned(path="/public", file: Asset)

requestMapping的な何かをしてそうなところ

HomeControllerを見てみる

package controllers

import javax.inject._
import play.api._
import play.api.mvc._

/**
 * This controller creates an `Action` to handle HTTP requests to the
 * application's home page.
 */
@Singleton
class HomeController @Inject() extends Controller {

  /**
   * Create an Action to render an HTML page with a welcome message.
   * The configuration in the `routes` file means that this method
   * will be called when the application receives a `GET` request with
   * a path of `/`.
   */
  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }
}

views/index.scala.htmlを返してそう

index.scala.htmlは同じところにあるmain.scala.htmlを読んでいるらしい

CountControllerを見てみる

package controllers

import javax.inject._
import play.api._
import play.api.mvc._

import services.Counter

/**
 * This controller demonstrates how to use dependency injection to
 * bind a component into a controller class. The class creates an
 * `Action` that shows an incrementing count to users. The [[Counter]]
 * object is injected by the Guice dependency injection system.
 */
@Singleton
class CountController @Inject() (counter: Counter) extends Controller {

  /**
   * Create an action that responds with the [[Counter]]'s current
   * count. The result is plain text. This `Action` is mapped to
   * `GET /count` requests by an entry in the `routes` config file.
   */
  def count = Action { Ok(counter.nextCount().toString) }

}

counter.nextCount()を呼んでそう

counterクラスを呼んでそう

Counter.scalaを見てみる

package services

import java.util.concurrent.atomic.AtomicInteger
import javax.inject._

/**
 * This trait demonstrates how to create a component that is injected
 * into a controller. The trait represents a counter that returns a
 * incremented number each time it is called.
 */
trait Counter {
  def nextCount(): Int
}

/**
 * This class is a concrete implementation of the [[Counter]] trait.
 * It is configured for Guice dependency injection in the [[Module]]
 * class.
 *
 * This class has a `Singleton` annotation because we need to make
 * sure we only use one counter per application. Without this
 * annotation we would get a new instance every time a [[Counter]] is
 * injected.
 */
@Singleton
class AtomicCounter extends Counter {  
  private val atomicCounter = new AtomicInteger()
  override def nextCount(): Int = atomicCounter.getAndIncrement()
}

トレイトを実装したAtomicCounterがあった

getAndIncrement()で1足した値を取得してるっぽい

返ってきたやつをtoStringして出力といった感じ

とりあえずコントローラーにメソッドを一つ追加してみる

conf/routesを変更

GET     /test                       controllers.TestController.test

TestControllerの追加

package controllers

import javax.inject.Inject

import play.api.mvc.{Action, Controller}

/**
  * Created by mituba on 2017/06/29.
  */
class TestController @Inject() extends Controller {
  def test = Action {
    Ok("hello")
  }
}

実行してhttp://localhost:9000/testにアクセスするとhelloという文字が出た

とりあえずリクエストパラメータをもらってみる

conf/routesの変更

GET     /params                     controllers.ParamController.getParam

ParamController.scalaの追加

package controllers

import javax.inject.Inject

import play.api.mvc.{Action, Controller}


/**
  * Created by mituba on 2017/06/29.
  */
class ParamController @Inject() extends Controller  {
  def getParam = Action { request =>
    val params : Map[String, Seq[String]] = request.queryString
    val message = params("message").head

    Ok(message)
  }
}

実行してhttp://localhost:9000/params?message=helloにアクセスすると,helloと表示された.

まとめ

後はJSONの送り合いとDB処理の実装について調べてみたい.

Apache Solrを利用してみた

この記事はApache Solrの設定からpythonによる使用までを書いていく記事です。 今回Apache Solrのバージョンは、5.5.0を対象にします。

参考

ダウンロード

リンクからバージョンを選んでダウンロードして下さい。(解凍も忘れずに)

解凍後のディレクトリに移動して、作業していきます。

サーバ起動

Apache Solrの起動は以下のコマンドで行います.

bin/solr start

以下のリンクに飛ぶと、Apache Solrという画面が出てくると思います。

これで起動成功です。

起動時のポート番号を指定する場合

bin/solr start -p $(port_number)

コア

コアとは、一つのデータベースみたいなものです。 コアごとにいろいろデータを登録していきます。

  • 作成
bin/solr create -c $(core_name)
  • 削除
bin/sor delete -c $(core_name)

ドキュメント登録

bin/post -c $(core_name) $(file_name)

対応ファイルは、

検索

ここから登録したドキュメントに対して検索を行います. 以下のコードをコピペして,main.pyというファイルを作成してください. (core_nameとqueryを各自のコア名と検索単語に変えてください)

import urllib

url = "http://localhost:8983/solr/<core_name>/select?q=<query>&wt=python&indent=true"
res = urllib.urlopen(url) # urlを開く
response = eval(res.read()) # dictを取り出す

# 検索結果のdictを、hitとして取り出す
for hit in response['response']['docs']:
    # hitを用いた処理を書く(ex. hit['data'] -> dataという名前のkeyのValueを取得する)

いろいろなパラメータ

Apache Solrは多くのパラメータによって検索結果を変更することができます. 以下に今回使用するパラメータを示します.

パラメータ 役割
sort 与えられた値でソートをする(ex. &sort=<field_name>)        
rows 取得件数を設定する(ex. &rows=100)            
fl 検索結果として取ってくるフィールドを設定する(ex. &fl=<field_name>)

パラメータを用いた検索

url = "http://localhost:8983/solr/<core_name>/select?q=<query>&sort=strdist(<fl_name>,<query>,edit)+desc&rows=100&fl=<fl_name>&wt=python&indent=true"

ファンクションクエリ(strdist関数)

上記のパラメータを用いた検索で利用したstrdistなどはファンクションクエリといいます.strdistの他に多数あります.

strdist(<str1>, <str2>, edit)

この場合はstr1とstr2の距離をはかって、値を返します。 他にもいろいろありますが、使用してないので説明は省きます。

ファンクションクエリを使う際の注意点

Apache Solrの性質上、登録したドキュメントを用いてソートなどを利用したい時には設定を変えないとエラーになります.

そこで回避方法としてsolr-x.x.x/server/solr/<core_name>/conf/managed-schemaの中を少し変更します。

  • 変更内容
multiValued="true"
↓
multiValued="false"

その後、以下のコマンドで再起動し修正完了です。

bin/solr restart

まとめ

起動→コア作成→データ登録→検索、までが一連の流れです。