君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

git cloneしたあとに--recursiveを付け忘れたことに気づいた。あとからsubmoduleをcloneしたい。

submoduleで管理しているライブラリをcloneし忘れた

いつもなら

git clone --recursive {gitリポジトリurl}

でcloneしてsubmoduleで管理しているライブラリもcloneしてくるのだが、

git clone {gitリポジトリurl}

としてしまった。

対処法

git submodule update --init --recursive

とすればsubmoduleで管理しているリポジトリも落としてこれる。

Docker for Macが重いので、VagrantでcoreOSをたててDocker Composeを導入する

Docker for Macが重い

Docker for Macを立ち上げると、Macのファンがとてもうるさくなる。

Docker for Macはハイパーバイザ型の仮想化をしているらしいが、

そのハイパーバイザがとてもCPUを枯渇させることがある。

denor.daa.jp

冬だと暖かいからいいけど夏だと熱いから困る(・ω・)

対処法としては

1. docker-syncを使う

2. cachedオプションを使う

3. Vagrant + coreOS

の3つがあるみたい。今回は3をやってみようと思う。

これでMac上でDockerを動かさず、VM(coreOS)上で動かすことができる。

噂によるとDocker for Macより軽いらしいので導入してみる。

手順

VagrantVM(coreOS)をたてる

$ git clone https://github.com/coreos/coreos-vagrant.git
$ cd coreos-vagrant
$ vagrant up
$ vagrant ssh

git cloneすると、Vagrantfileと諸々が落ちてくるので、

通常と同じようにvagrant upすればcoreOSが立ち上がる。

こういうところでVagrantはまだまだ便利だなと感じる。

dockerのバージョン確認

$ docker --version
$ Docker version 17.09.0-ce, build afdb6d4

おお。ほんとにDockerが入ってる。

Docker Composeを入れる

coreOSにはDocker Composeが入っていないので、別途installする必要がある。

docs.docker.com

基本上記に従ってinstallする。

現在1.17.0が最新だが、公式サイトにて最新バージョンを確認の上installしてください。

$ sudo -i
$ mkdir -p /opt/bin
$ curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /opt/bin/docker-compose
$ chmod +x /opt/bin/docker-compose

Docker Composeのバージョン確認

$ docker-compose -v
docker-compose version 1.17.0, build ac53b73

やったね。いろいろ動かしてみよう。

データベースの「データ型」とは何か?(その1)

はじめに

皆さん、データベースのデータ型について意識した事はありますか?

「データ型って何?」って思ったそこのRails使いの方!

rails db
postgres=# \d table_name

を叩いてみましょう。

※ table_nameは存在する適当なテーブル名

すると、以下のテーブルがあらわれます。

  Column    |            Type             |                    Modifiers                      
------------+-----------------------------+----------------------------------------------------
 id         | integer                     | not null default nextval('blogs_id_seq'::regclass)
 title      | character varying           | 
 content    | text                        | 
 created_at | timestamp without time zone | not null
 updated_at | timestamp without time zone | not null
 user_id    | integer                     | 

この「integer」「character varying」「text」などが、

「データ型」です。

データ型とは

データ型は、

「テーブルのカラムにどういうデータが入るか」を指定します。

先ほどの例では、

* idカラムにはinteger(整数)

* titleカラムにはcharacter varying(制限付き可変長文字列)

* contentカラムにはtext(制限なし可変長)

という感じですね。

なので、idカラムに文字列を入れようとするとエラーがおきます。

データ型は、想定していない変なデータがカラムに入ることを防いでくれる

なんだか厳格なヤツなのです。

データ型にもいろいろある

先ほど上げたのはpostgresqlのデータ型ですが、

MySQLOracle Database、SQL Serverなど使用するRDMSによって若干異なります。

MySQLの場合はintegerじゃなくてint型だったり、

smallint, tinyint, bigintなんて型があったりします(格納可能な数値の範囲が異なる)

データ型を知っておくと良いこと

DB設計をするときには必須の知識です。

データ型の決定時にミスすると、

サーバのディスク容量を無駄に使用しすぎてしまったり、

本来入れたい値を入れることができなくなったりします。

これはなかなか困ります。

また、アプリケーション開発中、

データベース側でエラーが起きたときに直ぐに原因が特定でき、

デバッグが早くなります(多分)。

これから

気が向いたときにMySQLpostgresqlのデータ型について

まとめていこうと思います。お待ち下さいm( )m

MySQLにて外部キーが貼れない。エラーは出ていない(MySQLのストレージエンジン・InnoDBとMyISAMの違い)

MySQLにて外部キーを貼ろうとした

table_aのidがtable_bのidを外部キーとして参照するように、

以下のようなSQLを流した。

alter table `table_a_name`
  add
    constraint table_a_name_ibfk_1 foreign key (table_a_id)
    references table_b_name(table_b_id)
  on delete cascade
  on update cascade;

特にエラーは起きなかったのに、なぜか外部キーが貼れていない。

解決方法

MySQLのストレージエンジンをMyISAMからInnoDBに変えればOK.

ALTER TABLE dbname.tablename ENGINE=InnoDB;

MySQLでMyISAMからInnoDBに変更させる – Simple IT Life

解説

ストレージエンジンがMyISAMだと外部キーが貼れず、

InnoDBだと外部キーが貼れる。

今回扱っていたテーブルはMyISAMだったので、InnoDBに変更した。

simple-it-life.com

ストレージエンジンとは

ストレージエンジンとは、公式サイトで以下のように紹介されている。

ストレージエンジンは、さまざまなテーブル型に対する SQL 操作を処理する
MySQL コンポーネントです。

…よくわからない。

が、要するにMySQLの機能の中で、

データの読み書きを担当している部分がストレージエンジンなのだろう。

MySQLは以下の2つのストレージエンジンを持っている。

の2つのストレージエンジンから選ぶことができる。

shindolog.hatenablog.com

これらのストレージエンジンはテーブルごとに設定できる。

tableAはMyISAM

tableBはInnoDBという感じ。

MyISAMInnoDBの違い

MyISAMは外部キーが使えない

InnoDBは使える。

今回はMyISAMが使われていたので、外部キーが貼れなかった。

他にも、

の違いがある。

MyISAMInnoDBの使い分け

基本InnoDBで問題なさそう。

トランザクションと外部キーはデータの整合性を保つには必須なので。

複雑な親子関係がなく、トランザクションのようなまとまった処理がなく、

かつ速度が要求される場合はMyISAMを選ぶ感じかな

(あまり思いつかないけど)

おまけ

alter table `table_a_name`
  add
    constraint table_a_name_ibfk_1 foreign key (table_a_id)
    references table_b_name(table_b_id)
  on delete cascade
  on update cascade;

外部キー名によく使われる「ibfk」は、

InnoDB Foreign key

の略らしい。

そりゃInnoDBでしか外部キー使えないわな。

名は体を表すとはこういうことか。

Angular2/4とgoogle maps apiを使って、緯度経度から住所を取得してみた(逆ジオコーディング)

やりたいこと

google mapのある地点をクリックした際に、

その地点の緯度経度・住所を取得する必要がありましたので、

google maps apiを使って逆ジオコーディングを実現してみました。

ジオコーディング / 逆ジオコーディングとは

  • ジオコーディング

住所の情報から、緯度経度などの地理的情報を取得すること。

  • 逆ジオコーディング

緯度経度から、住所等の情報を取得すること。

google mapをクリックした時点で緯度経度情報が取得できるので、

今回は逆ジオコーディングを行いました。

準備

karoten512.hatenablog.com

まずはこちらでAGMを導入してmapを表示するところまでやっておきます。

手順

  1. mapをクリックしたときのイベント取得ができるようにする

  2. 逆ジオコーディングを行うためのサービスを定義する

  3. mapをクリックしたときに逆ジオコーディングが行われるようする

1 mapをクリックしたときのイベント取得ができるようにする

1-1. app.component.htmlにて、mapのクリックイベントを設置する

<agm-map
  [latitude]="lat"
  [longitude]="lng"
  [zoom]="zoom"
  (mapClick)="mapClicked($event)"
  >
</agm-map>

1-2. app.component.tsにて、クリックイベントが起こった時の処理を記述する

export class AppComponent {
  /** ここから */
  mapClicked($event)
  {
    let lat = $event.coords.lat;
    let lng = $event.coords.lng;
    console.log('緯度: ' + lat);
    console.log('経度: ' + lng);
  }
  /** ここまで追加 */
}

1の結果

f:id:karoten512:20171030181914g:plain

2 逆ジオコーディングを行うためのサービスを定義する

2-1. サービスクラスを生成

ng g service geocoder

2-2. サービスクラスに必要モジュールを読み込む

/** geocoder.service.ts */
import {
  Injectable, 
  NgZone
} from '@angular/core';
import {
  AgmCoreModule,
  GoogleMapsAPIWrapper,
  MapsAPILoader
} from '@agm/core';

2-2. コンストラクタの記述

// GoogleMapsAPIWrapperを継承する
export class GeocoderService extends GoogleMapsAPIWrapper {

  constructor(
    private __loader :MapsAPILoader,
    private __zone :NgZone
  )
  {
    super(__loader, __zone);
  }
}

2-3. google maps apiの読み込み処理の記述

export class GeocoderService extends GoogleMapsAPIWrapper {
  getAddress(lat, lng)
  {
    this.__loader.load().then(() => {
      /**
       * google maps scriptsがloadしおわると、
       * この中でgoogle maps apiが使える
       * (new google.maps等)
       */
      let geocoder = new google.maps.Geocoder();

      /** 以下に逆geocode処理を記述 */
    }

  }
}

2-4. 逆ジオコーデングの処理を記述

export class GeocoderService extends GoogleMapsAPIWrapper {
  getAddress(lat, lng)
  {
    this.__loader.load().then(() => {
      /**
       * google maps scriptsがloadしおわると、
       * この中でgoogle maps apiが使える
       * (new google.maps等)
       */
      let geocoder = new google.maps.Geocoder();

      /** 以下に逆geocode処理を記述 */
      let latlng = new google.maps.LatLng(lat, lng);
      geocoder.geocode( { 'latLng': latlng }, function(results, status) {
        if (status == 'OK') {
          console.log(results);
          console.log(results[0]);
        } else {
         alert('Geocode was not successful for the following reason: ' + status);
          return false;
        }
     }
  }
}

3. mapをクリックしたときに逆ジオコーディングが行われるようする

3-1. app.module.tsにgeocoderサービスをDI

import { GeocoderService } from './geocoder.service';

@NgModule({
  providers: [
    GeocoderService
  ]
})

3-2. app.component.tsにgeocoderサービスをコンストラクタに追加

import { GeocoderService } from './geocoder.service';

export class AppComponent {
  constructor(
    private geocoderService :GeocoderService
  ) {}

3-3. app.component.tsにてgeocoderサービスの住所取得関数を呼び出す

export class AppComponent {
  mapClicked($event)
  {
    let lat = $event.coords.lat;
    let lng = $event.coords.lng;
    console.log('緯度: ' + lat);
    console.log('経度: ' + lng);
    this.geocoderService.getAddress(lat, lng); // 追加
  }
}

結果

f:id:karoten512:20171030185222g:plain

apiからは複数の逆ジオコーディング候補が返ってきます。

そのなかからいろいろな条件を指定したり、整形するなどして目的の情報を手に入れる必要があります。

今回の例では1件目(results[0])を取得してみました。

Rubyの配列を集合として扱った時の操作がとてもわかりやすい(共通集合・和集合・集合の差)

配列の準備

ary1 = %w(a b c)  # ["a","b","c"]
ary2 = %w(b c d)  # ["b","c","d"]

%wというのはパーセント記法と言うやつです。

これを使うと配列の定義が簡単にできます。

今回の場合は文字列を要素に持つ配列を作成しています。

共通集合

p (ary1 & ary2)
#=> ["b", "c"]

和集合その1

p (ary1 | ary2)
#=> ["a", "b", "c", "d"]

和集合その2

p (ary1 + ary2)
#=> ["a", "b", "c", "b", "c", "d"]

その1と違って重複を許してます。

単純に集合をくっつけている感じ。

差分

p (ary1 - ary2)
#=> ["a"]

これはほぼ見たまんま。

感想

すごく直感的にかけるので良い。

Rubyのmoduleとmodule_functionについて

moduleを定義する

module MyModule
  def hello
    puts 'hello'
  end 
end

MyModule.hello #=> undefined method `hello' for MyModule:Module (NoMethodError)

これだけだと使えない。

moduleを定義 + module_functionを定義

module MyModule
  def hello
    puts 'hello'
  end
  module_function :hello 
end

MyModule.hello #=> hello

使える。