高品質パソコン バッテリーを販売します, 全く新しい商品を保障できます, 人気バッテリーを購入すれば、30%の割引を楽しみます, 即日発送,すべてのノートPCバッテリーは1年の保障があるから、ご安心して購入してください。 denchiya.org バッテリー専門店!
Windows 10 Creators Updateで「Bash」がバージョンアップ【後編】
2017年04月18日 14:46WordPress REST APIからみでいろいろプログラミングネタを載せているが、さすがにここでそのまま扱うには場違いのような気がするので、まずNoSQLのMongoDBで遊んでみたい。
というのも、クライアントサイド(WebブラウザとJavaScript)だけで処理できるものであれば、わざわざ「Bash on Ubuntu on Windows」を使う必要がないからだ。
またMySQLなどSQLなら少し知っているが、NoSQLに関しては話は聞いているものの、使ったことがない方も多いのではないだろうか(実際、筆者もそうだった)。そこで「Bash on Ubuntu on Windows」をきっかけとして、NoSQLのMongoDBを触る機会になればと思った次第だ。
NoSQLのMongoDBをインストール
SQLはご存知のように関係データベース(RDB)へ問い合わせなどを行なう言語だ。RDBは複数のテーブルを持ち、それらを結合して利用することができる。加えて、スキーマと呼ばれる、データ定義や関連性を厳密に定義する。
(物凄く)大雑把なイメージとしては、Excelに複数の表を持ち、各々がLOOKUPしつつ、各カラムは、整数とかテキストとか日付とかが定義されている感じで、それをSQLで問い合わせる……という形になるだろうか。
対してNoSQLは、いくつかのタイプがあるのだが、基本的にRDBのスキーマに相当する部分がなく、XMLやJSONなどのデータ構造をそのまま扱える(ドキュメント型の場合)のが特徴だ。
MongoDBのデータ構造はキーと値のペアを要素としたシンプルなバイナリデータ配列で、検索速度を稼ぐためのインデックスも構築できる。
SQL(RDB)とMongoDB(NoSQL)の関係をざっくり書くと、
SQLとMongoDBのデータ構造の呼称の違い |
||||
---|---|---|---|---|
SQL |
db |
table |
row |
column |
MongoDB |
db |
collection |
document |
field |
こんな感じだろうか。SQLの分かる人であれば、MongoDBでどうなるのか見れば容易に見当が付くと思う。
SQLの構文例
SELECT * FROM posts WHERE id=1273; SELECT id,date FROM posts WHERE id=1273;
MongoDBの構文例
db.posts.find({id:1273}) db.posts.find({id:1273},{id:1,date:1})
上はどちらもpostsというtable or collectionからid=1273を探し、column or field全部、もしくはidとdateを表示する……となる。何となく相違点がお分かりいただけるだろうか。
MongoDBのインストールと確認は、
$ sudo apt-get install mongodb $ mongo -version MongoDB shell version: 2.6.10 $ mongod -version db version v2.6.10 2017-04-11T12:00:53.045+0900 git version: nogitversion 2017-04-11T12:00:53.046+0900 OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016
となる。ただ最新は3.4系なのでBash on Ubuntu on Windowsでインストールされるものは若干古いが、試すだけなら問題ないだろう。実際筆者が実験しているサイトも、ほぼ同じバージョンだ。起動と終了は、
$ sudo /etc/init.d/mongodb start $ sudo /etc/init.d/mongodb stop
これでOK。ただし、「Bash on Ubuntu on Windows」は、デーモンの自動起動などができず、また「Bash on Ubuntu on Windows」を終了すると、すべてのプロセスが落ちるところが、本物のUbuntuとの違いとなる(つまりBash起動時にMongoDBも毎回起動しなければならない)。
さて、実際MongoDBを操作するのに何かデータがないと面白くない。ちょうど筆者が別件で、WordPress REST APIで得たtags(BLOGのタグ情報でJSONフォーマット)の情報をサイトに置いてあるので、今回はそれを例として使うことにする。
Bashのローカルにそのファイルを保存して、MongoDBへインポートする方法は以下の通り。
$ curl https://blog.iwh12.jp/test/tags.json -o tags.json $ mongoimport --db test --collection tags --jsonArray --type json --file tags.json ※db: test、collection: tagsへtags.jsonからインポートする
curlコマンドもmongoimportコマンドもすでにインストールされているので、このまま操作できる。tags.jsonの中身は、more tags.json として確認すれば分かるが、[]{}:,を使ったデータの羅列で、「:」の左側がKey、右側がValueとなっており、「,」で区切られ並んでいるフォーマット=JSONとなる。
MongoDBにデータが入ったので、mongoDBのシェルを使って、実際にデータベースを操作してみよう。
まず、MongoDB内にあるデータベース名の確認。testが存在している。次にこれから操作するデータベースをuseで指定。以降、db: testでの操作となる。collectionは、system.indexesとtagsの2つ。タグのデータが入っているのは、tagsとなる。
$ mongo > show databases admin (empty) local 0.078GB test 0.078GB ※存在するdb名の一覧表示 > use test switched to db test ※db: testを選択 > show collections system.indexes tags ※db: testにcollectionが2つある。tagsが今回使うcollection
以下、検索などありがちなパターンを掲載したのでご覧いただきたい("> "はプロンプトなので入力する必要はない)。
> db.tags.find({}).count() 19 ※collection: tagsの件数 > db.tags.find({},{id:1,name:1,_id:0}) { "id" : 21, "name" : "2in1" } { "id" : 30, "name" : "Android" } { "id" : 14, "name" : "Audio" } { "id" : 13, "name" : "BABYMETAL" } { "id" : 12, "name" : "Blog" } { "id" : 20, "name" : "Desktop" } { "id" : 15, "name" : "Event" } { "id" : 27, "name" : "Hardware" } { "id" : 26, "name" : "Information" } { "id" : 28, "name" : "Music" } { "id" : 25, "name" : "Note" } { "id" : 9, "name" : "Nutube" } { "id" : 22, "name" : "OS" } { "id" : 8, "name" : "PC Watch" } { "id" : 18, "name" : "Photo" } { "id" : 23, "name" : "Program" } { "id" : 19, "name" : "Smartphone" } { "id" : 24, "name" : "Software" } { "id" : 16, "name" : "Windows" } ※collection: tags、全件のidとnameを表示。_idはMongoDBが自動的に追加するもので本体のデータとは無関係。非表示=_id:0とする(:1が表示) > db.tags.find({},{id:1,name:1,_id:0}).sort({id:-1}) { "id" : 30, "name" : "Android" } { "id" : 28, "name" : "Music" } { "id" : 27, "name" : "Hardware" } { "id" : 26, "name" : "Information" } { "id" : 25, "name" : "Note" } { "id" : 24, "name" : "Software" } { "id" : 23, "name" : "Program" } { "id" : 22, "name" : "OS" } { "id" : 21, "name" : "2in1" } { "id" : 20, "name" : "Desktop" } { "id" : 19, "name" : "Smartphone" } { "id" : 18, "name" : "Photo" } { "id" : 16, "name" : "Windows" } { "id" : 15, "name" : "Event" } { "id" : 14, "name" : "Audio" } { "id" : 13, "name" : "BABYMETAL" } { "id" : 12, "name" : "Blog" } { "id" : 9, "name" : "Nutube" } { "id" : 8, "name" : "PC Watch" } ※collection: tags、全件のidとnameをidで降順ソートして表示(昇順ソートは1) > db.tags.find({id:12},{id:1,name:1,_id:0}) { "id" : 12, "name" : "Blog" } ※collection: tags、id=12のidとnameを表示 > db.tags.find({count:{$gt:10}},{id:1,name:1,count:1,_id:0}) { "id" : 8, "count" : 13, "name" : "PC Watch" } { "id" : 23, "count" : 13, "name" : "Program" } { "id" : 16, "count" : 11, "name" : "Windows" } ※collection: tags、count>10 のidとname、countを表示(このcountは参照している記事の数) > db.dropDatabase() { "dropped" : "test", "ok" : 1 } ※db: testを削除。この記事の後半で使うので、この段階で実行しなくても大丈夫だ。
ちなみに、mongoDBのシェルから抜け出すのはexitコマンドだ。
いかがだろうか。ハッシュそのままなので、わりと分かりやすい操作系のように思う。例に挙げた比較だけでなく、正規表現を使ったり演算なども可能だ。MongoDBでWeb検索すれば、いろいろ情報が見つかるので、興味のある人は調べて試して欲しい。
Node.jsでMongoDBをコントロールする
さて、ここまではプログラミングで扱うデータの下準備。ここからが本番だ。
Node.jsは、聞き覚えのある方も多いと思うが、ザックリ説明すると、サーバーサイドでのJavaScript実行環境となる。
「え?Webブラウザでのクライアント環境でなく!?」と筆者も当初は思っていた。すでにPHPやPerl、Ruby、Pythonなどがあるので、今更JavaScriptをサーバーで動かす意味が分からない……と思っていた。
しかし実際使ってみると、すでに言語仕様は分かっているので、取り立てて勉強する必要もなく、デバッグもしやすく意外と使い勝手が良いことがわかった。まだ仕事としての実践経験はないものの、暇(で天気が悪い日)な時は適当にプログラミングして遊んでいる。
まず環境の構築から。MongoDBと同じように、apt-getでもパッケージをインストール可能だが、さすがにバージョンが古いので、今回はnodebrewと呼ばれている専用のマネージャを使いってみよう。インストールは簡単だ。
$ curl -L git.io/nodebrew | perl - setup
もしくは、
$ wget git.io/nodebrew $ perl nodebrew setup
でインストールできる。仕上げはエディタなどで.bashrcの最後に
export PATH=$HOME/.nodebrew/current/bin:$PATH
を追加(vi ~/.bashrcでファイルを編集できる)。一度Bashを終了して、再度Bashを起動するとパスが通るので(ほかにも方法はあるが)、
$ nodebrew ls-remote ※インストール可能なNode.jsのバージョン一覧が表示される。ここではv7.8.0を使用 $ nodebrew install-binary v7.8.0 $ nodebrew use v7.8.0 use v7.8.0 $ node -v v7.8.0
これで準備は完了。今回は関係ないが、Node.jsでバージョン依存するアプリを使う場合は、install-binaryで該当するバージョンをインストールして、useでそのバージョンを指定すると、バージョンをスイッチングできる。
これから実際プログラミングを始めるが、まず作業用のフォルダを作る。これはWindowsからもUbuntuからも扱えた方がいいので、/mnt/c以下のどこかにする。通常だと、/mnt/c/Users/[ユーザー名]/Documents下になるだろうか。
たとえば今回、Documents/pcwatchを作業フォルダにした場合、pcwatchフォルダを作ってカレントディレクトリを移動後、初期設定として、npmコマンドを実行する。いろいろ聞かれるが、取りあえず全て[Enter]で問題ない(最後はyを入力)。
$ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install --save` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. name: (pcwatch) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: license: (ISC) About to write to /mnt/c/Users/knish/Documents/work/pcwatch/package.json: { "name": "pcwatch", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this ok? (yes) y
ソースコードのファイル名はindex.jsとなり、これからここへいろいろプログラミングしていく。もちろん、この段階では作られないので、Windowsのエクスプローラーなのでファイルを新規作成して始めよう。
Cドライブの下なので、Windowsからも直接アクセスでき、秀丸やVisual Studio Codeなど好きなエディタで編集可能だ。
まず手始めに、index.jsへ
console.log('test');
これだけ書いて保存。Bashで
$ node index.js test
を実行し、testが表示されればNode.jsが正しく作動している証拠だ。
次に、MongoDBをNode.jsから扱うモジュールをインストールする。HTMLでJavaScriptのライブラリをいろいろ「script type="text/javascript" src="xxx"」とするのと同じ感じだ。
$ npm install mongodb --save
これでNode.jsでMongoDBが扱えるようになる。index.jsの内容は以下の通り。
var mongoClient = require('mongodb').MongoClient; var mongodb = 'mongodb://localhost:27017/test'; mongoClient.connect(mongodb, function(err, db) { db.collection('tags').find({}).toArray(function (err, tags) { console.log(tags); db.close(); process.exit(0); }); }); // error処理などは含まれていない
1行目は、先にnpmしたモジュールを使う宣言。2行目は、MongoDBのDefaultはlocalhostでPort 27017、db: testを指定、という意味となる(もちろんvarを使わず、ダイレクトに該当箇所へ入れれるが便宜上)。
node index.jsで実行すると、find({})なのでcollection: tagsの内容が全件表示されているはずだ。また、検索条件をcount > 10で、idで降順ソート、id/name/count表示は、このように先のmongoDB shellからの検索と同じとなる。
db.collection('tags').find({count:{$gt:10}},{id:1,name:1,count:1,_id:0}).sort({id:-1}).toArray(function (err, tags) {
いかがだろうか。割と簡単にNode.jsでMongoDBが扱えるのがお分かりいただけただろうか。
加えてJavaScriptなので、(特にLAMP経験者なら)これといって言語仕様を勉強する必要もなく、サラッとプログラミングできてしまうのが魅力的だ。
次にデータがないと面白くないので、はじめにツールを使いJSONをインポートしたが、もちろんNodo.jsだけでMongoDBへデータを読み込める。先では一旦ファイルをダウンロードしたが、今度は直接ネットからDBへセットしてみたい。
まずhttpプロトコルを扱うモジュールを先にインストールする。
$ npm install http --save
そして上記のコードはいったん破棄して、index.jsへ以下のコードを書き込む。
var mongoClient = require('mongodb').MongoClient; var mongodb = 'mongodb://localhost:27017/test'; mongoClient.connect(mongodb, function(err, db) { var http = require('http'); http.get('https://blog.iwh12.jp/test/tags.json', (json) => { var body = ''; json.setEncoding('utf8'); json.on('data', (chunk) => { body += chunk; }); json.on('end', () => { var d = JSON.parse(body); db.collection('tags').insertMany(d).then(function(err, r) { db.close(); process.exit(0); }); }); }); }); // error処理などは含まれていない。実行前にdb.dropDatabase()でdb: testの削除を忘れずに(追記となるため)
ここでのポイントは、httpを使ってajaxでtags.jsonを読み込んでいるところと、実際MongoDBへ書き込むinsertMany()となる。
ajaxを使ってファイルを読み込むのは、クライアントでもよくあるパターンなので特に説明の必要はないだろう。非同期で通信して、'end'の部分で読み込んだデータが扱えるようになる。
そして配列dへ値を入れ、いきなり全部insertMany()でMongoDBへ入れている。中身が整数とかテキストとかスキーマ定義は一切なしなのがNoSQLの所以。またinsert()というメソッドもあるのだが、この場合は、Document(row)単位になるので、件数分ループする必要があり、一発で全てInsertできるinsertMany()の方がシンプルとなる。
確認はmongoDB shellを使って件数や検索結果を見て、ツールでインポートした時と同じになっていればOK。
これで読み込みの部分と検索の部分を1本のコードにまとめれば動く……と思ってしまうが、実は単に繋げるだけではうまくいかない。というのも、MongoDBの作動が非同期だからだ。
同期の場合は、基本的にコードを書いた順番で作動するので分かりやすいが、非同期の場合は、「httpでtags.jsonの読み込みよろしく!」と丸投げして、その結果を待たず先に進んでしまう=検索のコードが動いてしまい、(データ量にもよるので今回に限っては動くかも知れないが)当然DBの中身は空っぽ(もしくは半端な状態)なので、うまく検索できなくなる。
これを解決するには、読み込みのコールバック関数の中、process.exit(0);の部分へ、検索のコードを入れるか、同期/非同期のコントロールができるasyncモジュールを使う必要がある(今回のケースでは1つの処理だけなので前者で十分)。
とはいえ、これ以上は本サイトから逸脱しそうなので、ここまでとしたい。以降、もし興味があれば、"Node.js MongoDB"などでWeb検索すれば無数の情報がある。筆者もそれで勉強した。
Anniversary Updateでは、LAMP環境を作ってphpMyAdminやWordPressを動かし、今回は、MongoDBとNode.jsを動かした。
筆者のブログでは、これらとNode.jsモジュールのExpress(主にルーティング)+EJS(テンプレートエンジン)を使い、複数のWordPressサイトを1つにまとめて表示する実験サイトも公開しているが、全てBashで開発した。
Windows 10標準の機能「Bash on Ubuntu on Windows」でこれだけのことができるのだから、そろそろmacOSでの同環境に頼る必要もないかも知れない(加えてmacOSではシステムにいろいろ混ざるが、Bashはいざとなったら簡単に環境全てリセットできる)。実に楽しい時代になったものだ。Microsoftに感謝!
タグ:
—————