ようへいの日々精進XP

よかろうもん

JSON キーに Dot(.) が含まれている場合、どうやって jq でパースするのか

例えば

Elasticsearch の Index Mapping を確認したい場合。

curl -s localhost:9200/debug-2017.06.23/_mapping

以下のようなレスポンスが返ってくる。

{
  "debug-2017.06.23": {
    "mappings": {
      "debug-apilog": {
        "properties": {
          "@timestamp": {
            "type": "date"
...

冒頭のキー debug-2017.06.23 は要らないので、以下のように jq でパースしようとすると…

$ curl -s localhost:9200/debug-2017.06.23/_mapping | jq '.debug-2017.06.23'
jq: error: Invalid numeric literal at EOF at line 1, column 10 (while parsing '2017.06.23') at <top-level>, line 1:
.debug-2017.06.23
jq: 1 compile error
(23) Failed writing body

あれ?ってなった。

色々と試してみるも…

$ curl -s localhost:9200/debug-2017.06.23/_mapping | jq ".debug-2017.06.23"
jq: error: Invalid numeric literal at EOF at line 1, column 10 (while parsing '2017.06.23') at <top-level>, line 1:
.debug-2017.06.23
jq: 1 compile error
(23) Failed writing body

$ curl -s localhost:9200/debug-2017.06.23/_mapping | jq ."debug-2017.06.23"
jq: error: Invalid numeric literal at EOF at line 1, column 10 (while parsing '2017.06.23') at <top-level>, line 1:
.debug-2017.06.23
jq: 1 compile error
(23) Failed writing body

うーむ。

ドキュメントを見る

ドキュメント には以下のように書かれていた。

Object Identifier-Index: .foo, .foo.bar

The simplest useful filter is .foo. When given a JSON object (aka dictionary or hash) as input, it produces the value at the key “foo”, or null if there’s none present.

...(snip)

For example .["foo::bar"] and .["foo.bar"] work while .foo::bar does not, and .foo.bar means .["foo"].["bar"]

どうやら、["debug-2017.06.23"] とすれば良さそう。

挙動を確認してみる。

#
# jq のバージョン
#
$ jq --version
jq-1.5

#
# シンプルなフィルタ
#
$ echo '{"foo":"bar"}' | jq .
{
  "foo": "bar"
}

$ echo '{"foo":{"bar":"baz"}}' | jq '.foo.bar'
"baz"

#
# jq 的には .foo|.bar と同義
#
$ echo '{"foo.bar":"baz"}' | jq .foo.bar
null

#
# なんかおかしい
#
$ echo '{"foo.bar":"baz"}' | jq .["foo.bar"]
{
  "foo.bar": "baz"
}
"baz"

#
# うまくいった
#
$ echo '{"foo.bar":"baz"}' | jq '.["foo.bar"]'
"baz"

#
# これでもうまくいった
#
$ echo '{"foo.bar":"baz"}' | jq '."foo.bar"'
"baz"

ということで

冒頭の Elasticsearch Index の mapping 確認については、以下のように書けば良さそうということが解った。

curl -s localhost:9200/debug-2017.06.23/_mapping | jq '.["debug-2017.06.23"]'

もしくは

curl -s localhost:9200/debug-2017.06.23/_mapping | jq '."debug-2017.06.23"'

以下のように意図した出力になった。

{
  "mappings": {
    "debug-apilog": {
      "properties": {
        "@timestamp": {
          "type": "date"
        },
        "@version": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
...

めでたし、めでたし。

参考

有難うございました。

2017 年 06 月 20 日(火)

ジョギング

  • 香椎浜 x 2 周
  • 引き続き、右太ももから股関節にかけて違和感、体固い

日課

  • 今日は無し

仕事で

諸々ミスが目立った一日だった。

気を引き締めんといかんばい。

Elixir School に入学した(1)〜 パイプライン演算子 〜

Elixir School という学校に入学した

入試は無いけど、卒業は何時になるかわからない。

elixirschool.com

上記のサイトの各レッスンを適当に掻い摘んで勉強してみようと思う。

尚、手元の環境については Docker イメージで提供されている Elixir 環境又は MacOS X に導入した Elixir を利用する。

#
# Docker の場合
#
$ docker run -t -i --rm elixir
Erlang/OTP 19 [erts-8.3.5] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.4.4) - press Ctrl+C to exit (type h() ENTER for help)

#
# MacOS X の場合
#
$ iex
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (1.4.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

今日は

パイプライン演算子

Elixir の特徴の一つであるパイプライン演算子から学んでいきたい。

elixirschool.com

パイプライン演算子とは

  • ある式の結果の別の式の第一引数として渡す処理
  • シェル操作の |(パイプ)に似た処理を行える
  • 複数の引数がある場合にも利用可能

例えば

文字列を分割して list オブジェクトを生成したい場合。

iex(1)> String.split("hoge fuga")
["hoge", "fuga"]

こんな風に書きたくなる衝動。

パイプライン演算子を使えば以下のように書くことができる。

iex(3)> "hoge, fuga" |> String.split
["hoge", "fuga"]
iex(4)>

これは便利だし、個人的には後者(パイプライン演算子)を使った方が直感的な気がする。

更に

文字列を大文字に変更してから list 化する場合。

iex(9)> String.split(String.upcase("hoge fuga"))
["HOGE", "FUGA"]

一応、こんな風に書いても動くけど、パイプライン演算子を使えば…

iex(13)> "hoge fuga" |> String.upcase |> String.split
["HOGE", "FUGA"]

コード量的にはそんなに変わらないような気がするけど、パイプライン演算子を使った方が何をしているのか一目で分かるような気がするし、シェルのパイプっぽい。

も少し

以下のようなテキストファイルがあったとする。

$ cat test.txt
aaaaaaaaaaaaaaaaa
bbbbb
ccccccccccccc
dddddddd
eeeeeeeeeee

そのテキストファイルを読み込んでリスト化する例。

#
# パイプライン演算子を利用
#
$ iex
...
iex(1)> File.read!("test.txt") |> String.replace("\n", " ") |> String.split
["aaaaaaaaaaaaaaaaa", "bbbbb", "ccccccccccccc", "dddddddd", "eeeeeeeeeee"]

パイプライン演算子を使わない場合には以下のように書く。

#
# パイプライン演算子未使用
#
$ iex
...
iex(1)> String.split(String.replace(File.read!("test.txt"), "\n", " "))
["aaaaaaaaaaaaaaaaa", "bbbbb", "ccccccccccccc", "dddddddd", "eeeeeeeeeee"]

なんだかパイプライン演算子を使った方が見た目が良い。

データ変換の流れが読める

Elixir のパイプライン演算子ってどんなことが出来ると?というレベルだったけど、プログラミング Elixirの冒頭に「プラグラミングはデータの変換をするものだ」という文言があったけど、まさにパイプライン演算子はデータ変換の過程を読む出来ると思う。(プログラミング Elixir の 55 ページより引用)

2017 年 06 月 18 日(日)

抜けるような

青空。

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

AWS Batch が

東京リージョンで使えるようになった…

ので、従来 EC2 で行っていたような cron をトリガとした夜間バッチの置き換えになるのではと思って資料を読んだりしてみた。

www.slideshare.net

aws.typepad.com

夜間バッチ

夜間バッチの置き換えになるのではという期待を込めた視点では…

  • 現時点では残念ながら cron をトリガとするような夜間バッチの置き換えにはならない
  • AWS Batch はユーザが任意に投入したジョブをキューイングして、 リソース状況に合わせて、効率良く実行するシステム

ということで、夜間バッチの置き換えは意図したサービスではないことが判明。Data Pipeline の方が良さそう(今のところ)。

その他

  • コンピュート環境(実際に処理を行う部分)には Docker イメージを利用するとのことなので、Docker イメージ内に必要なツールやスクリプトや設定等を予め組み込んでおく必要がある

奥さんと

博多デート。

たまたま二人の意見が一致して夕飯は手羽先を食べる。

お店の名前は忘れてしまったけど、塩焼きがとても美味しかった。

2017 年 06 月 17 日(土)

ジョギング

日課

  • (腕立て x 30 + 腹筋 x 30) x 3

義父の四十九日法要

石原裕次郎の「わが人生に悔いなし」を聴きながら。

f:id:inokara:20170618083159j:plain

四十九日の意味についてググってみたら…

四十九日は、初七日から七日ごとに受けたお裁きにより来世の行き先が決まるもっとも重要な日で、「満中陰(まんちゅういん)」と呼ばれます。

故人にとっても、家族にとっても重要な日になるらしい。

そんな日に我々夫婦は小さな写真立てに義父の写真を飾り、オレンジ色のバラの花を一輪挿しに飾り、自宅から手を合わせた。

きっと、義父のことだから閻魔大王にも見初められて極楽浄土に途中下車無しでたどり着かれんたんではないかと思う。

合掌。

夕飯

自宅近くの回っていないお寿司屋さんに奥さんと義父も一緒に。

f:id:inokara:20170618083102j:plain

義父が大好きだったという「あげ巻貝」とビールや日本酒を頂く。

たまたまカウンターの横に座ったさんちゃんとゆーこさん、大将も交えて色々と人生について語る。

大将が慰めてくれるように言った、

葬式や仏事ってさ、生きてる人間の自己満足なんだよ、だから形なんてどーでもいいんだよ

って言いながら義父にお酒を頂いたのがすごく嬉しくて心が救われた気がした。

初めて暖簾をくぐったお店だったのに、義父が導いてくれたのかとても楽しく記憶に残る一日になった。

Elixir と Maru を使って REST API アプリケーションを丸っと 10 分くらいで実装する(写経する)

ちょっと

REST API で操作出来たら便利になりそうだなあと思った部分があったので、maru で実装してみようと思って、Maru ドキュメントの Basic Usage を写経してみたメモです。

◯ (Maru)

github.com

先日の fukuoka.ex で教えて頂きました。

www.slideshare.net

特徴については、上記資料の 6 ページに簡潔に纏まっていますので転載させて頂きます。

https://image.slidesharecdn.com/maru-170611034513/95/elixir-maru-rest-api-6-638.jpg?cb=1497152754

有難うございます。

Rubygrape という REST API ライクな API を生成するフレームワークインスパイヤされているらしい。grape 自体初耳でした。

Basic Usage 写経

写経した環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G1421

$ iex -v
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

IEx 1.4.4

新規アプリケーションの作成

$ mix new oreno_app

以下のように出力されます。

$ mix new oreno_app
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/oreno_app.ex
* creating test
* creating test/test_helper.exs
* creating test/oreno_app_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd oreno_app
    mix test

Run "mix help" for more commands.

mix.exs に依存するモジュール名を定義

mix.exs に以下を追記する。

defp deps do
  [ {:maru, "~> 0.11"} ]
end

アプリケーションの実装

lib/oreno_app.ex に以下のように実装します。

defmodule OrenoApp.Router.Homepage do
  use Maru.Router

  get do
    json(conn, %{ hello: :maru })
  end
end

defmodule OrenoApp.API do
  use Maru.Router

  plug Plug.Parsers,
    pass: ["*/*"],
    json_decoder: Poison,
    parsers: [:urlencoded, :json, :multipart]
  
  mount OrenoApp.Router.Homepage

  rescue_from :all do
    conn
    |> put_status(500)
    |> text("Server Error")
  end
end

アプリケーションを起動させる為の設定

config/config.exs に以下を追記します。

config :maru, OrenoApp.API,
  http: [ip: {0, 0, 0, 0}, port: 8881]

ip: {0, 0, 0, 0} を追加しない場合、デフォルトでは 127.0.0.1 でのみ Listen するので Docker で動かす場合等は注意しませう。

アプリケーションの起動

依存するモジュールの導入 (maru の導入)

$ mix deps.get

以下のように出力されます。

$ mix deps.get
Running dependency resolution...
Dependency resolution completed:
  cowboy 1.1.2
  cowlib 1.0.2
  maru 0.11.4
  mime 1.1.0
  plug 1.3.5
  poison 3.1.0
  ranch 1.3.2
* Getting maru (Hex package)
  Checking package (https://repo.hex.pm/tarballs/maru-0.11.4.tar)
...

サーバーの起動

$ iex -S mix

以下のように出力され、コンパイルが走ります。

$ iex -S mix
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

==> poison
Compiling 4 files (.ex)
Generated poison app
===> Compiling cowlib
src/cow_multipart.erl:392: Warning: crypto:rand_bytes/1 is deprecated and will be removed in a future release; use crypto:strong_rand_bytes/1
...

09:40:54.442 [info]  Starting Elixir.OrenoApp.API with Cowboy on http://0.0.0.0:8881
Interactive Elixir (1.4.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

Cowboy という Web サーバーが起動します。

Hello Maru

Web サーバーが起動したら API にアクセスしてみる。

$ curl http://127.0.0.1:8881 -w '\n' -i
HTTP/1.1 200 OK
server: Cowboy
date: Sat, 17 Jun 2017 01:02:15 GMT
content-length: 16
cache-control: max-age=0, private, must-revalidate
content-type: application/json; charset=utf-8

{"hello":"maru"}

やったー。

Basic Usage + アルファ

プラスアルファ

  • Docker で動かしたい

Dockerfile

mix deps.get 等がそのまま利用出来ないので焦ったものの、以下のような Dockerfile で一通り動かせることを確認しました。

ROM elixir
ADD ./ /app
WORKDIR /app
RUN mix local.hex --force && mix deps.get
EXPOSE 8881
CMD ["mix", "run", "--no-halt"]

あと、フォアグラウンドで Web サーバーを動かす方法についても悩みましたが、mix run --no-halt でいけるようです。

docker-compose.yml

version: '2'

services:
  hello_maru:
    build: ./
    volumes:
      - ./:/app
    ports:
      - "8881:8881"

Docker 環境でも Hello Maru

ビルドして…

$ docker-compose build
Building hello_maru
Step 1/6 : FROM elixir
 ---> 066128183b87
Step 2/6 : ADD ./ /app
 ---> f5e8affa64c7
Removing intermediate container 6cde27f8f51f
Step 3/6 : WORKDIR /app
 ---> 9c269ced9d4f

...

Removing intermediate container 01de153aa0ac
Successfully built 8cadb37872f7

Run すると…

$ docker-compose up -d
Creating network "orenoapp_default" with the default driver
Creating orenoapp_hello_maru_1

$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                                            NAMES
0ecfa2a6d96b        orenoapp_hello_maru       "mix run --no-halt"      11 seconds ago      Up 9 seconds        0.0.0.0:8881->8881/tcp                           orenoapp_hello_maru_1

Hello Maru.

$ curl http://127.0.0.1:8881 -w '\n' -i
HTTP/1.1 200 OK
server: Cowboy
date: Sat, 17 Jun 2017 01:02:45 GMT
content-length: 16
cache-control: max-age=0, private, must-revalidate
content-type: application/json; charset=utf-8

{"hello":"maru"}

本当に

丸っと 10 分くらいで Maru を体験することが出来ました。

こちらの写経については、進捗が芳しくありません。ボチボチやっていきます。