いのでんの進捗

ゆっくりのんびり強くなっていきたいブログ。

【latex】overleaf(uplatex) でディレクトリ構造の図を書く

まえがき

latexでツリー図を書こうと思ったが、Overleafでは tree.sty が使えない1ので、代替packageを探した。 意外と記事がヒットしなかったので簡単にまとめる。

描画したい図

一般的な木構造に加え、ディレクトリ構造も描画できると嬉しい。

結論: forest package

個人的にはforest が最適だったのでこれを紹介する。ちなみに公式docでもお気持ち紹介されている2

www.ctan.org

Tikzをベースにしているので、オプション等は共通した文法である。例) grow'=0

描画可能な図(一例)

f:id:puyobyee18:20191224132545p:plainf:id:puyobyee18:20191224132550p:plain
tree & dirctories

書き方

上述の図のソースコードは以下の通り。

% main.tex
\documentclass[11pt, uplatex]{jsreport}
\usepackage[edges]{forest}

% 場合によっては以下も必要かもしれない。自分の環境では不要だった。
% \usepackage{tikz}
% \usetikzlibrary{trees}
% tree

\begin{forest}
 [VP, for tree={parent anchor=south, child anchor=north}
 [DP[John,tier=word]]
 [V’
 [V[sent,tier=word]]
 [DP[Mary,tier=word]]
 [DP[D[a,tier=word]][NP[letter,tier=word]]]
 ]
 ]
\end{forest}
% directory

\begin{forest}
 for tree={grow'=0,folder,draw}
 [/
  [home
   [saso
    [Download]
    [TeX]
   ]
   [alja]
   [joe]
  ]
  [usr
   [bin]
   [share]
  ]
 ]
\end{forest}
% 念のためlatexmkrc
$latex = 'uplatex';
$bibtex = 'pbibtex';
$dvipdf = 'dvipdfmx %O -o %D %S';
$makeindex = 'mendex %O -o %D %S';
$pdf_mode = 3; 

詳細は http://ftp.jaist.ac.jp/pub/CTAN/graphics/pgf/contrib/forest/forest-doc.pdf を参照されたい。

注意

上述のdirectory を描画する際、コードにインデント(スペース1つで良い)をつけないと以下のように、Package pgfkeys Error: I do not know the key '/tikz/grow' というエラーが発生した。latexのブロック定義ってインデントだっけ?

f:id:puyobyee18:20191224132549p:plain
Package pgfkeys Error: I do not know the key '/tikz/grow'


  1. overleaf で使えるパッケージのリスト(公式Doc) に含まれていない。

  2. Using the forest package to create trees in LaTeX - Overleaf, Online LaTeX Editor

【GitHub】草、生やしていますか?

はじめに

いの です。
とある事情で2月までデスマ状態なのですが、VTuberを見ながらどうにか精神を保っています。

GitHubのContributionについて

いわゆる「草」です。
最近までインターンもやっており、研究の進捗もgit管理しているのできっと大草原になっていることだろう…と思ったものの、

f:id:puyobyee18:20191011130650p:plain
gh_pre_contribution

砂漠地帯ですね…
さすがにもう少し進捗を産んでいるのでは?と思い、公式doc*1を見直してみました。

曰く、

  • The email address used for the commits is associated with your GitHub account.
  • The commits were made in a standalone repository, not a fork.
  • The commits were made:
    • In the repository's default branch (usually master)
    • In the gh-pages branch (for repositories with Project Pages sites)

このうち自分は1つ目の「メールアドレスの設定」を見逃していたようです。早速修正しましょう。

contributionに関連するメールアドレス

今回登場するメードアドレスには以下の2種類があります。

  • GitHubのアカウントに登録されているメアド
  • commitに紐付いているメアド

一つずつ見ていきましょう。

GitHub

これは GitHubに紐づく メアドです。
Github > Settings > Public profile > Public email」から適当なメールアドレスを選択します。自分の場合、そもそもメアドをpublicにしていなかったので、「Settings > Emails」内の Keep my email addresses private のチェックを外しました。

Git側

ローカル の設定も確認しましょう。
該当するディレクトリで git config user.email コマンドを打つことで確認できます。先程GitHub側で設定したメアドと一致しているか確認しましょう。
なお、 --global オプションをつけることでグローバルの設定を一括で変更できますが、どうやらcontributionに反映させるには各ディレクトリからpushしないといけないようです。

おわり

以上の操作が済んだらprofileページで確認してみます。きっとcontributionに草が生い茂っていることでしょう。

f:id:puyobyee18:20191011130643p:plain
gh_contributions

まぁまぁだな…

あとがき

先日はVsingerの花譜を紹介しましたが、今回紹介するのはにじさんじ所属の「健屋花那(すこやかな)」さんです。

youtu.be

デビューから数週間にもかかわらず、登録者数は8万人越え。見た目はもちろんのこと、滲み出る博識さも気に入っています。 まぁなにより可愛いんですけど。

なにかと魅力的なナース姿であることもあってか、ファンアートも充実しています(ハッシュタグ #いらすこや で検索!)。
けろりらさん作のアニメーション(https://twitter.com/kerorira1/status/1182243727219163136)もかなりの人気度。本人のことを知らない人でも思わず手を止めてしまうのではないでしょうか…

【Node.js】Webpack でバンドルしたファイル群を Heroku にデプロイしたときに環境変数を適切に使いたい

タイトルながい

あらすじ

Github Pages をはじめ、シンプルな HTML と JS で構成された web ページなら無料で公開できるサービスは五万とあります。

ただ今回自分の作ったものには API key が含まれていたため、Heroku にデプロイすることにしました。
Heroku なら設定も楽だし大丈夫っしょ~~なんて思ってたら 1 日溶けました。つらい。
つらいのでブログにまとめました。誰かの 1 日を守れたら幸いです。

構成

<project>
|_ app/
    |_ index.html
    |_ src/
        |_ index.html
        |_ index.js
    |_ dist/
        |_ bundle.js
|_ .env(git管理外)
|_ webpack.config.js
|_ package.json
|_ app.js

やりたいこと

node ./app.js でexpressを用いたサーバを立て、src/ 以下の index.htmlindex.js を読み込む静的ページを構成。
この js ファイル内で API key を使用するため、.env環境変数を入れておきます。
また index.js は他モジュールを読み込んでいるので babel で es5 へ変換しつつ webpack でバンドルさせます。

1. dotenv

ググったらいっぱい出てくるので説明は割愛。

環境変数.env ファイルで管理するときに用いられるモジュールとして有名なものですが、webpack でバンドルしてしまうとバンドル後の js ファイルに直接書き込まれてしまうっぽい。secure じゃないので却下。

2. dotenv-webpack

上記の問題を解決した(?)モジュールが dotenv-webpack
これは dotenvWebpack.DefinePlugin をラップしたもので、環境変数の呼び出し方はほとんど dotenv と相違ありません。

github.com

dotenv は js ファイル内で require('dotenv').config() と呼び出していましたが、dotenv-webpackwebpack.config.js 内に諸設定を書き込みます(以下参照)。

/* webpack.config.js*/
const Dotenv = require('dotenv-webpack');
module.exports = {
  // 中略
  plugins: [new Dotenv()],
  // 中略
};

これで OK。 あとは呼び出したいところで process.env.SAMPLE_API_KEY と書けば API key が使用できます。

2. 余談

ちなみに .env: でなく = なので注意。

# .env
SAMPLE_API_KEY="hogehoge"

3.Heroku へのデプロイ

Heroku で環境変数の管理をするなら heroku-config をインストールしましょう。楽です。説明は割愛。

ローカルでも動くしあとはdeployだけすればおっけー!なーんて思ってたら

f:id:puyobyee18:20190714120431p:plain

うーん

結論

ローカルなら動く、さらに言えば手元でバンドルしてからデプロイしたらAPIが正しく設定されるようのでHeroku側でwebpackを動かしたときに config vars が読み込めていないっぽい。

一行一行設定を変えつつデプロイして、を繰り返した結果、 webpack.config.js に以下の設定を加えることで解決した。

plugins: [
  new Dotenv({
    systemvars: true,
  }),
],

公式のREADME曰く、

load all the predefined 'process.env' variables which will trump anything local per dotenv specs.

ということらしいのだが、なぜこの設定で行けるようになるのかは謎。

とりあえずデプロイできてよかった。

私の webpack.config.js

諸事情によりレポジトリは公開できませんが、package.jsonwebpack.config.js だけ公開しておきます(一部改変)。参考にしてください。

/* package.json */
{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node ./app.js",
    "deploy": "git push heroku staging:master",
    "build": "npx webpack",
    "heroku-postbuild": "webpack -p"
  },
  "devDependencies": {
    "dotenv-webpack": "^1.7.0",
    "eslint": "^5.16.0",
    "eslint-config-prettier": "^4.3.0",
    "eslint-plugin-prettier": "^3.1.0",
    "prettier": "^1.17.1",
  },
  "dependencies": {
    "webpack": "^4.35.3",
    "webpack-cli": "^3.3.5",
    "@babel/core": "^7.5.4",
    "@babel/preset-env": "^7.5.4",
    "babel-loader": "^8.0.6",
    "@babel/polyfill": "^7.4.4",
    "express": "^4.17.1",
  }
}
/* webpack.config.js */
const path = require('path');
const Dotenv = require('dotenv-webpack');

module.exports = {
  // メインとなるJavaScriptファイル(エントリーポイント)
  // aync/await を使うには `@babel/polyfill` を以下のように設定する
  entry: ['@babel/polyfill', './app/src/index.js'],

  // ファイルの出力設定
  output: {
    //  出力ファイルのディレクトリ名
    path: path.resolve(__dirname, 'app/dist'),
    // 出力ファイル名
    filename: 'bundle.js',
  },

  node: {
    fs: 'empty',
  }, // これもよく分からないけどとりあえずemptyにしておけば動いた。
  mode: 'development',
  plugins: [
    new Dotenv({
      path: path.resolve(__dirname, './.env'), // いらないかも。
      systemvars: true,
    }),
  ],
  module: {
    rules: [
      {
        // 拡張子 .js の場合
        test: /\.js$/,
        use: [
          {
            // Babel を利用する
            loader: 'babel-loader',
            // Babel のオプションを指定する
            options: {
              presets: [
                // プリセットを指定することで、ES2019 を ES5 に変換
                '@babel/preset-env',
              ],
            },
          },
        ],
      },
    ],
  },
};

あとがき

最近、Vsinger の 花譜 ちゃんにめちゃくちゃハマっています。
15 歳と思えぬ力強い歌声、カンザキイオリさんの楽曲の魅力を持て余すことなく歌い切る表現力 ――――― 。

今後も、もっともっと活躍してほしいです。
8/1 の初の単独ライブ『不可解』、院試のせいで行けないので泣いてる。2nd LIVE に期待。

youtu.be

ラプラスかわいい

【Git】個人用備忘録

Git 備忘録

Git で知らなかったことまとめ。随時更新していきます

対象: なんとなく Git の仕組みが分かってきた人・add,commit,push,pull,checkout,branch は一応使える人

ToC

  • index, HEAD の違い
  • add のオプションまとめ
  • commit を削除する
  • commit をまとめる
  • 差分を見る
  • rebase 使い方
  • branch削除方法

☆index, HEAD の違い

要更新


☆add まとめ

$ git add ... の選択肢は以下の3つ。

  • file 名そのまま: ディレクトリを指定すればそれがステージングにあがる。 ex) $ git add ./app/models/clinic.rb
  • フォルダ名 フォルダ内を再帰的に add してくれる。 ex) $ git add ./app
  • まとめて追加 3 種類(⇒参考)

    • $ git add . バージョン管理されていないファイルは追加されない。 すなわち新たに追加されたもの変更があったものが対象。 Git ver.1 まではこうであったが、現在は追加変更削除いずれも追加される。
    • $ git add -u バージョン管理されているファイルをすべて追加。 すなわち変更があったもの削除されたものが対象。
    • $ git add -A[--all] 全てが追加される。

    $ git add .$ git add -Aの違い 前者はカレントディレクトリ下のファイルをすべて追加するが、後者はプロジェクトのどこからでも該当ファイルをステージングにあげるという違いが存在する。


☆commit の削除方法

  • commit を削除したことを残す $ git revert <commit>で削除可能。コンフリクトがおきたらがんばれ。 なお<commit>のところには commit ID を入力すればよい。

☆commit をまとめる方法

  1. $ git log --onelineでまとめたいコミットを確認。
$ git log --oneline

# 1a2b3c <commit msg3>
# 4d5e6f <commit msg2>
# 7g8h9i <commit msg1>
  1. $ git rebase -i HEAD~<n>により、HEAD から n 個の commit をまとめる。
  2. vim が展開するので、以下のように合成元をfixupに書き換える。:wqで保存。
pick 7g8h9i <commit msg1>
- pick 4d5e6f <commit msg2>
- pick 1a2b3c <commit msg3>
+ fixup 4d5e6f <commit msg2>
+ fixup 1a2b3c <commit msg3>

※やばいときは$ git rebase --abortで rebase を取りやめる。

参考


☆diff により差分を見る方法

git diffで差分を見れる。アレコレ書こうと思ったけど普通にこの Qiita が優秀だったので読みましょう。

参考 ⇒忘れやすい人のための git diff チートシート


☆rebase まとめ(含: stash, fetch)

git rebaseとは?

歴史のを改変するコマンド。以下の図が分かりやすい。

rebasemerge(則ちpull)のコミットログ比較(⇒参考)

rebaseとmergeの比較

分かりやすい。

やり方

ブランチ名は上記画像に則る。

  1. git fetch git fetch はリポジトリと結びついているorigin/masterが最新になる。(正確にはリモートのmasterブランチをもとに、ローカルにFETCH_HEADというブランチが構築され、それが最新状態になる。) 他人からの変更があった場合、こまめにやるのがよさそう。 ちなみにgit pullは、この fetch の作業が終わったのち作業ディレクトリに結び付く master を origin/master をもとに更新する。
  2. git rebase origin/develop featureブランチの"元"を上記図 D から引っ張るようにする。うまくいけば 3 へ。 以下うまくいかない場合
    • featureに commit が残っているとき この場合は、rebase をする前にgit stash saveを行う。この stash コマンドにより作業を一時退避させることができる。退避させたのち rebase を行い、git stash applyで適用する。以下便利なコマンド。
      • git stash list: stash の一覧を確認したい場合。
      • git stash apply <stash_name>: <stash_name>を適用する。コンフリクト起こしたときはがんばれ。
      • git stash drop <stash_name>: stash を削除するコマンド。適宜消すべし。
      • git stash pop <stash_name>: 適用と削除を一気に行うコマンド。自信がないうちは apply でいいよ。
    • conflict を起こしたとき Aborted! やらなにかしら吐いて rebase が一時中断される。 git statusで確認すると、コンフリクトを起こしているファイルはboth modifiedとなっているハズ。 適当なエディタ等でコンフリクトを solve したら、該当ファイルを add するのを忘れないように。 さらにそののちcommit はせずgit rebase --continueで rebase 処理を再開させる。この手順じゃないとうまくいかなかった。
    • よく分からなくなってしまったとき git rebase --aborted一択。rebase を実行する前の状態まで戻してくれるので、ヤバい!と思ったらこれを行う。
  3. git push --force origin feature あまりやりたくはないが push --force をやる。

一連の流れ

$ git fetch
$ git stash # プッシュしていないcommitがあったとき
$ git rebase origin/master
$ git status # コンフリクトがありそうなとき
$ git diff -- FILE_NAME # 差分を確認
$ git add FILE_NAME
$ git rebase --continue
$ git stash pop
$ git push --force origin mybranch

☆branch削除方法

ローカル

# コミット済み(HEADにmerge済?)の場合
git branch --delete my-branch
# 上記を問わない場合
git branch -D foo

リモート

# 一覧表示
git branch --remote
# 消し方は一通り
git push --delete origin my-branch

【mongoDB+Node.js】mongoDBの公式Docを読んで軽く触ってみた(つもりが思わぬ沼にハマった話)

はじめに

GraphQLの話をするって前回のブログで宣言したのにちょっと寄り道してmongoDBのお話です。ちょっと触ってみた感想としては、やはりNoSQLは新たに言語を取得する必要がない(今回私はNode.jsを使いました)ので学習コストが低く、それでいて多アクセスからの負荷耐性が強い(だった気がする。違うかも)とくれば人気が出るのも納得です。
今回は筆者のメモ書き程度の記事なので、いつも以上に正確さが疎かに、そしてそれ以上にです/ます調がテキトーになっています。間違い等ございましたらコメントにて指摘いただけると幸いです。

mongoDBのインストール

mongoDBの公式サイトからインストーラをダウンロードする。インストールの途中でInstall MongoD as a Service(公式Doc)と言われたが、Windows Serviceについて何も知らなかったのでとりあえずチェックを外してインストールした。

windowsの場合、ローカルで動かすにはまずデータベースのディレクトリを作る必要がある。

$ cd C:\
$ mkdir \data\db

これでおk。

次に今作ったディレクトリにデータベースのパスを通す(".\MongoDB\Server\4.0\bin"をPATHに追加したものとする)。

$ mongod --dbpath "C:\data\db"

ここで実行するのはmongod.exeであることに注意。よく調べてないけどmongodがデータべース自体を起動する実行ファイル、mongoはデータベースに接続するためのMongo Shellを起動するためのものっぽい。

これで設定は完了。mongodを起動すると、waiting for connections on port 27017(初期状態)と出てくるはずである。

mongoDBのデータ形式

mongoDBはデータをBSON(JSONのBinary形式)で記録している。
構造自体はJSONと同じ。

{
    field1: value1,
    field2: value2,
    ...
    filedN: valueN
}

filedはstring型。.$も一応使える。

valueは様々な型になる。ObjectId, Object, Date type, array of string, NumberLong typeなどなど。

データ構造

先ほど挙げた{ fieldN: valueN }のひとまとめを、documentという(たぶん)。そしてこれらの集合をcollectionと呼び、RDBでいうtableにあたる(たぶん)。 mongoDBはこの collection を多数集めたものでひとつのデータベースを構築している。

図示するとこんな感じ

db
 --- collection
        --- documents
        --- documents
        --- ...
 --- collection
        --- documents
        --- documents
        --- ...
 --- ...

db
 --- collection
 --- ...

mongoDB + Node.jsでCRUD

公式ドキュメントを読んだので簡単にまとめてみる。特に発展的なことは何もしていないです。

準備

npmでmongodbをインストールしておく

$ npm i -l mongodb

データベースへの接続の仕方は以下の通り。

const mongodb = require("mongodb")
const Client = mongodb.MongoClient

Client.connect("mongodb://127.0.0.1:27017/myDb", (err, db) => {
    // ここに以下のCRUDを書いていく
})

CREATE = Insert Documents

最も簡単なのはCollection.insertOne()を用いる方法。

awit db.collection('inventory').insertOne({
    item: 'canvas',
    qty: 100,
    tags: ['cotton'],
    size: {h: 28, w: 35.5, uom: 'cm'}
});

ちなみに戻り値はPromise型。また複数のdocumentsを挿入するCollection.insertMany()も存在する。

READ = Query Documents

documentsを読むAPIとしてはCollection.find()を用いることができる。
次の例1のように空のオブジェクトを渡すと、inventorycollectionにあるdocumentsすべてを呼び出すことができる。例2はANDを、例3はさらにORのクエリを用いている。

// example 1
const cursor = db.collection('inventory').find({})

// example 2
const cursor = db.collection('inventory').find({
    status: "A",
    qty: { $lt: 30 }
})

// example 3
const cursor = db.collection('inventory').find({
    status: "A",
    $or: [{ qrt: { $lt: 30 } }, { item: { $regex: '^p' } }]
})

ここで$ltとあるが、これはless thanを表す演算子$+αのかたちはクエリやアップデートの演算子によく見られる(詳細はこちら)。

ちなみにこのメソッドはSQLの次の文に対応しているっぽい。 ```sql

example 1

SELECT * FROM inventory

example 2

SELECT * FROM inventory WHERE status = "A" AND qty < 30

example 3

SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%" ) ``` SQLの文法は分からないがまぁそんな感じがする。

UPDATE = Update Documents

Collection.updateOne(filter, update[, options, callback])を使っていきます。insertOne()find()と異なり、第一引数にfliterを渡し、更新するdocumentsを選択します。そして第二引数に更新内容を書きます。
また、updateの文にはupdate operatorを使い、更新内容を記述します。

// 以下のようなinventorycollectionが存在するとする。
//  {
//    item: 'notebook',
//    qty: 50,
//    size: { h: 8.5, w: 11, uom: 'in' },
//    status: 'P'
//  },
//  {
//    item: 'paper',
//    qty: 100,
//    size: { h: 8.5, w: 11, uom: 'in' },
//    status: 'D'
//  },
//  {
//    item: 'planner',
//    qty: 75,
//    size: { h: 22.85, w: 30, uom: 'cm' },
//    status: 'D'
//  }

await db.collection("inventory").updateOne(
    { item: "paper" },
    {
        $set: { 'size.uom': 'cm', status: 'P'},
        $currentDate: {lastModified: true}
    }
)

ちなみにupdateOne()が用いられたとき、filterによって複数のdocumentsが抽出された場合は一番初めにfilterされたdocumentがupdateの対象となる。

もちろん複数のdocumentsを更新するupdateMany()もあり、また書き込みではなく置き換えを行うreplaceOne()も存在する。

※1 書き込みの操作はAtomicallyに行われるらしい。OSで勉強したことが活きてくるネ!

※2 _id fieldの書き換えは禁止されているらしい。

※3 optionにupsert: trueを加えておくと、filteringされたdocumentsがあればそれを更新し、見つからなければ新規に作成する。UPDATE + INSERTの造語(?)らしい。

DELETE = Delete Documents

URLはremoveなんですね笑 最後はDeleteです。Collection.deleteOne(filter[, options, callback])またはCollection.deleteMany(filter[, options, callback])を使えばおk。これはfilterを引数に渡してやることで、該当のdocumentsを削除するというもの。

「よっしゃ!サンプルプログラム動かすぞ!」→いのでん「エラーだと?貴様この野郎......」

サンプルコードが動かない事態にいのでんが激怒した理由がやばすぎる...ブログの読者も動揺を隠せない最悪な事態に一同驚愕!!!

はい、執筆現在深夜2時のテンションでお送りしておりますが、上記のとおりサンプルコードそのままではエラーを吐いて動きません。

そのコードがこちら。

// Example of a simple insertMany operation

var MongoClient = require('mongodb').MongoClient,
  test = require('assert');
MongoClient.connect('mongodb://localhost:27017/test', function(err, db) {
  // Get the collection
  var col = db.collection('insert_many');
  col.insertMany([{a:1}, {a:2}], function(err, r) {
    test.equal(null, err);
    test.equal(2, r.insertedCount);
    // Finish up test
    db.close();
  });
});

これを実行すると、以下のように怒られます。

C:\ (略) \node_modules\mongodb\lib\operations\mongo_client_ops.js:474 
     throw err;
      ^

TypeError: db.collection is not a function
    at insertDocs (C:\ (略) \test_mongodb.js:20:8)
    at Client.connect (C:\ (略) \test_mongodb.js:9:5)
    at result (C:\ (略) \node_modules\mongodb\lib\utils.js:414:17)
    at executeCallback (C:\ (略) \node_modules\mongodb\lib\utils.js:406:9)
    at err (C:\ (略) \node_modules\mongodb\lib\operations\mongo_client_ops.js:294:5)
    at connectCallback (C:\ (略) \node_modules\mongodb\lib\operations\mongo_client_ops.js:249:5)
    at process.nextTick (C:\ (略) \node_modules\mongodb\lib\operations\mongo_client_ops.js:471:7)
    at process._tickCallback (internal/process/next_tick.js:61:11)

どうやらdb.collection()が原因らしい。筆者はjavascriptが最もまともに書ける言語と自称している割に、Promiseを深く理解しきれていないのできっと自分の書き方が悪いんだろう...とはじめは思いましたが、このエラーはどう考えても変数dbが予想されるものと確実に異なります。諦めてgoogle大先生に聞いてみると、こちらの記事が見つかりました。以下その記事より引用。

qiita.com

さっきnpmインストールしていつも通り接続しようとしたら TypeError: db.collection is not a function になるので調べてみたら、3系から仕様が変わったみたいです。

MongoClient.connect now returns a Client instead of a DB
http://mongodb.github.io/node-mongodb-native/3.0/upgrade-migration/main/

今のversion、4.0なんですが、、、

ということで、MongoClient.connectの返り値が変更されていたようですね。
(以下のコードも先ほどの記事から引用させていただいております。)

MongoClient.connect(_url, (err, client) => {
  // callbackに渡されるオブジェクトが変わった
  // db名を明示的に指定してdbオブジェクトを取得する必要がある
  const db = client.db('heroku_xxxxxxxx');

  db.collection('foobar', (err, collection) => {
    collection.find().toArray((err, docs) => {
      :
      :

    });
  });
});

~3.0とcallbackに渡されるオブジェクトが変更され、dbではなくclientと同じものが返ってくるようになりました。それに伴いcallback関数の中で明示的にdb名を指定してあげる必要ができます。

ちなみにlocalで実行している場合、MongoClient.db()の引数にIPv4のかたちで渡すことはできないので"localhost"でおk。

こんな大事な仕様変更、ちゃんとサンプルコードにも反映させておいてくださいよ…

ちょっと引っかかったこと

上記の仕様変更をmongo shellと行き来しながら確認している間、いくつか不明点が生じた。

自分が行ったことを事細かに記しておく。

/* test_mongodb.js内 */
const db = client.db("localhpst")
// とtypoしてしまった。この時mongo shellでdbを見てみると
$ mongo
> show dbs
admin
config
localhpst # typoしたDBが存在
testDb
> db.localhpst.find({})
> --- # empty
> db.testDb.find({})
> --- # empty

次に

/* test_mongodb.js内 */
const db = client.db("localhost")
// typoを修正。この時
$ mongo
> show dbs
admin
config
localhost # 新たにlocalhostが追加されていた
localhpst
testDb
> db.localhost.find({})
--- # empty
> db.testDb.find({})
--- # empty!!!
# この時移動する前に`db`コマンドでどこにいたのか確認したらよかったと後悔。
> use testDb
> db.localhost.find({})
--- # empty
> db.testDb.find({})
{"_id" : ObjectId("..."), "a" : 3 } # テスト用にmongo shellからinsertしていたものを発見。個人的にはここで{ "a" : 4 }{ "a" : 5 }が出てくるもの思っていただけに混乱してしまった。
> use localhost
> db.localhost.find({})
--- # empty
> db.testDb.find({})
{"_id" : ObjectId("..."), "a" : 4 }
{"_id" : ObjectId("..."), "a" : 5 } # test_mongodb.js内でinsertしていたもの

(はてなではハイライトされていないかもしれません。読みづらくてごめんなさい。)

要はデータベース名コレクション名を区別できていなかったため、自分が意図した(結果的に勘違いしていたわけだが)コマンドで、documentsが表示されなかったのである。

どうやらMongoClient.db()は引数にとったものをUPSERTで受けとるようだ。このメソッドの引数や、mongo shellでのdbコマンドやshow dbsで出力されるものは総じてデータベース名であるが、検索で指定するのはコレクション名であり、指定のデータベース下にいることは確保されていなければならない。その意味ではmongoDB v3からの仕様変更は、データベース名を明記する必要性があり確かなメリットが存在するので、手間などではなく改良されたものであることが頷ける。


P.S.

軽い気持ちでmongoDBをインストールしあれこれイジっていたら、こんな時間(am3:34)になってしまった。進捗産めたのはいいけど、翌日に響くからみんなは早く寝ようね。睡眠大切。

Webアーキテクチャのお勉強(前半)

今更ながら「Webを支える技術 ~HTTP、URI、HTML、そしてREST~」を読んだので自分の理解のためにも軽くまとめてみました。
RESTfulとはどういうことか、RESTに代わると言われる最近HOTなGraphQLについても述べていくよ。
間違いありましたらぜひぜひコメントください。


RESTとは?

歴史

90年代のwebの急速な発展に伴い、HTTP,URI,HTMLの標準化を執り行う必要性が生じてきた。この問題を解決するためBerners-Leeが中心となって設立したのが W3C(World Wide Web Consortium) である。このような背景で、Roy Fieldingという人物がwebアーキテクチャを分析し、とりまとめを行ったのがRESTである。

Resourceとは

RESTについてまとめる前にResourceを軽く説明。
リソースとは、簡単には「Web上にあるありとあらゆる情報」のことを指す。このリソース一つ一つを識別するための名前こそが、URIとなっている。あるURIが示すリソースは一意に定まり、URIを指定すればコンピュータは期待のリソースへアクセスできる。
なお、URIが指すリソースは一意だが、リソースは複数のURIを持つことができる。これによりリソースにアクセスしやすくなる。

  1. アドレス可能性(Addressablity)

    アドレス可能性とは、URIが備える、リソースを簡単に指し示せる性質のことである。

  2. リソースの「表現」

    リソースとは「web上の情報」という曖昧な概念であったが、実際にはクライアント・サーバ間でデータのやり取りを行う。このデータのことを「リソースの表現」と呼ぶ。概念的であったリソースが表層に出てきたものを指す。もちろん、このデータの現れ方は何通りも存在しうる。例えば天気情報は、HTMLで表現されることもあれば、画像やテキスト形式にもなりうる。

  3. リソースの「状態」

    またリソースは「状態」が変わりうるものである。例えば「今日の天気」というリソースは、時間とともに変化してゆき、「晴れ」から「雨」、はたまた「曇り」へとその状態を変え得る。

RESTとは

RESTとは、Representational State Transferの略称。これは「HTTPとは、ハイパーテキストだけでなく、実際には『リソースの状態(Resource Dtate)』及び『リソースの表現(Representation)』を運んでいる」としたものである。

具体的にRESTの設計思想を列挙すると、以下のような特徴があげられる。

  1. クライアント/サーバ型 : ユーザインターフェースと、リクエストの処理を分離できる

  2. ステートレス : サーバがアプリケーションの状態を管理しないことで、実装が楽になる。これはReact.jsの設計思想とも似てるね(コンポネント/コンテナの関係性とか)

  3. キャッシュ : 通信を減らすことで、サーバの負荷を軽減。やりすぎると情報の信頼性が下がる。

  4. 統一インターフェース : インターフェースを統一する(HTTPのメソッドは8種類のみ)ことで、クライアントとサーバの実装がより独立性を高められる(=相手のことを意識しなくてよい)。

  5. 階層化システム : ロード(Load)バランサやプロキシを間に挟んで負荷分散を行う(これだけじゃないけど)。インターフェースが統一されているからこそできる。

  6. コードオンデマンド : プログラムコードをサーバからダウンロードし、クライアント側で実行すること。

なんだかどれも分散システムの授業でやったことと同じだった。コードオンデマンドもコードマイグレーションの一種と考えられる。
このように設計することで、リソース同士繋がりあったハイパーリンクが成り立ち、大規模分散システムのwebを構築できるのだ。


GraphQLとは?

...

ブログ執筆欲を強制的に向上させるため、近日中にブログを書くことを宣言します…。

【後期実験】「情報可視化とデータ解析」を終えて

まえがき

こんにちは、いのでんです。
3年後期になりぼちぼち忙しい日々を送っていたわけですが、ついに後期実験も終わりほぼ冬休みな状態です。やったね! とか言ってる間に試験期間になりました。つらい。
実験終了して1ヶ月くらい経ちますが、実験最後のタームで行った「情報可視化システム」について書いていこうと思います。次年度の参考になるかはわかりません。

「情報可視化システム」とは?

近年、世に溢れるデータの数は爆発的に増え、「ビッグデータ」や「Deep Learning」が巷で話題となっております。この実験では、そんな大量にある情報を集積し、より見やすく秩序立て、有意な知見を得られるようにするにはどうしたらよいか、を考える講義となっております。間違ってたら不可来そう。

2〜3人のチームに分かれ、実験10日分(週3回なのでおよそ3週間)の期間で1つ可視化システムを作り、最終日にプレゼンを行います。プレゼンの日は大手企業の方もいらしており、優秀者には特典があるかも?フツーにめっちゃ緊張しました。

何を作ったか

我々のチームは終電情報可視化システムの「しゅうでん!」を作りました。

  • 動機 : ふつうに便利そう
  • 目的 : ある出発駅に対し、任意の到着駅への時刻を色分けし、鉄道の利用状況や都市の発展との相関を見る
  • ターゲット : なにかと終電を利用することが多い大学生や社会人

成果物は以下のURLから見ることができます。

https://inox-ee.github.io/infovis18_shuden/

(データはライセンス的に怪しいので品川.csvのみ載せてあります。よって「品川」と検索した場合のみ色付けが開始されます。)

機能紹介

  1. 右上の検索ボタンから出発駅を指定すると色分けを開始します。
  2. 出発駅から各駅エリアまでの経路に対して終電まで1時間以上なら終電まで1時間以内なら(時間が無くなるほど濃くなる)、終電が終わってしまったならに塗り分けられる。
  3. デフォルトでは現在時刻を取得し、右下のスライドバーで±1時間の変化を見ることが可能。切り替えボタンを押すと、入力して指定することも可能。
  4. GPSボタンからは、現在地を取得し最寄駅を提案してくれる。
  5. レスポンシブデザインに考慮し、スマホからでも見やすいデザインに設計。

苦労した点

おそらく一番重労働となったのはデータ収集でしょう。(ライセンス的にやや怪しいですが)某路線検索サイトをスクレイピングし、出発時刻と到着時刻を取って来るだけ…のはずが、入手した駅一覧のデータと検索サイトの駅名とが一致しない・冬季限定運行の駅も含まれていた・同駅名が意外と多い などの問題が発生。さらにAzureが使えず手持ちのマシンでクローラーを回すことに…….
無事に関東2000駅のデータを取り終えたチームメンバーに感謝です。

また、当然ながら取得したデータの見せ方も悩みました。都市部と地方とで駅密度は大きく異なり、一目で分かるようどんな手法を取り入れるか……。最終的に、d3.jsにも標準機能として搭載されていたボロノイ図を用いました。これは二次元平面上では、複数個の点に対して各々の母点の二等分線で領域の境界を結んでいくというものです。これを用いることで地方は1駅あたりの領域を大きく、都市部では小さくとることができます。数学の雑学が思わぬところで役に立ちますね……

そのほかにも、可視化・インタラクションの7つの原則である

  • Select/Focus:マップ上にボロノイ図を描画する
  • Explore:スライドバーによる検索時間の変化
  • Reconfigure
  • Encode:色塗りによる時間表現
  • Abstract/Elaborate:ホバー時、ポップアップによる追加情報の表示
  • Filter
  • Connect

を意識し、作成しました。

反省点

正直に申し上げると、あまり有益な情報は得られませんでした。「〇〇線の終電はかなり遅い」だとか「東京の東部と西部で大きく異なる」と言ったような有意な差が見られず、凡そ結果と呼べるものが少なかったかもしれません。原因として考えられるのは以下の通り。

  • 実験結果とその原因への仮説検証の不十分さ
  • 用いたデータの単一性
  • 可視化表現の単一性

これらの反省点から導びけるのは、やはり「このデータを可視化して何が得られる(or何を得たい)のか」が明確ではなかったことにつきるでしょう。HCIの分野では、「何をテーマにしたか」を聞いただけで(その後の手法や結果がどうであれ)学会に論文が通るか否かがおおよそ目星がつくそうです。

またデータ解析とは別に、システムの応答時間の短縮も、ユーザビリティを考慮したときに非常に重要な因子になる[1]ことを感じました。
地図・路線・駅・ボロノイ図と複数のデータをレンダリングする際は、読み込みや描画の方法を十分に考慮しなければならない。たとえばGoogle Mapのように、地図をタイル分割し、範囲を限定して読み込み・レンダリングを行うといった工夫もあります。フロントエンドに興味がある自分にとって、一度勉強しなければならないなぁと思っています。

[1] Judy Chuan-Chuan Lin, & Hsipeng Lu (2000). Towards an understanding of the behavioural intention to use a web site. International Journal of Information Management, Volume 20, Issue 3, Pages 197-208

最後に

この実験は、短い期間でインタラクションの基礎を学び、実際にデータを収集し、システムを開発するところまで行くため、非常にハードな実験でした。多少なりともPythonJavaScriptの知識がないときついかもしれません(と言ってもJS初学者が自分よりも圧倒的に良いものを作ってしまうのが弊学科ですが…)。
ですが、担当教授が基礎からデータ可視化を指導していただけることや、オリジナルのwebアプリ(?)を開発できるので、得られるものはとても大きかったなと感じています。来年も開講されたら、是非挑戦されてみいかがでしょうか。