ページ

2016年3月9日水曜日

道の駅を巡ってみる 10 - 福岡 -


最近道の駅に行けてなかったんで機会を待っていたんですが、
やっとゆっくりできそうだったので、久しぶりに行ってきました。
今回は福岡の道の駅「うきは」です。


1. 道の駅「うきは」




道の駅「うきは」ですが、見晴らしが良く景色が良かったので
外であまおうソフトをいただきました^^

あまおう自体美味しいですが、加工せず、そのまんまのあまおうを
ソフトクリームにしてる感じで本当美味しかったです。





少し中に入ると囲炉裏を囲んでくつろげるスペースもありました。
ソフトクリーム食べながらあったまるのも贅沢な感じがします。


室内の様子です↓



そして一番びっくりしたのが、うきは市の観光コンシェルジュとして
なんとペッパーがいましたw



なんか溶け込んでる・・・



ホームページ




2016年3月5日土曜日

Heroku Buttonを設置してみる


Paas環境のサービスHerokuにはHeroku Buttonがあります
ボタン一つでアプリケーションを自分のHeroku環境にデプロイできてしまう
便利なボタンです。
今回はMichiEkiのHeroku Buttonを作成してみたいと思います
※ ちなみにMichiEkiは道の駅好きな筆者が勢い余って作ったAPIです^^;
   まだまだ作成途中なのでおかしな所があったら教えて頂けると助かります。

とりあえずできたものを動かしてみる


作成の手順の前に・・まずは作成したボタンを以下に貼ってます。
このボタンを押すと自分のHeroku環境にデプロイできます。
※ 事前にアカウントの作成が必要です

Heroku Deploy

押してログインすると、こんな画面になるので

そのままDeploy to Freeを押すとデプロイが開始されます。

デプロイが完了するとViewボタンが表示されるのでそのまま押します

以下の画面が表示されれば成功です。


Heroku Button作成



作成はとっても簡単です。
自分のGithubプロジェクトの直下にapp.jsonを作成し、ボタンのリンクを貼るだけです。とは言えプロジェクトはデプロイできる形式にしとかないといけません。
  • app.json
{
  "name": "MichiEki",
  "description": "API for provides information Road Station",
  "success_url": "/api/info",
  "env": {
    "BUILDPACK_URL": "https://github.com/heroku/heroku-buildpack-ruby"
  }
}
今回はnamedescriptionsuccess_urlenvを指定しています。
  • name ・・・ アプリ名
  • description ・・・アプリの詳細
  • success_url ・・・ デプロイ成功時に叩かれるURL
  • env ・・・ 今回はbuildpackを指定しています

buildpackは実行環境をまるっとパックしたもので、デプロイするアプリが動作するのに必要な環境をurlで指定します。
今回はGithubで公開されているHerokuのruby環境を指定しています。
自分で環境を作成しGithubに上げたurlを指定することも可能です
その他のスキーマに関してはこちらを参照

次にHeroku Buttonのリンクを貼ります。
Github上のREADME.mdに貼る場合は
[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy)
その他のサイトの場合は(上で貼ったリンクも↓になります)
<!-- template=[デプロイさせたいGithubのレポジトリのURL] -->
<a href="https://heroku.com/deploy?template=https://github.com/Slowhand0309/MichiEki">
<img src="https://www.herokucdn.com/deploy/button.png" alt="Heroku Deploy">
</a>
になります。
いや〜便利ですw

2016年3月3日木曜日

Elasticsearchを学ぶ 4 - Analyzer -


今回はMappingする際に設定できるAnalyzerというものを触ってみたいと思います。
その前に・・・
前回作成したmapping.jsonの補足です
"user_id":  {
  "type":   "string",
  "index":  "not_analyzed"
},
user_idのフィールドに対してtypeがstringでindexがnot_analyzedとなっています。
このnot_analyzedですが構文解析せず完全一致で検索します。
例えば・・
user_idにABCD-1234というデータが入ってる場合にnot_analyzedを指定せずにABCDや1234で検索した場合
ABCD-5678でもEFGH-1234でもヒットしてしまいます。
ユニークなフィールドにはつけたほうがいいやつです。
次に・・
"created":  {
  "type":   "date",
  "format": "strict_date_optional_time||epoch_millis"
}
ですが、JSONは日時のデータ型を持ってないので、以下の3つの方法で表現します。
  1. 文字列 ('2016-01-01'や'2016/01/01 10:10:50'など)
  2. 数値 (1970/01/01 00:00:00からの経過ミリ秒(UTC))
  3. 数値 (1970/01/01 00:00:00からの経過秒(UTC))
↑のstrict_date_optional_time||epoch_millis2になります

Analyzer


一つのanalyzerは一つのTokenizerと0個以上のTokenFiltersで構成されます。
サンプルとして以下のMappingをanalyzer_testというIndexに作成します。
※このサンプルではTokenFiltersは使用してません。
{
  "settings": {
      "analysis": {
          "analyzer": {
              "my_analyzer": {
                  "tokenizer": "my_tokenizer"
              }
          },
          "tokenizer": {
              "my_tokenizer": {
                  "type": "nGram",
                  "min_gram": "2",
                  "max_gram": "2",
                  "token_chars": [
                      "letter",
                      "digit"
                  ]
              }
          }
      }
  },
  "mappings": {
    "sample": {
      "properties": {
        "text": {
          "type": "string",
          "analyzer": "my_analyzer"
        },
        "subtext": {
          "type": "string",
          "analyzer": "my_analyzer"
        }
      }
    }
  }
}
my_tokenizerとして設定している箇所で、nGramとありますが、
これはN個文字を区切って検索するものです。ここではmin_grammax_gram
2と設定しているので2文字毎区切って検索を行います。
早速analyzer_test_mapping.jsonとして保存しインポート
$ curl -X POST 192.168.33.180:9200/analyzer_test -d @analyzer_test_mapping.json
ちゃんと設定できているか確認してみます。
$ curl -X GET '192.168.33.180:9200/analyzer_test/_mapping/sample/?pretty'
{
  "analyzer_test" : {
    "mappings" : {
      "sample" : {
        "properties" : {
          "subtext" : {
            "type" : "string",
            "analyzer" : "my_analyzer"
          },
          "text" : {
            "type" : "string",
            "analyzer" : "my_analyzer"
          }
        }
      }
    }
  }
}
ちゃんと設定できているので、データ登録して動作を確認してみます。
$ curl -X PUT 192.168.33.180:9200/analyzer_test/sample/1 -d '{
   "text" : "abcdefg", "subtext" : "123456789"
 }'
まずは、分割された2文字に対して検索。
$ curl -XPOST '192.168.33.180:9200/analyzer_test/_search?pretty' -d
'{ "query": { "match": { "text": "cd" } } }'
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.11506981,
    "hits" : [ {
      "_index" : "analyzer_test",
      "_type" : "sample",
      "_id" : "1",
      "_score" : 0.11506981,
      "_source" : {
        "text" : "abcdefg",
        "subtext" : "123456789"
      }
    } ]
  }
}
これはヒットしますが、1文字で検索してみると、
$ curl -XPOST '192.168.33.180:9200/analyzer_test/_search?pretty' -d
'{ "query": { "match": { "text": "c" } } }'
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}
最低2文字で単語を分割してる為こちらはヒットしません。
結構細かくAnalyzerを調整できるので、チューニング次第でヒット率が上がったり下がったりしそうですね。

2016年2月27日土曜日

Elasticsearchを学ぶ 3 - Mapping -



今回はデータをElasticSearchにインポートする際に、どのデータがどの項目に入るか?
そのデータは数値か?日付か?などなど、データインポートする際に必要な
Mapping作業を行ってみたいと思います。

  • 環境
引き続き前回の環境を前提に進めていきたいと思います。

Mapping用のJSONファイル作成


MySQLなどで言うところのテーブルCreate用のSQLを作成するような感じだと思います。
  • 用語
Mapping Types
Index内のTypeを定義するもの。
メタ要素として_index_type_id_sourceがある。
またMappingTypes内にはフィールドとしてPropertiesを持つ。

Field datatypes
フィールドのデータタイプを定義する。
主要なものとしてstringdatelongdoublebooleanipなどがある。
JSONのフォーマットで階層を用いてobjectnestedというデータタイプもあるらしい。

サンプルとして公式ページを参考にシンプルなMappingを作成してみます。
{
  "mappings": {
    "user": { *1
      "properties": { *2
        "name":     { "type": "string"  }, *3
        "age":      { "type": "integer" }  
      }
    },
    "book": {
      "properties": {
        "title":    { "type": "string"  },
        "description":     { "type": "string"  },
        "user_id":  {
          "type":   "string",
          "index":  "not_analyzed"
        },
        "created":  {
          "type":   "date",
          "format": "strict_date_optional_time||epoch_millis"
        }
      }
    }
  }
}
一応ユーザーと、そのユーザーが所有する書籍というイメージで作成してみました^^;
*1MappingType*2がフィールド、*3がフィールドとデータタイプになります

試しにこれをuser_booksというIndexに適用させてみたいと思います。
↑のJSONファイルをmapping.jsonとして保存し、保存した場所で以下コマンド実行。
$ curl -X POST 192.168.33.180:9200/user_books -d @mapping.json
{"acknowledged":true}が返って来れば成功

早速 http://192.168.33.180:9200/_plugin/head/ にアクセスして確認してみます。
ちゃんとuser_booksというIndexが作成されていれば成功です。

テストデータ投入


試しに1件userのTypeのid=1にデータを投入してみます。
$ curl -X PUT 192.168.33.180:9200/user_books/user/1 -d '{
  "name" : "hoge",
  "age" : 20
}'
以下のようなレスポンスがあれば成功です。
{
  "_index":"user_books",
  "_type":"user",
  "_id":"1",
  "_version":1,
  "_shards":
  {
    "total":2,
    "successful":1,
    "failed":0
  },
  "created":true
}
ちゃんと登録されているか確認します。
ちゃんと登録されてますね。
今回はここまで。