OmniAICreator/Qiita-1.07M huggingface dataset:
Puppeteer(ぱぺてぃあ)、Headress Recorderで自動化が進んだ

#実現させたいこと
とあるサイトのマイページから、データを毎日ダウンロードしたい。しかし、ダウンロードには1,000クリック程度必要。
画面が遷移してもURLが変わらないので、SeleniumやPRAでの実現は難しい。
Chrome操作の自動化ができ、さらにExcelマクロのような録画ツールもあるそうなので、puppeteerを試してみた。
環境構築と習得に1日ほどかかったが、使いやすくて感動！

#puppeteerとは
Chromeブラウザを操作できるNode.jsのライブラリ
ヘッドレスブラウザを使用できるので高速

#Headless Recorderを使う
[HeadlessRecorder](https://chrome.google.com/webstore/detail/headless-recorder/djeegiggegleadkkbgopoonhjimgehda)をChromeの拡張機能に追加。
録画ボタンを押して、録画したい操作をすると、Puppeteerコードを出力してくれる。
puppeteerで必要なidやclassを調べる手間が省けて便利。

#Headless Recorderでできなかったこと
##①録画が上手くいかない箇所があった
idやclassが一意でない箇所で、コードに落とせていない操作があった。
お客様番号を使って指定できそうだったので、お客様番号を含むXpathで指定した。

```javascript:お客様番号のクリック
const customerNumber = '123456'; //文字列型で店舗番号を指定
const customerLink = "//a[contains(@onclick,customerNumber)]";  //XPathを検索

await page.waitForXPath(customerLink);
const customerLinkClick = await page.$x(customerLink);
await customerLinkClick[0].click();
```

##②ダウンロード先の指定

```javascript:ダウンロード先を指定
  const downloadPath = 'C:\\yyy\\xxx\\test'; // 絶対パスで指定
  await page._client.send(
    'Page.setDownloadBehavior',
    { behavior: 'allow', downloadPath: downloadPath }
  );
```

# 感想
・学習コストは低め。PythonとGASしか使ったことがない私でも、環境の設定からはじめて、1日あれば使えた。Headless Recorderが偉大。

# 参考
・[Puppeteer入門(本)](https://www.amazon.co.jp/Puppeteer%E5%85%A5%E9%96%80-%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0-Web%E6%93%8D%E4%BD%9C%E8%87%AA%E5%8B%95%E5%87%A6%E7%90%86%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E3%83%B4%E3%82%A7%E3%83%8D%E3%83%81%E3%82%A2%E5%86%92%E9%99%BA%E5%9C%98/dp/4798055204) 
・[HeadlessRecorder](https://chrome.google.com/webstore/detail/headless-recorder/djeegiggegleadkkbgopoonhjimgehda)
javascriptのforEachを利用する際の注意点

#javascriptのforEachについて

色々な言語には、for、foreachが用意されています。
javascriptで言うと、foreachはforEach()に当たるっぽいですね。

最近までちょっと勘違いしていたことがあります。
その気付きを共有したいと思います。

#他言語とjavascriptのforEachはそもそも考え方が違う。

例えばPHPで言うと、foreachは、配列、オブジェクトで利用できるfor文ですね。
利用できる型が決められていますが、動作は大体forと一緒です。

javascriptの.forEach()も同じだと思ってました。
ですが、そもそもforEach()は根本的な考え方が違うことに気がつきました。

#javascriptのforEach()は「繰り返し」の命令ではない！

使い方自体は、他言語と似ていますよね。

```forEach.js
const array = ['a','b','c'];

array.forEach(element => console.log(element));
```

じゃあこれって何が違うの？
もう少し分かり易く書いてみましょう。

```forEach2.js
const array = ['a','b','c'];

array.forEach(function(element){
  console.log(element);
});
```

上記は書き方を変えただけです。
1番重要な点は、これがfunction（即時関数）であると言う点です。
javascriptのforEach()は、配列の順番通り即時関数を叩くと言う命令である。と言うことです。

#ふーん。だからどうなの？

ここで重要になってくるのは、即時関数は、javascriptの言語特性上、平行で実行されると言う点です。
for命令は、配列のインデックスの0番目から順番に実行されます。
0番目の処理が完了した後に、1番目の処理を始めます。

一方、forEachで叩かれた即時関数は、即時関数が叩かれるタイミングこそ0番目からですが、0番目が終わった後に1番目を実行。と言う動作にはなりません。

平行処理が行われますので、1番目に叩かれた配列index0が一番最初に終わるとは限らないのです。
ここが最も重要な点です。

forと動作は同じだと思って、forEachを使うと、想定していた結果にならないと言うのは、大体この特性のせいであると、最近気づきました。

javascriptの言語特性と、即時関数である。と言うことに気がつけばすぐたどり着ける内容ではありますが、以前かなりはまった部分なので、私と同じ悩みを抱えている方がいれば、参考にしていただけたらと思います。
AIに自分好みのアイドルを生成させる

# はじめに
@Kim_Burton さんの[AIに自分だけの美女を生成してもらう](https://qiita.com/Kim_Burton/items/d7cbab04253a843f18ed)という記事を読んで，美女生成を実際に学習からトライしているのを見たのがきっかけです．

僕が先日フォーラム顔学2020で発表してきた内容とカスっていたので自分のやり方でこのテーマをマネしてみます．引用先の記事では一から学習を行っているので，画像の条件などをそろえるのも大変です．じゃあ学習しなければいいのでは？というのが僕の考えです．今日は自分のやってる研究を含めて少し紹介したいと思います．詳細や実装はまだ研究がまとまりきっていないので伏せます．

# 深層生成モデルと潜在変数について
[StyleGAN2](https://arxiv.org/abs/1912.04958)は高解像度の顔画像生成モデルのSOTAです．NVIDIAが開発をしたモデルで，表現力が非常に高く潜在変数空間も滑らかなことで知られています．[Image2StyleGAN](https://arxiv.org/abs/1904.03189)と呼ばれる技術も研究がなされており，データセットに存在しないような現実の顔画像でも再構成可能であることが分かっています．

近年の深層生成モデルは，基本的には潜在変数と生成画像が1:1対応をしています．なので，生成画像が所望の画像と同じになるように潜在変数に最適化処理をかけてあげれば実在する人の顔画像を再現することができます．

StyleGAN2で再実装して僕の顔でやってみた結果が以下です．そこそこの精度が出ていると思います．
右が本物で左が再構成画像です．
![スクリーンショット 2020-11-26 17.53.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593087/06b2c43f-811e-9838-334c-60ba5836d7ac.png)
![スクリーンショット 2020-11-26 17.54.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593087/a3a9b14b-495c-2f02-0aee-350b2db3caf4.png)

潜在変数に関する記事は[こちら](https://qiita.com/triwave33/items/a5b3007d31d28bc445c2)が非常にわかりやすいです．ともかく，深層生成モデルは潜在変数から画像を生成するので，1:1で対応づいているということがわかれば大丈夫です．

# アイドル顔を生成する方法
先ほども説明した通り，StyleGAN2は所望の顔画像を再現することができます．これはすなわち「ある人物の顔画像に対応する潜在変数を得ている」と言えます．なので，潜在変数空間上に好みの女性の画像をどんどん埋め込んで（Image Embedding）やってその分布を計算すれば，好みのアイドル分布を取得することができます．

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593087/f7e88e46-5de0-c265-8db9-dca011109fde.png)

分布さえ得られてしまえばあとはサンプリングで新たにその分布からサンプル潜在変数を取得して，好みのアイドル顔サンプルを生成するだけです．

# AKB48からアイドル顔を生成する
あんまりアイドルに詳しくないので、とりあえずアイドルグループとしてAKB48さんの画像を使わせていただきました．
使った画像は公式サイトの[メンバー一覧](https://www.akb48.co.jp/about/members/)です．僕が画像を取ってきた時とものが変わっているので、生成画像のテイストが若干変わると思います．AKB48の方の顔を再構成したものを勝手にアップすると勝手に改変して公開したことになってしまう（著作者人格権など）のではないかと思うので，あくまでも分布を作成するまでに得た結果は公開しません．

前述の手法でメンバーの顔を潜在変数空間上に埋め込んでその分布を計算，その分布からサンプル潜在変数を取ってきてStyleGAN2で生成しました．
![スクリーンショット 2020-11-26 18.03.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593087/728f0d5c-eca2-a3a2-1b60-fe5c5b1ab981.png)

どうでしょうか？それなりにちゃんとアイドル顔が生成できたと思います．こんな感じで、もう画像生成系は再学習が不要な時代になってきていると思います．巨人の肩に乗らせていただきましょう．

ちなみに，@Kim_Burton さんの記事でも挙げられているように平均顔は顔が集団として持つ特徴を捉えることができます．これは潜在変数空間上でもおそらく同じことが言えると思います．
一応AKB48の潜在変数空間上の平均顔もおまけで載せておきますね．重ね合わせ法を使わないので普通の平均顔みたいにぼやけたりしません．

![スクリーンショット 2020-11-26 18.21.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593087/61536d9a-72b0-9e31-4faf-55518a315d02.png)

# さいごに
フォーラムでも発表しましたし、そろそろかなと思って少しだけ研究の内容を記事にしました．
実装の詳細やコードに関しては修論が落ち着いて気が向いたら公開しようと思います．それでは．

# 参考
- https://arxiv.org/abs/1912.04958
- https://arxiv.org/abs/1904.03189
- https://www.akb48.co.jp/about/members/
- https://qiita.com/Kim_Burton/items/d7cbab04253a843f18ed
- https://ci.nii.ac.jp/naid/110003312015
- https://qiita.com/triwave33/items/a5b3007d31d28bc445c2
Requestは、ただのバリデーションファイルじゃない【2020/11/26】

今まで僕は、LaravelのRequestファイル(ex userRequest.phpとか)は、バリデーションをするためにあるファイルだと思っていました。しかし、バリデーション機能はどちらかとういうとおまけ。

- △　バリデーション機能
- ◯　データを保持するオブジェクト + おまけでバリデーション

だった。

strutsでいう、ActionFormに一番近い(懐かしいな)。
ここからフォームやクエリパラメータに突っ込んだデータを取り出してくる。

だから、Controllerファイルの中で、

```php:xxxController.php
$this->all() 
```
でデータとれるのはもちろん、

```php:xxxRequest.php
$this->all() 
```

と、Requestファイルの中でも普通に`$this`でデータが取れる。

バリデーションしているだけなのになんで`Request`ファイルなのか疑問だったけど、get/postの`リクエスト`を受け取っている紛れもないファイルだった。

フレームワーク作る人って、すごいな。
reactのaxiosでリクエスト失敗時の例外処理をいれる


## catchで例外処理

```react
        request
            .post("/getpodstatus", obj)
            // やりたい処理
            .then(request => {
                this.setState({
                    podstatus: request.data.items[0].status.phase
                });
                console.log(this.state.podstatus)
                }
            )

            // エラー時というかやりたい処理ができなかった場合の処理
            .catch(error => {
                this.setState({
                    podstatus: 'Stop'
                });
                console.log('POST失敗時の処理')
            })
```
Pythonのdataclassの落とし穴

## 落とし穴: クラスを作りなおしても前のクラスのデータを持っている

このコードを走らせたときにハマった。

```python
from dataclasses import dataclass, field
from typing import List


@dataclass
class Struct:
    x: str


@dataclass
class Container:
    content: List[Struct] = field(default_factory=list)


@dataclass
class ContainerWrapper:
    container: Container = Container()

    def append(self, s: Struct) -> None:
        self.container.content.append(s)
        return None

    def store_structs(self, s_list: List[Struct]) -> None:
        for s in s_list:
            self.append(s)
        return None

s_list = [Struct("foo")]
ContainerWrapper().store_structs(s_list)
# recreated instance strangely contains old instance's data.
wrapper = ContainerWrapper()
print(wrapper.container) #=> Container(content=[Struct(x='foo')])
```

なんで`ContentainerWrapper`クラスを作り直したあとも作り直す前のインスタンスのデータを持っとるんやってなっでハマった。

## 種明かし: 実は`@dataclass`に問題があるみたい

[Pythonのドキュメント](https://docs.python.org/ja/3/library/dataclasses.html)では、`@dataclass`デコレータを用いてデフォルトの値を設定すると、 `__init__()`でデフォルト値を設定したのと同じように、インスタンス変数に値が代入されると主張している。

すなわち、以下の2つのコードは等価であるはずだ。

```python
from dataclasses import dataclass

@dataclass
class Foo:
    x: int = 0
```

```python
class Foo:
    def __init__(self, x: int = 0) -> None:
        self.x = x
```

しかし、python 3.8.6と3.9.0で試してみた限り、`@dataclass`を使って定義したデフォルト値はクラス変数として扱われてしまった。

```python
from dataclasses import dataclass

@dataclass
class Foo:
    x: int = 0

    @classmethod
    def print_x(cls):
        print(cls.x)

Foo.print_x()  #=> 0
```

クラス変数とインスタンス変数を取り違えれば問題が起こることは[この記事](https://qiita.com/7shi/items/d37493c58a8bb8d7beed)が詳しい。
Web Workerを実装してみる


## Web Workerって？

>Web Worker とは、ウェブアプリケーションにおけるスクリプトの処理をメインとは別のスレッドに移し、バックグラウンドでの実行を可能にする仕組みのことです。時間のかかる処理を別のスレッドに移すことが出来るため、 UI を担当するメインスレッドの処理を中断・遅延させずに実行できるという利点があります。

by [MDN] (https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API)

JSはシングルスレッドですので、本当の意味での並列処理が行えません。
なのでめちゃ重な処理を書いてしまうと、その他のボタンやら何やら効かなくなって、サイトが完全に固まってしまいます。
これを解消してくれるのが`Web Worker`です。
昨今ではSPAなどによりでクライアント側で重たい処理をすることが増えていますので、ぜひ使いこなしておきたいと思っています。


## 現在ブラウザ側の対応はどうなってる？
![スクリーンショット 2020-11-26 16.51.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441432/f662f868-805e-f880-7290-9033091a904e.png)
by [Can I use](https://caniuse.com/?search=worker)
Opera以外はほとんどOKですね。

## とにかくコード

```js:index.js

const worker = new Worker('worker.js');

worker.addEventListener('message', (e) => {
  console.log('Workerから受け取ったデータは: ', e.data);
}, false);

worker.postMessage('Hello, world');
```

```worker.js
self.addEventListener('message',(e) => {
  self.postMessage(e.data);
}, false);
```

ちなみに、workerのスクリプト内のグローバルはワーカー自身なので、下記のように書いてもOKです。

```worker.js
addEventListener('message',(e) => {
  postMessage(e.data);
}, false);
```

```:結果
Hello, world
```

図でみるとわかりやすいです。

![worker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441432/2e04b31f-3e8e-02e4-4435-f83e2093afb7.png)


それぞれのjsの `onmesssage` で、受信時/送信時のイベントを記述することができます。
受信データ/送信データはいずれも messsageイベントのdata属性から取得できます。
思ったよりも非常にシンプルです。

### workerを終了したい場合。

```js
worker.terminate();
```
`terminate`メソッドを使って直ちに終了することができます。

## エラーハンドリング

>Worker で実行時エラーが発生すると、 onerror イベントハンドラーが呼び出されます。これは、 ErrorEvent インターフェイスを実装している error イベントを受け取ります。

とのことです。（by [MDN] (https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API))
早速試してみましょう。

```js:index.js
const worker = new Worker('worker.js');

worker.addEventListener('error', function (e) {
  console.log(e)
}, false);
worker.postMessage('Hello World');

```

```js:worker.js
self.addEventListener('message', (e) => {

  throw new Error('何かしらのエラー')
  self.postMessage(e.data)
}, false)

```

結果:
![スクリーンショット 2020-11-26 17.47.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441432/c257fb68-1a9f-a464-0356-949c17d20d64.png)

確かに `ErrorEvent` をキャッチしています。

たくさんのプロパティがありますが、下記の3つが重要な項目です。

`message` ・・・　人間用メッセージ
`filename` ・・・ エラー発生元のファイル名
`lineno`・・・エラーが発生した行番号


## スクリプト/ライブラリのimport
Workerスレッド側で何かインポートしたい場合は `importScripts()`　を使います。
ここで引数として指定したスクリプトが実行されます。

```js
importScripts('test.js')
importScripts('foo.js', 'bar.js')
```


## 別ファイルでなく埋め込みでWorker
ファイルの記述を文字列化しBlobを生成、そしてそのURLを渡せばOK。
実際の実装ではあまり使わなそうだと思ったので例は割愛します。

### 注意点
Workerは直接DOMを操作できない点、そしてwindowのメソッドやプロパティは使用できない点に注意です。(worker自身がグローバルのため)
逆にそれ以外ならほとんど使用可能です。
MDNに使用できるクラス・関数がまとめられています。
https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workers

また、Workerとやり取りする際のデータは共有しているのではなく、それぞれでコピーされています。
なので渡したデータをWorker側で操作しても、コピー元には関与できない点に注意です。



次回はService Workerについて学習予定です。

## 参考資料
https://www.html5rocks.com/ja/tutorials/workers/basics/
https://www.codit.work/notes/ytq5ztvnhceco3jn2bkb/
【Route53】サブドメインの登録メモ書き

##内容
Route53に登録済みのドメインのサブドメインを設定する。
以下の形式のNSレコード及びAレコードをセットする。

**<サブドメイン名>**  IN NS  **<サブドメインを管理する権威DNSサーバのFQDN>**
**<サブドメインを管理する権威DNSサーバのFQDN>** IN A  **<サブドメインを管理する権威DNSサーバのグローバルIPアドレス>**

![tempsnip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647616/a0745352-eab3-48fe-8cb2-50f134d650e5.png)

コマンドプロンプトから以下名前解決確認し、正常に解決されること。

```bash
# NSレコードの確認
nslookup -type=ns <サブドメイン名>

# Aレコードの確認
nslookup <サブドメインを管理する権威DNSサーバのFQDN> 
```

以下サンプル出力

```bash
# NSレコード
C:\Users\ryosu>nslookup -type=ns subdomain.ryosukepersonal.com
サーバー:  UnKnown
Address:  192.168.11.1

権限のない回答:
subdomain.ryosukepersonal.com   nameserver = ns.subdomain.ryosukepersonal.com

ns.subdomain.ryosukepersonal.com        internet address = 13.231.237.4

# Aレコード
C:\Users\ryosu>nslookup ns.subdomain.ryosukepersonal.com
サーバー:  UnKnown
Address:  192.168.11.1

権限のない回答:
名前:    ns.subdomain.ryosukepersonal.com
Address:  13.231.237.4
```
Athenaからクエリ出来てCSVダウンロードやS3から直接ダウンロード禁止する

ただのメモです。


AthenaとGlueのリストと読み取り権限ポリシー作ってアタッチ

S3の読み取り権限ポリシーを、calledviaを使いAthenaからだけ許可する形で作成してアタッチ

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucketMultipartUploads",
                "s3:AbortMultipartUpload",
                "s3:CreateBucket",
                "s3:ListBucket",
                "s3:GetBucketLocation",
                "s3:ListMultipartUploadParts"
            ],
            "Resource": [
                "arn:aws:s3:::aws-athena-query-results-xxxxxxxxxxxx-ap-northeast-1/*",
                "arn:aws:s3:::aws-athena-query-results-xxxxxxxxxxxx-ap-northeast-1"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "aws:CalledVia": "athena.amazonaws.com"
                }
            }
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::test",
                "arn:aws:s3:::test/*"
            ],
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "aws:CalledVia": "athena.amazonaws.com"
                }
            }
        }
    ]
}
```
RailsアプリをHerokuでデプロイしてみた。

Railsでアプリケーションを作成してみたのでHerokuでデプロイしてみました。
下の方のを参考にしたらなんなく出来ました！アウトプットの為に投稿させてもらいます。
[【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】](https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39#comments)
自分はPostgreSQLを使って作っていたので最初の設定はスルーして実装しました。
gitも入っていたので省略します。

##環境
Rails 5.2.1
Ruby 2.6.5
git 2.23.0
heroku/7.47.3 darwin-x64 node-v12.16.2

##Herokuとは

PaaS（Platform as a Service）と呼ばれるサービス
開発したwebアプリケーションを開発、実行するための基盤(プラットフォーム)を提供するサービス
無料で使用する事が可能で簡単にデプロイ出来ちゃいます。

##Herokuの登録
日本語翻訳の方がありましたのでこちらからどうぞ！
パスワードはターミナルでログインする際に使いますので忘れない様に！
https://signup.heroku.com/jp
会員登録が終わったら、PCとHerokuを紐づける為にcliをダウンロードします。OSで違いが出るのはここぐらいです！
https://devcenter.heroku.com/articles/heroku-cli

Herokuが入っているか確認

```
$heroku --version
>> heroku/7.47.3 darwin-x64 node-v12.16.2
```

##PCとHerokuの紐付け

```
$heroku login
```

メールアドレスとパスワードを入力します!

```
Enter your Heroku credentials:
Email: ~~~~~@example.com （登録したメールアドレスを）
Password: **********  (登録したパスワードを)
Logged in as ~~~~~@example.com
```

URLがアプリ名になります

```
$heroku create アプリ名
```

##Herokuにデプロイする

```
$git push heroku main or master ←自分のブランチ名によります
```

##本番環境のマイグレーション

```
$heroku run rails db:migrate
```

これで特にエラーとかでなければ作成されたURLを叩いてみましょう！
※自分はHerokuにデプロイする時に以下のエラーが出ました

```
error: src refspec master does not match any
error: failed to push some refs to 'アプリURL.git'
```
自分のgitにはmasterというブランチはなくmainというブランチだったのでエラーが出てました。
Android11での内部ストレージのAndroid/data以下に端末でアクセスする方法

## ただの備忘録

端末からの操作の話。
Android11から何故か普通のファイラーや、Google Playから入れたファイラーでは、
内部ストレージにある `Android/data` 以下にアクセス出来ない状況が発生した。

ただし以下の方法であれば、アクセスが可能

```
1. 「設定」を開く
2. 「ストレージ」を選択
3. 「ファイル」を選択
4. ファイラーを選択
```

これで開いたファイラーでは、
`Android/data` 以下にアクセスできる。（出来た）

中のデータを選択して圧縮して、コピーや転送も可能だった。
(つまり今まで(Android 10まで)通り使えるっぽい)


...アクセスがだるくなっただけ (?)
(セキュリティのためなんだろうけどさ...)

注：実験したのはPixel3a,4
phpUnit: 対象コードでワーニングが出た場合、UTを赤くしたい（特定のテストクラスのみの場合）

備忘録: phpUnit でテスト対象のコードにワーニングがあった場合も気づきたい時用のsetUp/tearDown組み合わせ。
phpUnit 全体の設定を変更したくないが、ワーニングもキャッチしておきたい場合役に立つ。

```
    public function setUp()
    {
        set_error_handler(function ($errorCode, $errStr, $errFile, $errLine) {
            throw new RuntimeException($errStr . '('.$errorCode.') エラー発生, 行: ' . $errLine . ' ファイル: '. $errFile);
        });
    }
    
    public function tearDown()
    {
        restore_error_handler();
    }
```
Nodejsのコンテナを作成してみた。

## はじめに
`Nodejs`のバックエンドサーバーをコンテナで作成する際、ベースイメージを`nodejs(node:10)` にしてDockerfileを作成したが、コンテナにアタッチができず、中の構成などを直接見る方法が分からなかった。
困ったときには`nodejs`の設定などを見れるようにしたかったので、試しに`Ubuntu`ベース`にnodejs`をインストールする方法でDockerfileを作成してみた。
（記事の内容は、試しに動かすところまで記載あり。）

## 実行環境（前提）
【Docker導入環境】
　　・Ubuntu 20.04 LTS（GCP上）
　　・docker 19.03.13

## 今回やった事のメモ

今回の作業のためにテストフォルダを作成して、そこに移動。

```shell:
$ sudo mkdir ./test_container
$ cd ./test_container
```

**Dockerfileの作成**

```
$ sudo nano ./Dockerfile
```

```docker:Dockerfile
# ベースイメージ
FROM ubuntu:20.04

# 必要パッケージのインストール
RUN apt update
RUN apt install -y tzdata
RUN apt install -y \
  nodejs \
  npm

# Nodejs関連のパッケージインストール
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY ./package.json ./
RUN npm install

# index.jsファイルの設置
COPY ./index.js ./

# ポート開放
EXPOSE 8080

CMD ["node", "index.js"]
```

**nodejsモジュールをインストールするための`package.json`を作成**
 ※上記の`npm install`のタイミングで、`package.json`の情報を元にインストールが行われる。

```
$ sudo nano ./package.json
```

```json:package.json
{
  "name": "test",
  "description": "test",
  "version": "0.0.1",
  "main": "index.js",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "10"
  },

  "dependencies": {
    "express": "^4.17.1",
    "moment-timezone": "^0.5.31",
    "body-parser": "^1.19.0"
  }
}
```

**`index.js`を作成**

```
$ sudo nano ./index.js
```

```js:index.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 8080;


app.use(bodyParser.urlencoded({ extended: true }));

app.get('/get_test1', function(req, res) {
  res.send('GET1パラメータ取得: ' + req.query.get1)
});

app.get('/get_test2', function(req, res) {
  res.send('GET2パラメータ取得: ' + req.query.get2)
});

app.post('/post_test1', function(req, res) {
  res.send('POST-URLへの送信です。')
});

app.post('/post_test2', function(req, res) {
  res.send('POSTパラメータ取得: ' + req.body.data1)
});

app.listen(port)
```




**ビルド実行**

```shell:
$ docker image build -t test_container:v1 ./
```

**コンテナ作成 ＆ 起動**

```shell:
$ docker container run -it -d -p 80:8080 --name con1 test_container:v1
```

## 動作確認

**GETの動きを確認**
以下に接続して、上記で設定したレスポンスが返ってくればOK。

　`外部IP/get_test1?get1=10`
　`外部IP/get_test2?get2=20`


**POSTの動きを確認**
ローカル環境（自分のPC）に適当な`html`ファイルを作成して、それをブラウザで開きアクセス。

```html:post_test1.html（POST送信のテスト用）
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>POSTテスト用</title>
</head>

<body>

<form action="http://[外部IP]/post_tset" method="post">
  <input type="hidden" name="data1" value="aiueo">
  <input type="submit" value="送信">
</form>

</body>
</html>
```

```html:post_test2.html（POSTパラメータ取得の確認用）
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>POSTテスト用</title>
</head>

<body>

<form action="http://[外部IP]/post_tset2" method="post">
  <input type="hidden" name="data1" value="aiueo">
  <input type="submit" value="送信">
</form>

</body>
</html>
```


## おまけ
はじめの背景の部分で 『コンテナにアタッチができず、中の構成などを直接見る方法が分からなかった』 と書いたが、コンテナ作成時のシェルコマンドで **[CMD]** のコマンドを `/bin/bash` で上書きできていなかっただけだった・・・
Linuxのディレクトリ構成の確認

## 勉強前イメージ

独自でインストールするときとかどこにディレクトリほったらいいかわからなくて....
なんとなくイメージでやってたけど確認する

## 調査

今回はCentOS7のroot配下のディレクトリを取ってきました。

### /

最上位のディレクトリ、つまりrootディレクトリ。

### /bin

システムを管理する上で基本コマンドが入っている。
cd, cp, mvなどOSをインストールしたときの標準コマンドのプログラムが入っている

### /boot

ブートに必要なファイルが入っている。
カーネルイメージとかが保存されている

### /dev

デバイスのファイルが入っているディレクトリ。
sdaやcdromなど

### /etc

ほとんどの設定ファイルが置かれているディレクトリ
httpdとかインストールしたら `/etc/httpd` に設定ファイルが置かれたりする

### /home

一般ユーザのホームディレクトリ。
ユーザが追加されるごとに/home配下にディレクトリが作成される

### /lib

システムを起動するのに必要なドライバモジュールやライブラリファイル用のディレクトリになります

### /lib64

64bitの場合は/libでなくこちらのディレクトリが使用される。
一覧を作ったサーバは64bitだったので、libはみずにlib64を見ているってことかな。

### /media

CD-ROMやフロッピーディスクなどの外付けメディア用のディレクトリ。

### /mnt

一時的なファイルシステムのマウントに使うディレクトリ

### /opt

rpmなどパッケージ管理システムのインストール先。

### /proc

プロセスのIDの付いたディレクトリが置かれています
各プロセス名によってディレクトリが存在し、現在実行されているプロセスに対する情報とデータが含まれている

### /root

rootユーザーのホームディレクトリ

### /run

実行中のプログラムのPIDファイルを格納するときによく使われるディレクトリです
.pidというファイルを使って重複して起動しないために使われることが多い
再起動の際にデータが削除されます

### /sbin

システム管理者用のコマンド(再起動やファイルシステムの操作等)が置かれている
新しいコマンドの追加や変更は推奨されていません

### /srv

ftp、wwwなどで利用するユーザー用のスペース

### /sys

動作しているプロセスやドライバのうち、ドライバ関連がまとめられている

### /tmp

テンポラリデータの保存場所。

### /usr

各ユーザーが共通して利用するライブラリのデータを保存する場所
ソースからコンパイルしたアプリケーションなどはこちらにインストールされる。
/etcに保存されている設定のシンボリックリンクが貼られることがある

### /var

ログやキャッシュなどのデータが保存されている

## 勉強後イメージ

/etcに設定ファイルが置かれないようなミドルウェアってあるけどあれの違いってなんなのかな...
あと/mediaにCD-ROMのディレクトリあるけど、/dev/cdromとどう違うんだろう...
いろいろ疑問が出てきたのでまた今度確認する。
今回はライブラリとかどこ置いたらいいんやろ...って思うことが多いから、どういう役割をしてるのかでも知れたので良かったと思います

## 参考

- [ディレクトリ構造](https://bi.biopapyrus.jp/os/linux/linux-directory.html)
- [Linuxの基本の基本。Linuxの基本的なディレクトリ構成](https://oxynotes.com/?p=5987#1)
- [Linuxディレクトリ構造](https://qiita.com/nys9302/items/a8ddeedd3cd9d0deb332)
- [Linuxのディレクトリ構造の一覧](https://gigazine.net/news/20080104_linux_directory_structure/)
- [UNIX系OSのディレクトリ構造表(CentOS 7.3)](https://qiita.com/uhooi/items/c1388f8d803641bfb8f3)
この素晴らしいプログラミングに文脈を！

突然だが私はコンテキストが好きだ。
先ず言葉の響きがいい。そして謎めいているからだ。

コンテキストって何だ？という疑問からこの記事を書くことにしたが
筆者の考えや経験に基づいた内容が多く、正確ではないのであまり真に受けないで頂きたい。

# コンテキストの効果

コンテキスト（Context）とは、文脈を表す単語である。

例えばここに

```
パンを食べた
```

という文がある。

コンテキストは`○を○した`である。
この文からは`パン`と`食べた`という2つの情報を得ることができる。

では次に

```
私は朝食にパンを食べた
```

コンテキストは`○は○に○を○した`である。

後者の方がより詳細な情報と認識できるのはコンテキストの影響である。
このようにコンテキストを変えることで文章の情報量や意味を変えることができる。

また、後者のコンテキストが前者のそれを内包しているように、コンテキストはコンテキストを内包する。この記事もそうだが、大きな1つの文章はいくつものコンテキストによって形成されている。誤解を恐れずに書けば、最小のコンテキストは`5W1H`の一部であり、最大のコンテキストは物語そのものである。

さて、前置きが長くなってしまったが
プログラミングは言語によって処理の流れを文として記述していく。
したがってプログラムの中にも多くのコンテキストが含まれているのだが
意識して読むと書き手によって様々なコンテキストが形成されていて大変興味深いものである。

# プログラミングにおけるコンテキスト

筆者がプログラミングにおいて初めてコンテキスト（Context）というワードを目にしたのは
Windowsのスケルトンプログラムのサンプルコードである。

スケルトンプログラムとは、エントリポイント（WinMain）とウインドウプロシージャだけからなる非常に質素なプログラムのことである。

ウインドウに何かを描出したい時には、ウインドウプロシージャで`WM_PAINT`メッセージを受け取り、円や四角形などの図形を出力することができる。描出を開始する前に[BeginPaint](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-beginpaint)を呼び出し、描出を終了した後に[EndPaint](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-endpaint)を呼び出すというルールが定められている。
この`BeginPaint`の戻り値の型が`HDC`、即ちディスプレイのデバイスコンテキスト（のハンドル）である。

例えばウインドウに図形を描出したいときは以下のように書く。

```c++
if (auto hdc = BeginPaint(...)) {
  Ellipse(hdc, ...); // 楕円を描出する
  Rectangle(hdc, ...); // 次に矩形を描出する
  EndPaint(...);
}
```

このように、デバイスコンテキストに描出したい図形の形状や順番を保存していくことで
流れが形成されていくのである。

しかし、プログラミングにおけるコンテキストは流れを形成するだけではなく、流れを制限する役割もある。

例えば、新しく星型の図形を描出する機能を作りたいとする。
以下のような関数を定義して直線を駆使することで実現したとする。

```cpp
void Star(...) {
  Line(...);
  Line(...);
  ... // 以下省略
}
```

さて、`Star`関数は`BeginPaint`と`EndPaint`の間で呼ばれなければならないが
そのような制限を関数に付与するにはどうすれば良いだろうか？

実は`hdc`が存在しているということが`BeginPaint`と`EndPaint`の間であるということを暗に保証しているのである。
つまり`hdc`を引数に受け取るようにするだけで呼び出しを制限できるというわけである。

```cpp
void Star(HDC& hdc, ...) {
  Line(hdc, ...);
  Line(hdc, ...);
  ... // 以下省略
}
```

これこそがプログラミングにおけるコンテキストの役割である。
まさにSNSなどで稀に目にする「この流れ（コンテキスト）なら言える」というやつである。

# コンテキストの使用例

前述の通りコンテキストには、文脈の構成要素を定義することと、処理の流れを制限する機能がある。

私が最もよく利用するのは単に文脈のパラメータとして使うケースである。

```cpp
struct DrawParams
{
  int pos_x; // 座標
  int pos_y;
  int color; // 色
};
```

このような構造体は図形を描出する流れでは頻繁に見かけるだろう。
これは単なる構造体に見えるが
上記の構造体を使うコンテキストでは座標と色が構成要素であることがわかる。

もう一つは処理の識別子として使うケースである。

例えば以下のようなマルチスレッドなプログラムで効果を発揮する。

```cpp
class ContextA
{
  ContextA() = default; // privateなコンストラクタ
public:
  static void thread_entry() {
    ContextA ctx;
    // ここはスレッドaのコンテキスト
  }
};

class ContextB
{
  ContextB() = default;
public:
  static void thread_entry() {
    ContextB ctx;
    // ここはスレッドbのコンテキスト
  }
};

void main() {
  // ここはメインのコンテキスト

  std::thread a{ContextA::thread_entry}; // スレッドa

  std::thread b{ContextB::thread_entry}; // スレッドb
}
```

この時`ContextA`の参照を関数の引数に取ることで呼び出し元をスレッドaだけに制限する。

```cpp
void call_from_a(ContextA&) {
  // この関数はスレッドaから呼ばれている
}
```

もちろん`ContextA`にパラメータを持たせることもできるし、複数のコンテキストで関数をオーバーロードしたりもできる。

以上
VScode で一括置換　大文字小文字を区別[備忘録]

# やりたいこと
VSCode上での現在開いているファイル内で一括置換を行う
大文字と小文字を区別する

#　方法
VScodeのメニューバーから　[Edit] => [Replace in Files]をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253261/0550f3b3-a6d0-1866-728e-81e66ce18dab.png)

そうするとVScode上で虫眼鏡アイコンから２つのバー（[検索する文字列] ボックスと[置換後文字列]のボックス）が出るのでそこで置換したい文字列、置換後の文字列を入力する

defaultだと大文字と小文字は区別しないので、下図の　Aa　　のアイコンをクリックすると区別して置換される（検索時も同様）　

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253261/911b941e-23c8-ae4d-2d3d-6d4949c8626d.png)

ちなみにその横のアイコンで単語単位での検索、さらによこのアイコンで正規表現などを使った検索が可能になる

# ショートカット
僕の環境だと置換のボックスを出すショートカットは　shift + option + H でした。

Preferance => Keyboard Shortcut　から変更可

# 参考
https://docs.microsoft.com/ja-jp/visualstudio/ide/replace-in-files?view=vs-2019
VScode document
MacでHomebrewをインストールしてみた！

#はじめに
HomebrewはMacやLinuxにおける主要なパッケージ管理システムです。
今回は、インストール手順を簡潔に確認してみたいと思います。

参考記事
<https://awesome-linus.com/2019/08/17/mac-homebrew-install/>

#1.Xcodeのインストール
ターミナルを開いて、以下のコマンドを実行します。

```
$ xcode-select --install
```
確認が入りますが、Enterでインストール開始！
少し時間がかかります。。

#2.Homebrewのインストール
1.が完了したら、引き続きターミナルで以下のコマンドを実行します。

```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

公式サイトにインストールコマンドが記載されています。
<https://brew.sh/index_ja.html>

##You have not agreed to the Xcode license agreements ... と表示された場合
Xcodeのライセンス規約に同意していません。とのことなので、規約に同意していきます。

Xcodeを開いて、agreeを選択し、
再度、前述のHomebrewインストールのコマンドを実行するとインストールされます。

#3.インストール完了を確認
ターミナルにて、以下のコマンドを実行します。

```
$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!
```

```
$ brew --version
Homebrew 2.5.11
```

バージョンが出力されました。
今回はこれで終わります。

#まとめ
今回は、Homebrewインストール手順を確認しました。
参考にさせて頂いた記事の作成者様ありがとうございました。

次は、brew installで、Python/Djangoを触っていきたいと思います。
初学者の方、一緒に頑張っていきましょう！
Windows10のPCにVueCLIを使用してVue.jsの環境を構築



〈下準備〉
Node.js、IDE環境（今回はVSCode)のインストールを行います。

①Node.jsのインストール

下記のURLよりNode.jsのインストールを行います。
https://nodejs.org/ja/

※VueCLSを使用するにはNode.jsのバージョンは8.9以上がインストールされている必要があります。
すでにNode.jsはインストールされているがバージョンが8.9未満の場合はインストールを行いましょう。

【確認方法】
コマンドプロンプトで「node --version」のコマンドを実行

![Nodejsバージョン確認コマンド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865589/d3518af4-ec01-83e8-a275-34b0cd2b206d.png)

実行すると画像のようにバージョンが表示される


②VSCodeのインストール

下記のURLよりインストールを行います。
https://azure.microsoft.com/ja-jp/products/visual-studio-code/

/*-----------------------------------------------------------------------------------------------------*/

ここからが実際のインストールです。
ここからのコマンドはVSCode上のターミナルからすべて実行します。

【ターミナルの表示方法】
VSCodeメニューの「View」→「Terminal」と選択してください。

①Vue CLIのnpmパッケージをインストール

次のコマンドを実行します。
npm install -g @vue/cli

処理が走るので、一通り終わったところで下記のコマンドを実行します。
vue -V

コマンド実行後にバージョン情報が表示されたらインストールは成功です。



次はプロジェクトの作成方法について書きます。
KubebuilderでCore API GroupのSecretsにアクセスするRBACのRoleを生成するMarkerを指定する方法

```go
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch
```

verbsには、`get`, `list`, `watch` の3つを指定する必要がある。

## 生成されるYAML

```yaml:config/rbac/role.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: manager-role
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - list
  - watch
```
PHP: Google Cloud Translation API の使い方 (Basic)

```php:basic01.php
#! /usr/bin/php
<?php
// ------------------------------------------------------------------
//
//	basic01.php
//
//						Nov/26/2020
//
// ------------------------------------------------------------------
require 'vendor/autoload.php';
use Google\Cloud\Translate\TranslateClient;

$text = 'Es war einmal ein kleines Mädchen.';
$targetLanguage = 'ja';

$translate = new TranslateClient();
$result = $translate->translate($text, [
    'target' => $targetLanguage,
]);
print("Source language: $result[source]\n");
print("Translation: $result[text]\n");
// ------------------------------------------------------------------
?>
```

実行方法

```bash
export GOOGLE_APPLICATION_CREDENTIALS=./***.json
#
./basic01.php
```

実行結果

```text
Source language: de
Translation: 昔々、小さな女の子がいました。
```
GraphQL PlayGround画面が何も表示されなくなった時の対処法

##実行環境
apollo-server: 2.19.0
apollo-server-express: 2.19.0
graphql: 15.4.0
ブラウザ・・GoogleChrome

##事象
ApolloServerを用いてサーバーサイドのGraphQLの実装を行っていた所、突然画面に何も表示されなくなる事象が発生。
ローカルサーバーを何度立ち上げ直しても同様に画面には何も表示されないままになりました。（GraphQLPlaygroundの黒っぽい背景は残ったまま）
デベロッパーツールを開きコンソールを確認すると「**Uncaught Error: Mode graphql failed to advance stream.**」のメッセージを確認。

##対処方法
↓はGatsbyJS+Graphiqlで発生した事象のようですが、こちらにあった解決法で解消したので手順を紹介します。
https://github.com/graphql/graphiql/issues/735

１・デベロッパーツールを開く
　　　　　　↓
２・「Apllication」タブ内の「LocalStorage」を開き、自分のローカルのアドレス（localhost:4000とか）の箇所を開く
　　　　　　↓
３・ Clear(🚫)のボタンをクリック
　　　　　　↓
４・（GoogleChromeを使用している場合）右上のタブをクリック→その他のツール→タスクマネージャを開く
　　　　　　↓
５・GraphQLPlaygroundに関連するタブのプロセスをすべて終了
　　　　　　↓
６・ページをリロード

これで解消されました。

##原因
↑のサイトによると、GraphQLPlaygroundを操作している時に特定のキー入力で今回の事象が発生してしまうそうです。
もしキー入力によって画面に何も表示されなくても、上記の対処方法に沿っていけば解決できるはずです。
[Node.js][Deno] オブジェクト合成各種 ベンチマーク比較

先ほどの [クラスのベンチマーク](https://qiita.com/Yggdrasil-Leaves/items/62ee54ad98e2d6f758c1) の続編。
オブジェクト合成でより良い(というか速い)手法ないかなと悩みつつ。

# 実験環境

同じく

- Node.js 14.15.1
- Deno 1.5.4

# 実験設定

- 10要素で構成されるオブジェクト2組を1つのオブジェクトにまとめる
- 要素のうち5つは名前が重複しており、合成元を合成先に上書きする
- 合成先はプロトタイプではなく、独立したオブジェクトとして新規に生成される

という前提で

- 合成先オブジェクトを生成するファンクションと、合成元オブジェクトを用意
- 実行時間計測 
  - 合成先オブジェクト生成ファンクションだけの結果
  - 合成先オブジェクト生成ファンクション+各種アルゴリズムの結果
- 結果表示
  - 各種アルゴリズムの結果部分を抽出表示

という手順。

# オブジェクト合成アルゴリズム ここに集う

今回調査するアルゴリズムを御紹介いたしましょう。

<dl>
<dt>assign</dt><dd>Object.assign()</dd>
<dt>spread</dt><dd>スプレッド構文</dd>
<dt>for</dt><dd>for inループ</dd>
<dt>forEach</dt><dd>keys forEachコールバック</dd>
<dt>map</dt><dd>keys mapコールバック</dd>
<dt>entries</dt><dd>entries列挙ループ</dd>
<dt>lowtech1</dt><dd>(対照用) オブジェクト要素として個別に代入</dd>
<dt>lowtech2</dt><dd>(対照用) 連想配列要素として個別に代入</dd>
</dl>

多少のオーバーヘッドは仕方ないにしても、できるだけ対照用で挙げた方法に
近い(というか速い)アルゴリズムを採用したいという趣旨。

# いざ、実験

今回は記述がコンパクトなので、1ソースでまとめていけます

``` javascript:bench_obj_merge.js
function CreateParent(){return {a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9};}
var sub={f:10,g:11,h:12,i:13,j:14,k:15,l:16,m:17,n:18,o:19};

// 実行内容 
var funx={
	dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー 
	create:()=>CreateParent(),
	assign:()=>Object.assign(CreateParent(),sub),
	spread:()=>{return {...CreateParent(),...sub};},
	for:()=>{
		var t=CreateParent();
		for(var k in sub)t[k]=sub[k];
		return t;
	},
	forEach:()=>{
		var t=CreateParent();
		Object.keys(sub).forEach(k=>t[k]=sub[k]);
		return t;
	},
	map:()=>{
		var t=CreateParent();
		Object.keys(sub).map(k=>t[k]=sub[k]);
		return t;
	},
	entries:()=>{
		var t=CreateParent();
		for(var [k,v] of Object.entries(sub))t[k]=v;
		return t;
	},
	lowtech1:()=>{
		var t=CreateParent();
		t.f=sub.f;
		t.g=sub.g;
		t.h=sub.h;
		t.i=sub.i;
		t.j=sub.j;
		t.k=sub.k;
		t.l=sub.l;
		t.m=sub.m;
		t.n=sub.n;
		t.o=sub.o;
		return t;
	},
	lowtech2:()=>{
		var t=CreateParent();
		t['f']=sub['f'];
		t['g']=sub['g'];
		t['h']=sub['h'];
		t['i']=sub['i'];
		t['j']=sub['j'];
		t['k']=sub['k'];
		t['l']=sub['l'];
		t['m']=sub['m'];
		t['n']=sub['n'];
		t['o']=sub['o'];
		return t;
	},
};
// 計測結果を書き込むところ 
var rec={};

// 動作テスト 
//for(var k in funx)console.log([k,funx[k]()]);

// 繰り返し実行時間計測 
var loop=Array(1000000);
for(var k in funx){
	var f=funx[k];
	var bgn=new Date;
	for(var i of loop)f();
	var end=new Date;
	rec[k]=(end-bgn);
}

// 結果表示 
for(var k in rec)console.log(k+' :'+(rec[k]-rec.create));
```

ここで予想外の事態。当初は [前回](https://qiita.com/Yggdrasil-Leaves/items/62ee54ad98e2d6f758c1) と同じ1千万ループだったのですが、なかなか処理終わらなかったので一旦止めて桁減らしちゃいました。
それでループ回数が1桁減っているので、前回の結果と比べるときは御注意ください。

|  | Node.js | Deno |
|:--|--:|--:|
| assign | 255.7 | 265.4 |
| spread | 6906.3 | 6503.3 |
| for | 289.2 | 484.4 |
| forEach | 413.6 | 463.1 |
| map | 445.5 | 505.7 |
| entries | 417.3 | 428.4 |
| lowtech1 | 10.1 | 8 |
| lowtech2 | 10.3 | 8 |

まず目を疑ったのが、今(一部で)流行りのスプレッド構文、なんと1桁遅い。
記述が一番シンプルなだけに残念なところ。
で、とりあえず Object.assign() がベストというか幾分マシということで。
もっと素敵な手法ないものかしらん。
JavaScriptのデータ型について

##JavaScriptで扱うデータの種類について
JavaScriptでは8種類のデータ型が定義されています。
よく出てくるものの例として以下の物が挙げられます。

* Number(数値)
    * 3, 5, 18, 0 etc...
* String(文字列)
    * 'Hello', 'Good' etc...
* Boolean(真偽値)
    * true, false
* Object(オブジェクト)

またJavaScriptにはデータ型の種類を調べる`typeof`演算子というものが用意されています。

```javascript
console.log(typeof 5);
console.log(typeof 'Hellow');
console.log(typeof true);
```

##出力結果

```
number
string
boolean
```

##まとめ
まだ初学者のため使い所がいまいちピンと来ていませんが（そういう人僕の他にもいますか？）いつかあ〜typeof演算子ね〜ｳﾝﾁｸｳﾝﾁｸくらいになれたらいいな、、、、

##参考
[MDN web docs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_types)
googleフォームを使ってお問い合わせを実装した際につまったところ (2020.11) nameがない ディベロッパーツール使えない

googleフォームを使用してHTML内でカスタマイズしようとした際に、最新記事が見つからなかった
「開発ツール　使えない　googeフォーム　カスタマイズ」とかで検索したけどヒットしなかった
「googleフォーム カスタマイズ 2020」で検索すると仕様変更後のページがヒットした

#つまったところ
- chromeのディベロッパーツールが使用可にならない
→ソースを見るで確認した

- nameタグとentry.xxx記述が見当たらない
→2020.06に仕様が変更されたため、項目名（「お名前」「フリガナ」「電話番号」「ご質問等」など）で検索すると対象の記述が発見できた


#参考
https://www.gorilla-web.net/2020new_google_form_custmize/
Vue-CLI + Vuexでポケモンのパーティを構築するSPAをつくる。


## 概要

ポケモンのパーティを構築して遊べるSPAです。
VueでSPAを作りたいなーと思って作り始めた。
加えて、listの高速なフィルター機能も使いたい。
実装はGitHubPagesを使ってフロントエンドのみ。

[GitHub pokemon-zukan](https://github.com/gamin27/pokemon-zukan)

![readme.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378497/80b68345-5047-c3a8-d804-de2c83e19bd5.jpeg)


## 技術・ライブラリ
* Vue-CLI + webpack
* Vuex
* lodash
* moji
* axios
* ityped
* babel-polyfill
* reset-css
* progressbar.js

## つくったもの

### パーティをつくる

ポケモンずかんからポケモンをピックアップし、パーティを構築する。

<img alt="ずかん" width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378497/ef77508c-d3ea-9851-7b3f-3e878c826169.png">

パーティはVuexのstoreで管理し、てもち画面で確認できる。

<img alt="てもち" width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378497/9c12618b-74de-c85e-0b55-c22979c95d4b.png">

### ポケモンの詳細をみる

vue-routerを利用して詳細画面を生成。
ポケモンのステータスを確認できる。

<img alt="しょうさい" width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378497/6f90d88c-b738-07e8-5ca5-756033c2bbe3.png">


## 詳細設計

### ポケモンのステータス、画像

axiosで外部APIから取得。

* [PokeAPI](https://pokeapi.co)
* [pokemon.json](https://github.com/fanzeyi/pokemon.json)

### フォント

[pokemon-font](https://www.npmjs.com/package/pokemon-font)

### 検索機能

`computed()`でフィルター、ソートを実現。
早くてかっこいい。（全データを取得してるだけだから数が大きくなると問題だけど。）

#### ソート

* なまえ
* ばんごう

##### フィルター

* なまえ検索
* タイプ

どれもjsで操作しているだけなのでレスポンスが早い。
さらになまえ検索は日本語入力未決定前でも検索できるようにしている。早い。

```vue:vue

<input @input="handleInput"></input>
```

```js:js
    handleInput(event) {
      this.wd = event.target.value //未決定の値を取得
    },
```

タイプフィルターはチェックボックスから選ぶ。

<img alt="フィルター" width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378497/579fe6ff-6f30-5017-e3d5-bcf7d0f69d19.png">

## おわり

細かい実装やその方針などは省略していますが、素人ながらかなり手こずりました。
良い勉強になりました。
Ruby: Google Cloud Translation API の使い方 (Advanced)

```rb:advanced01.rb
#! /usr/bin/ruby
# -*- encoding: utf-8 -*-
#
#	advanced01.rb
#
#						Nov/26/2020
# ---------------------------------------------------------------------
require "google/cloud/translate"

project_id    = "project-translation"
text          = "Es war einmal ein kleines Mädchen."
target_language = "ja"
location_id = "global"

contents = [text]

client = Google::Cloud::Translate.translation_service

parent = client.location_path project: project_id, location: location_id

response = client.translate_text parent:               parent,
                                 contents:             contents,
                                 target_language_code: target_language

#
response.translations.each do |translation|
	puts "Translated text: #{translation.translated_text}"
end
#
# ---------------------------------------------------------------------
```

実行方法

```bash
export GOOGLE_APPLICATION_CREDENTIALS=./***.json
#
./advanced01.rb
```

実行結果

```text
Translated text: 昔々、小さな女の子がいました。
```
Spring Data Redis（Lettuce）のReadFrom.NEARESTはクラスタ構成でのみ有効な件

# 結論
Spring Data Redis（Lettuce）のReadFrom.NEARESTはクラスタ構成でのみ有効。
Master-Replica構成（Sentinel構成含む）では無効。

# 根拠
## 1. Lettuceのドキュメントで確認
> The latency of the nodes is determined upon the cluster topology refresh. If the topology view is never refreshed, values from the initial cluster nodes read are used.

https://lettuce.io/core/release/reference/index.html#readfrom.read-from-settings

## 2. Lettuceのソースコードで確認
コメントの通り、getNodesメソッドがレイテンシの少ない順にノードを返すのですが、これの呼び出し元がClusterScanSupportのみというところから判断しました。

```ReadFrom.java
    /**
     * Descriptor of nodes that are available for the current read operation.
     */
    public interface Nodes extends Iterable<RedisNodeDescription> {

        /**
         * Returns the list of nodes that are applicable for the read operation. The list is ordered by latency.
         *
         * @return the collection of nodes that are applicable for reading.
         *
         */
        List<RedisNodeDescription> getNodes();

    }
```


# そもそもの問題
RedisのMaster-Replica構成。

- 東京リージョン
    - az-a: Redis(master) + Application(Spring Boot + Spring Data Redis)
    - az-c: Redis(replica) + Application(Spring Boot + Spring Data Redis)
    - az-d: Redis(replica)

AZ間のネットワークレイテンシが大きいので、ApplicationからのRedisの読み取り処理については、同一AZ内のノードから出来るのが望ましい。
※書き込み処理については、masterに対してしか出来ないので、AZ間のネットワークレイテンシは諦めるしかない。

ApplicationにReadFrom.NEARESTを設定すれば、最寄りのRedisノードから読み取り処理してくれそうなのだが、実際は冒頭の結論の通りこれは期待通り動かない。

# 回避策
RedisStaticMasterReplicaConfigurationでコネクションをセットアップのうえ、ReadFromを下記のように設定する。SentinelConfigurationは不可。

- 東京リージョン
    - az-a: Redis(master) + Application(ReadFrom.MASTER_PREFERREDを指定。)
    - az-c: Redis(replica) + Application(ReadFrom.REPLICA_PREFERREDを指定。az-dにも行きそうだけど行かない。ノードの設定順に影響されるかも。)
    - az-d: Redis(replica)

これでRedisの読み取り処理については、AZ内に閉じることが出来る。

# その他対策
Pipelineを使って、複数コマンドを1回の通信に収めると良さそう（未検証）。

# お願い
Jedisや他言語のRedisライブラリがどうなのか教えて欲しいです。
JDLA Deep Learning for GENERAL 2020 #3 に合格したので、勉強方法のまとめと反省

# G検定概要について

> ディープラーニングの基礎知識を有し、適切な活用方針を決定して、事業活用する能力や知識を有しているかを検定する。

>受験資格	制限なし
実施概要	試験時間：120分
知識問題（多肢選択式・220問程度）
オンライン実施（自宅受験）
出題範囲	シラバスより出題
受験費用	一般：12,000円（税抜）
学生：5,000円（税抜）

>[一般社団法人日本ディープラーニング協会HP内より引用](https://www.jdla.org/certificate/general/)

- 問題数について
今回は191問でした。
G検定の申し込み後に届くメールに問題数が記載されていました。
<br>

- 開催日程に関して
現状年間3回（3月、7月、11月）行われているようです。
今回の個人申し込みは10月30日(金) 23:59締め切りで、11月7日(土) 13:00開催でした。
締め切りから開催まで1週間なので、締め切り直前まで勉強して、自信次第で受験をどうするか決めることが出来ます。
<br>

- G検定一番の特徴
オンライン実施で、検索可能なこと（G検定は、Google検定と揶揄される程です）
「検索いいの？！　カンニングし放題じゃん！」と思いますよね。
詳しくは[kusano_k’s blog / 日本ディープラーニング協会G検定でGoogle検索はありなのか？](https://kusano-k.hatenablog.com/entry/2020/11/19/232123)を読むのがいいと思います。

# 合格率について

> 2020年 第3回 G検定(ジェネラリスト検定) 結果発表
今回G検定の受験者数は7,250名。そのうち合格者数は4,318名で、合格率は59.56％でした。
[一般社団法人日本ディープラーニング協会HP内より引用](https://www.jdla.org/news/20201119001/)

引用ページに過去の合格率が記載されていますが、今回の
Google検定
G検定は合格率が低めでした。
検索が可能といえども、問題数が多く1問につき30秒程度で解かなければいけないので、勉強を疎かには出来ません。

# 学習教材
- 書籍
    - [深層学習教科書 ディープラーニング G検定（ジェネラリスト） 公式テキスト](https://www.amazon.co.jp/dp/B07H2ZR6M2/)(通称：白本)
    - [徹底攻略 ディープラーニングG検定 ジェネラリスト問題集](https://www.amazon.co.jp/dp/B07NDVCN99/)(通称：黒本)
    - [G検定 ～予測問題集～](https://www.amazon.co.jp/dp/B08BWP52Q2)
    - [G検定 ～最短合格指南書～](https://www.amazon.co.jp/dp/B07ZN2G8C1)
    - [詳解！実践で理解するG検定　Web模試　解説書](https://www.amazon.co.jp/dp/B07WWGJ716)(*1)

- Web模試試験
 - [DIVE INTO EXAM / G検定 模擬試験](https://exam.diveintocode.jp/exam)
 - [Study-AI / G検定模擬テスト](https://study-ai.com/generalist/)(*2)
 - [G検定 Web模試](https://deeplearning.sakura.ne.jp/)(*1)

- 動画
 - [JDLA G検定模試をひたすら解説する！](https://www.youtube.com/watch?v=9_5NqeW6gc4&t=252s)(*2)

(\*1)(\*2)はセットで使うと理解が深まると思います。

<br>

- セミナー(オンライン or オフライン参加)
 - G検定模擬試験 無料公開記念セミナー！対策本 著者伊達氏による受験の心得  
[これ1冊で最短合格 ディープラーニングG検定ジェネラリスト要点整理テキスト＆問題集](https://www.amazon.co.jp/gp/product/B085HNTY17/ref=dbs_a_def_rwt_bibl_vppi_i0)(通称：赤本)の著者である伊達貴徳さんの講演でした。  
赤本は、黒本ではカバー仕切れていない範囲の内容になっているそうです。  
ちなみに、今回の出題傾向は法制度や社会問題が多く出題される事を示唆されておられ、AI白書を読むのを勧めておられました。  

- G検定勉強のために役立った資格
 - Python 3 エンジニア認定データ分析試験  
 データ分析の勉強で得た知識（主に単語とその意味や使用用途、簡単な計算問題）が役立ちました。

# 勉強方法について
結論から言いますが、出題範囲をカバー仕切れていない勉強方法でした。
新しい問題集に手を出すたびに、少しずつ新しく知識を得ましたが、試験は初見問題が多かった印象でした。（特に法制度や社会問題。技術的な事も少し）

<br>

合格者の勉強方法などをぐぐると以下のことがわかりました。

- 白本だけでは合格できない
- 黒本はやっておくべき
- チートシートを作るのは有効

試験まで二週間ちょっとしかなったので、勉強方法は以下のようにしました。
1. 幅広い知識を問われるので、まずは白本をさらっと読んで、黒本で基礎知識を身に付ける
　その際、チートシートを作る（復習のため、覚える程でもないが試験に出る内容等）
　わからないところは、納得するまで詰める
2. 模擬試験をひたすら解いていく

【1. 基礎知識の身に付け】
白本１周（軽く読む）
↓
黒本２週（総仕上げ問題以外）
↓
白本1週（再度軽く読む）　←黒本を解いたおかげで内容の理解が深まりました
↓
DIVE INTO EXAM / G検定模擬試験の「カテゴリ事に学ぶ」を各章2~3週


【2. 模擬試験をひたすら解く】
黒本（総仕上げ問題） 2週
DIVE INTO EXAM / G検定模擬試験　１週
Study-AI / G検定模擬テスト & JDLA G検定模試をひたすら解説する！ １週
G検定 ～予測問題集～　１週
G検定 ～最短合格指南書～　１週
詳解！実践で理解するG検定　Web模試　１週

黒本1週目で7割、2週目で9割取れました。
DIVE INTO EXAMは9割取れました。
他の問題は、全て問題を解いてから答え合わせをするのがしんどかったので、一問一答で行いました。

# まとめ
チートシートが汚いので、作り直そうと思います。
合格はしましたが、初見問題が多い印象を受けたので、赤本やAI白書を読んでおけばよかったと思いました。
これからの検定ですし、多分今後も参考書や問題集など沢山発売されると思います。ですので、知識のアップデートという意味で、年1回とかAI等に関する法律が変わるタイミングで再受験をするにはいい検定だなと思いました。
Go開発環境構築(Mac)

# Go開発環境構築(Mac)
## 環境情報
||情報|
|---|---|
|OS| macOS High Sierra 10.13.6|
|Homebrew| 2.4.8|
|Go| 1.14.5 darwin/amd64|


## 目次
1. Golangのインストール
1. GOPATHの設定
1. 開発支援ツール

## Golangのインストール
[Go公式のインストーラ](https://xn--go-hh0g6u.com/doc/install) でも出来ますが、Macなのでパッケージ管理のHomebrewを使ってインストールしていきます。

①  インストール & 確認

``` sh
$ brew install go
$ go version
go version go1.14.5 darwin/amd64
```

## GOPATHの設定
外部パッケージのリソースなどが保存されるPATH情報。 実行バイナリもGOPATH内に配置されるため、PATHを通しておく。

① 現在のGOPATHを確認する。標準では`$HOME/go`が設定されている。

```sh
$ go env GOPATH
```
② GOPATHの向き先を再設定する。ターミナルで`export`を実行するだけだと、ターミナルを再起動した場合に設定した値が消えてしまうため、`~/.bashrc`に記載し、起動時に毎回実行されるように設定。

```sh:.bashrc
# 以下を追記する
export GOPATH=$(go env GOPATH)
export PATH=$PATH:$GOPATH:bin
```

③ `.bashrc`を読み込む。（ターミナル再起動でもOK）

```sh
$ source ~/.bashrc
```

## 開発支援用ツール
Goが標準で提供している機能は`go help`で確認可能。
標準での提供はしていないが、[公式に開発されている有用なコマンドラインツール](https://godoc.org/golang.org/x/tools/cmd)があるため、必用に応じてインストールする。

### gofmt
コードフォーマッターのコマンド。インデントや改行位置などを自動調整する。

```go
type Todo struct {
  ID uint64 `json:"id"`
  Title string `json:"title"`
  Finished *bool `json:"finished,default:18"`
  CreatedAt time.Time `json:"created_at"`
}
```

↓

```go
type Todo struct {
  ID        uint64    `json:"id"`
  Title     string    `json:"title"`
  Finished  *bool     `json:"finished,default:18"`
  CreatedAt time.Time `json:"created_at"`
}
```

### goimports
`gofmt`の上位互換のようなツール。コードフォーマッターの機能とプラスしてパッケージを読み込む`import`文の挿入と削除を自動的に行う。

※ Goは使用されていないパッケージがimport宣言されている場合にコンパイルエラーになる

### go vet
バグの原因となりそうなコードを検出するコマンド。

```sh
$ go vet main.go
$ go vet .
$ go vet ./...
```
### golint
Goらしくないコーディングスタイルに対して警告をするコマンド。

```sh
$ golint ./...
$ golint -set_exit_status ./...
```
※ `-set_exit_status`を指定することで、警告が出た場合にコマンドをエラー終了する。CIの実行などはこのオプションを指定するとよい。

### go doc（godoc）
ドキュメント閲覧用のコマンド。`godoc`の方がブラウザで確認できるため、ドキュメントが見やすい。
`go doc`・・・対象のパッケージのドキュメントが標準出力される。
`godoc`・・・GOMODに設定されている`.mod`ファイルをもとに、パッケージのドキュメントが*ブラウザで*確認可能。

```sh
$ go doc -all fmt
```

```sh
$ godoc
```

## 参考
[改訂2版 みんなのGo言語](https://www.amazon.co.jp/dp/B07VPSXF6N/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)
Ruby: Google Cloud Translation API の使い方 (Basic)

```rb:basic01.rb
#! /usr/bin/ruby
# -*- encoding: utf-8 -*-
#
#	basic01.rb
#
#						Nov/26/2020
# ---------------------------------------------------------------------
project_id    = "project-translation"
text          = "Es war einmal ein kleines Mädchen."
language_code = "ja"

require "google/cloud/translate"

translate   = Google::Cloud::Translate.translation_v2_service project_id: project_id
translation = translate.translate text, to: language_code

puts "Translated '#{text}' to '#{translation.text.inspect}'"
puts "Original language: #{translation.from} translated to: #{translation.to}"
#
# ---------------------------------------------------------------------
```

実行方法

```bash
export GOOGLE_APPLICATION_CREDENTIALS=./***.json
#
./basic01.rb
```

実行結果

```text
Translated 'Es war einmal ein kleines Mädchen.' to '"昔々、小さな女の子がいました。"'
Original language: de translated to: ja
```
React+Firebaseでゲストユーザーログイン機能を実装しよう！！


今回は、ボタンをクリックすると簡単にログインできる`ゲストログイン機能`を作成していきます。

ポートフォリオなどを作る際に、評価していただく方がわざわざformに入力する必要がないように、ゲストログインボタンは作っておいて損はないと思います。

それでは見ていきましょう😎

## 前提🙆‍♂️
- ReactとFirebaseでログイン機能を含むユーザー認証周りを作れるまたは既に完成している
- ゲストログインに関して大まかな実装の方法を知りたい場合
- 今回、firebaseの匿名認証は使いません

前提は上記の通りです。

FirebaseとReactでのログイン機能や会員登録機能についてわからないという場合は、以下のYouTube動画がおすすめです。

[React Authentication Crash Course With Firebase And Routing](https://www.youtube.com/watch?v=PKwu15ldZ7k&t=17s)

英語ではありますが、コードだけ見ていても多少は理解できると思います。

日本語解説については、`Firebase React 認証`などで検索すればいろいろ記事が出てきます。

## 実装🛠
さて、早速実装していきます。

と言っても、ログインの機能を既に作成済みであればとても簡単です。

考え方としては、**あらかじめgest userを作成しておき、ボタンをクリックしたときに自動的にそのguest userのemailとpasswordを使ってログインが行われるようにする**という感じです。

guest userのemailは`guest@example.com`、パスワードは`password`としてあらかじめアカウントを作成しておきます。

|ログインの手段  |違い  |
|---|---|
|普通のログイン  |formにemailとpasswordを入力し、その値を使ってfirebaseのsignInWithEmailAndPasswordでログインする| 
|ゲストログイン  |ボタンをクリックしたら、ゲストログインの関数を実行させ、signInWithEmailAndPasswordに変数ではなく、直接guest@example.comとpasswordを渡す|

コードで説明した方がわかりやすいかもしれませんねw

hooksなどの定義やimport含め長くなりすぎるので省略しています。

```Login.jsx
export default function Login() {

  const inputEmail = useCallback((event) => {
    setEmail(event.target.value)
  }, [setEmail]);
  
  const inputPassword = useCallback((event) => {
    setPassword(event.target.value)
  }, [setPassword]);
  
  //通常のログインの場合のログインボタンを押した後の処理
  const handleSubmit = async(e) {
    e.preventDefault()
    
    setError("")
    setLoading(true) 
    //login()の処理は別ファイルに記述しています。
    //loginではsignInWithEmailAndPasswordでログイン処理をしています。
    return login(email, password)
    .then(() => {
      history.push("/")
    })
    .catch((error) => {
      setError("failed!!")
    })
    .finally(() => {
        setLoading(false)
    });
  }

  //以下ゲストログイン用の処理(ほぼ通常のログインと同じです)
  const onClickGuestButton = () => {
    setError("")
    setLoading(true)
    //loginの処理で直接emailとpasswordをloginに渡しています。
    return login("guest@example.com", "password")
    .then(() => {
      history.push("/")
    })
    .catch((error) => {
      setError("failed!!")
    })
    .finally(() => {
        setLoading(false)
    });
  }
return(
  <>
  {/*ここにformのコードがあります*/}

　{/*ここからがゲストログインボタン。formの外に書いてます*/}
    <Button onClick={onClickGuestButton}>
      ゲストユーザーとしてログイン
    </Button>
  </>
)
```
以下は、loginの処理が書いてある`AuthContext.jsx`です

```AuthContext.jsx
  const login = (email, password) => {
    return auth.signInWithEmailAndPassword(email, password)
  }
```
[signInWithEmailAndPassword](https://firebase.google.com/docs/reference/js/firebase.auth.Auth#signinwithemailandpassword)はfirebaseのメソッドです。

これで実装完了です🤩

## 最後に🖊
いかがだったでしょうか？

ログイン機能を既に実装できているのであれば、ゲストログインボタンは結構簡単にできるものですよね。

作っておいて損はないです🙆‍♂️

もし、役に立ったと感じていただけたらLGTMお願いします!!🙏
俺でもわかるRedisの中身をMySQLにぶちこむ方法

俺です。
CacheStorageとしてよくつかうRedisですが、たまに中身をチェックしたい人生を迎えるときがあるとおもいます。

鼻ほじりながら `keys *` したら早いけど非力なRedis君は音を上げてしまうかもしれませんし、
RedisCommandだけで頑張るのがしんどいときはRDBMSにぶちこむと捗るよというメモをのこします。

# でもちょっとまってもしも俺がAthena玄人なら...

コネクタ使ったほうが楽そうです
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/athena-prebuilt-data-connectors-redis.html

以下人生を踏み倒したい俺向け

# Redis

## キー一覧を取得する

僕はkeys負荷死したくないので前者の手段を使います。

- ElatiCache RedisならSnapshotから複製して実行する
- ReadEndpointをアプリケーション側で参照してないならReadEndpointに実行でもいいかも


```
$ redis-cli -c -h hogefugare-dis.com --scan > keysfile
```

## キーに登録されているTypeを取得する

- typeごとにMySQLに作るテーブル列を洗い出します

 
```
$ redis-cli -c -h hogefugare-dis.com
redis-cli> type <キー一覧取得で取得したキーいくつか指定>
```

## Redisからキー指定でvalueを抽出する

- keyのtypeがhashだったときの例です。
- datetime型のvalueが含まれている場合はMySQLへ楽にLoadできるように取り出すタイミングで加工するのが楽ちんかと
- redis-cliは `--csv` オプションを使うとCSV形式で取得できるのが超よいですね
- hgetallだとtypeとvalue両方出力されるのでhmgetでtype指定で抜きだします。hgetallの結果を加工してvalueだけのcsvファイルを作るもアリだとおもいます
- 1行ずつvalueを抽出するのは本当に遅いので並列実行がおすすめ

```
REDIS_HOST=hogefugare-dis.com
cat keysfile|  while read KEY
do
	result=$(redis-cli -c -h ${REDIS_HOST} --csv  hmget ${KEY}  hoge fuga ..取得したいtypeを並べるのです..)
	echo "$result"
done > keydata.csv
```


# MySQL

## テーブル作成とデータロード

- typeにあわせてRDB側にテーブル作ります

```
CREATE TABLE hogehoge (
    hoge int(11),
    fuga varchar(768),
    bar json
)
```

- データロード

```
LOAD DATA LOCAL INFILE 'keydata.csv' INTO TABLE hogehoge FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';
```

データロード後煮るのも焼くのも好きにどうぞでレッツエンジョイ