読者です 読者をやめる 読者になる 読者になる

express-passportを触ってみる手順書

express node.js express-passport

expressを導入し、passportをためしに触ってみようと思いましたが、案の定初心者の私はハマりましたので、最低限の動作確認ができる手順書をメモとして残します。
ログインボタンを押下したら、passportで処理が動いていることを最低限確認します。

  1. 前提
  2. プロジェクトの準備
  3. 簡単実装
  4. 動作確認

0. 前提

・さくらVPS CentOS7で試しました
・node.js version7.4.0
・express version4.14.0
・passport version0.3.2

1. プロジェクトの準備

1.1. express-generatorの準備

expressのアプリケーションの雛形を簡単に用意できるモジュールのexpress-generatorを使います。
未導入の場合はインストールしてください。-gは適宜外してください。

$npm install -g express-generator

1.2. プロジェクト作成

テスト用アプリケーションのディレクトリを作成したい場所に移動した後、雛形を作成します。
ここでは、testappという名前で進めます。

$express -e testapp

-e は、テンプレートエンジンにejsを使用するオプションですので、好みです。

1.3. 必要なモジュールのインストール

passportをインストールします。

$npm install --save passport

passportでは、ログインの認証方法により、strategyをインストールする必要があります。
今回は、一番単純なpassport-localを使用します。

$npm install --save passport-local

他にインストールしなければいけないものをインストールします。

$npm install

2. 簡単実装

2.1. ログインフォームの実装

今回は、indexページにログインフォームをつけます。
views/index.ejsを以下のように編集してください。
以下はindex.ejsの全体です。

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
 <!-- ★ここから追加★ --> 
    <form action="/login" method="post">
      <input type="text" name="username" />
      <input type="password" name="password" />
      <input type="submit" value="login">
    </form>
 <!-- ★ここまで追加★ --> 
  </body>
</html>

2.2. ログイン操作のリクエストの処理実装

app.jsを編集します。今回は、最低限の実装を全てapp.jsに詰め込みますので汚い内容です。
以下はapp.jsの全体です。編集箇所には★マークがついています。

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var passport = require('passport'); // ★追加
var LocalStrategy = require('passport-local').Strategy; // ★追加

var index = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// ★この段落を新規追加
// passportの認証を実装します。
app.use(passport.initialize());
passport.use(new LocalStrategy(function(username, password, done){
  // ユーザ名aaa パスワードbbbのときだけ認証成功とします
  if(username !== 'aaa' || password !== 'bbb'){
    console.log('login failed');
    return done(null, false);
  }
  console.log('login successed');
  return done(null, username);
}));

app.use('/', index);
app.use('/users', users);

// ★この段落追加
app.post('/login', passport.authenticate('local', {
    // 認証失敗時にはrootページリダイレクトします
    failureRedirect: '/',
    session: false
  }), function(req, res) {
  // 認証成功時にはexpressの雛形のusersページに飛ばします
  res.redirect('users');
});

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};



  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

3. 動作確認

作ったアプリケーションを起動します。

$npm start

アクセスします。
http://(ipアドレス):3000/

今回は、ユーザ名aaa パスワードbbbを入力したときのみuserページに遷移します。
それ以外の入力の場合はルートページにリダイレクトします。
認証結果はconsoleでも確認できます。

実用に耐えるものを作るために、今後以下が必要ということを認識しています。
・DBへのユーザ情報問い合せ
・セッション
・セキュリティ対策
- パスワードのハッシュ化
- cookieまわり
- http通信まわり
- DBまわり
・エラー処理

express-sessionのざっくり翻訳2

node.js express express-session

express-sessionのざっくり翻訳1
express-sessionのざっくり翻訳2

node.jsでexpressを使いつつ色々実装しようと思っていて、express-passportで認証をしようとしていましたが、express-sessionが登場してしまったので、先にexpress-sessionについて公式をざっくりと斜め読みしました。
そのときのざっくり翻訳です。正確さは低めです。また、別のサイト等で自分で調べたことやコメントは(⇒ )としています。

参考:公式(2017/1/26)

  • (⇒express-sessionはセッションをうまいことやってくれるAPIです。)

  • 前提

  • インストール
  • API ☆途中から
  • Session Store Implementation ☆今回

2. API ☆途中から

req.session

sessionデータの保管、アクセスのために、単純に req.session を使うことができます。
保存環境により、JSON形式でserializeされています。以下が例で、ユーザのアクセスカウンタです。:
//公式より丸コピ

// Use the session middleware
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))

// Access the session as req.session
app.get('/', function(req, res, next) {
  var sess = req.session
  if (sess.views) {
    sess.views++
    res.setHeader('Content-Type', 'text/html')
    res.write('<p>views: ' + sess.views + '</p>')
    res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>')
    res.end()
  } else {
    sess.views = 1
    res.end('welcome to the session demo. refresh!')
  }
})
Session.regenerate(callback)
メソッドを呼び出し、セッションを再生成します。完了すると、新しい SIDとSessionがreq.sessionで初期化され、callbackが実行されます。(例省略)

Session.destroy(callback)
セッションを破棄し、req.sessionを未設定とします。完了後、callbackが実行されます。(例省略)

Session.reload(callback)
セッションを保管場所から再取得し、res.session に再設定します。
完了後、callbackが実行されます。(例省略)

Session.save(callback)
メモリ上にある内容でstoreの情報を置き換えることで、storeにsessionを保存します。
このメソッドはsessionのデータが編集された場合、HTTPレスポンスの最後に自動的に呼び出されます。
例えば WebSocketや長く生き続けるリクエストを扱うときに有用です。(例省略)

Session.touch()
.maxAgeプロパティを更新します。普通は実行する必要はなく、middlewareが勝手にやってくれます。

req.session.cookie

それぞれのsessionはそれぞれ固有のcookie objectを持っています。なので、アクセスしてくる人ごとに session cookieを編集することができます。
例えば、req.session.cookie.expres をfalseに設定して user-agent? の間にだけcookieを残しておくことができます。

Cokkie.maxAge
req.session.cookie.maxAgeの代わりに残り時間をミリ秒で返します。また、.expiresに値を再設定するために使用できます。(中略)

req.sessionID

読み込まれた session のIDを取得するために、req.sessionID を参照できます。読み取り専用の値で、sessionが読み込まれるか、生成された際に設定される値です。

3. Session Store Implementation

各 session store は EventEmitter であり、特定のメソッドが実装されている必要があります。

・必須のメソッドは必ずstoreで呼ばれるメソッドです。
・推奨されるメソッドは使用可能であればstoreで呼ばれるメソッドです。
・任意のメソッドは、このモジュールが呼び出すことはありませんが、ユーザに適したstoreを提供するのを助けます。

store.all(callback)
Optional
storeにある全ての session を配列として取得します。
callbackは callback(err, sessions)として呼び出されます。

store.destroy(sid, callback)
Required
session ID(sid)をもとに、storeから sessionを破棄します。callbackは sessionを破棄したあとにcallback(err)として呼びだされます。

store.clear(callback)
Optional
storeから全ての sessionを破棄するために使われます。callbackは、破棄が行われた後に callback(err)として呼び出されます。

store.get(sid, callback)
Required
session ID(sid)をもとに、storeから sessionを取得します。callbackは callback(err, session)として呼び出されます。
引数のsessionは見つかる場合はその sessionが、見つからない場合は nullか undefinedが設定されエラーは起こりません。(中略)

store.set(sid, session, callback)
Required
session ID(sid)とsessionオブジェクトを storeにupsertします。
callbackはstoreにsessionが設定された後 callback(err)として呼び出されます。

store.touch(sid, session, callback)
Recommended session ID(sid)とsessionオブジェクトに "touch"します。(⇒touchとは?)
callbackはsessionがtouchされた後に callback(err)として呼び出されます。

主にstoreによって、不要な sessionを自動的に破棄したり、sessionが有効であることをstoreに伝えるために使用されます。潜在的にidle timerをリセットしています。

Compatible Session Stores

express-sessionに互換性のあるmodule集
公式を参照

Example

公式を参照

express-sessionのざっくり翻訳1

node.js express express-session

express-sessionのざっくり翻訳1
express-sessionのざっくり翻訳2

node.jsでexpressを使いつつ色々実装しようと思っていて、express-passportで認証をしようとしていましたが、express-sessionが登場してしまったので、先にexpress-sessionについて公式をざっくりと斜め読みしました。
そのときのざっくり翻訳です。正確さは低めです。また、別のサイト等で自分で調べたことやコメントは(⇒ )としています。

参考:公式(2017/1/26)

  • (⇒express-sessionはセッションをうまいことやってくれるAPIです。)

  • 前提 ☆今回

  • インストール ☆今回
  • API ☆今回途中まで
  • Session Store Implementation

0. 前提

  • 2017/1/26現在のexpress-sessionのバージョンは1.15.0

1. インストール

$npm install express-session

2. API

$var session = require('express-session');

session(options)

指定されたオプションを使ってセッションのmiddlewareを作ります。
- セッションデータはcookie自体には保存されずサーバに保存されます。cookieにはセッションIDだけが保存されます。
- セッションデータはデフォルトでサーバのMemoryStoreに保存されますが、実用向きではないのでうまくやる必要があります。
- 1.5.0から cookie-parserが不要になりました。(⇒特に2014/6以前のネット上の解説を読む際には注意です。)

Options

cookie
session ID cookieのオブジェクトの設定。デフォルトは
{path: '/', httpOnly: true, secure: false, maxAge: null}

cookie.domain
cookieのDomain属性の設定ができます。
(⇒「どこのドメインに対して有効なcookieにしますか」的なやつみたいです。)
デフォルトではドメインは設定されておらず、だいたいのクライアントではそのドメインだけにcookieを対応させます。

cookie.expires
Dataオブジェクトを指定して、cookieのExpires属性を指定します。
デフォルトでは期限が設定されておらず、だいたいのクライアントでは"non-persistent cookie"(⇒セッション切れたら無効)として扱われます。
・expiresとmaxAgeがどちらも設定されていたら、遅く設定された方が適応されます。
・expireを直接設定するべきではありません。代わりにmaxAgeのみを設定しましょう。

cookie.httpOnly

true/falseでcookieのHttpOnly属性を設定します。デフォルトではtrueでHttpOnlyが設定されます。

cookie.maxAge
ミリ秒指定でcookieのExpires属性を計算します。
その時点のサーバー時間にmaxAgeをミリ秒で加えてExpires属性にします。
デフォルトでは未設定です。
・expiresとmaxAgeがどちらも設定されていたら、遅く設定された方が適応されます。

cookie.path
cookieのPath属性を設定します。デフォルトではドメインルートの'/'が設定されます。

cookie.sameSite
true/falseでcookieのSameSite属性を指定します。
・true : SameSite属性にStrictが設定されます。
・false : SameSite属性は設定されません。
・'lax' : SameSite属性にLaxが設定されます。
・'strict' : SameSite属性にStrictが設定されます。

- まだ標準化されていない部分のため、今後変わる可能性があり、多くのクライアントで無視される可能性があります。

cookie.secure
true/falseでcookieのSecure属性を指定します。trueでSecureが設定されます。
デフォルトではSecureは設定されません。
- trueにするときは気をつけましょう。HTTPS接続していないブラウザはサーバにcookieを返しません。

secure: true とするのが推奨されますが、httpsに対応したサイトが必要です。
HTTPを介してサイトへ接続するとcookieはセットされません。
node.jsをproxyの背後で動かしていて secure: true とする場合は、"trust proxy"をexpress上で設定する必要があります。(コード例省略)
cookie.secureには他にも auto という特別な値を設定することができます。(⇒いい感じに設定してくれます。)
この設定をHTTP, HTTPS両方に対応するサイトで指定する場合には注意する必要があります。(省略)
genid
新しいセッションIDを生成し返す関数です。
req を第一引数とすることで、セッションID生成時にreq内のいくつかの値を使用できます。
デフォルトでは uid-safe ライブラリを生成に使用する関数です。

name
response、またrequestからの読み取り時にセッションIDのクッキーに設定される名前です。
- 同じホスト名で複数のアプリケーションを実行する場合、それぞれのセッションcookieを分ける必要があります。一番簡単なのは、name に対してアプリケーションごとに違う値を設定することです。

proxy
secure cookiesを設定した時、リバースプロキシを信用します。("X-Forwarded-Proto"ヘッダを使用)
デフォルトは undefinedです。
・true : "X-Forwarded-Proto"ヘッダが使用されます。
・false: 全てのヘッダが無視され、TSL/SSL接続が直接行われている場合にのみ secureと処理します。
・undefined : expressの"trust proxy"設定を使用します。

resave
セッションに変更が加えられていないとしても、session storeにセッションを強制的に保存します。
使用するstoreにより必要になるかもしれませんが、クライアントが並列で複数のリクエストをサーバに投げた場合など、変更した内容を変更前の内容で上書きしてしまう恐れもあります。(振る舞いは使っているstoreにもよりけりです。)
デフォルトはtrueですが、デフォルトでの使用は非推奨です。デフォルトの値は将来的に変更される可能性があります。
一般的には false となると思いますが、どちらが必要かケースごとに考える必要があります。

チェックする一番簡単な方法は、使用するstoreが touch methodを持っているかどうかです。
touch methodが実装されていれば、resave : false を安心して設定できます。
touch methodが実装されておらず、保存されているセッションに期限が設定されている場合には resave : trueが必要なのかもしれません。

rolling
毎回の応答で、セッションの識別用cookieを強制的に設定します。cookieの期限はmaxAgeの値をもとに再設定され、期限はリセットされます。
デフォルトはfalseです。
- このオプションがtrueで、かつ saveUninitialized オプションも falseの時、初期化されていないセッションの応答に対してcookieがセットされません。

saveUninitialized
"uninitialized"なセッションをstoreに強制的に保存します。セッションが新しいが編集されていない場合に "uninitialized"となります。
false はloginセッションを実装するのに適しており、サーバー要領の削減、cookieの設定の前に認証を行うという法律?にも対応します。並列に投げられるrequestについても、falseとすることで競合をさけることにつながります。

デフォルトは true ですが、trueは非推奨です。デフォルトの値は将来的に変更される可能性があります。
ケースごとにどちらがよいか考えてください。

- passportjs0.3.0以前を使用している場合は別途注意

secret
Required option
session ID cookieに署名するときに使われる鍵です。
文字列、もしくは文字列からなる配列を設定できます。配列を設定した場合、最初の文字列だけがsession ID cookieの署名に使用され、requestの署名を認証する際に全ての要素が考慮されます。(⇒要するにどういうこと?)

store
セッション保存のインスタンスで、デフォルトでは new MemoryStoreのインスタンスです。

unset
未設定の req.sessionに対する処理を決めます。デフォルトは'keep'です。
・'destroy' : 応答が完了したとき、セッションは破棄されます。
・'keep' : セッションは保存されますが、request中に行われた編集は無視され、保存されません。

初心者が最低限覚えておきたいvimの画面切り替え系

複数のファイルを画面分割したり、切り替えたりしてこそのvimという感じがします。
調べれば色々出て来ますが、最低限これができていればまぁ当面は困らないかな・・・というものを自分のために調べました。

画面分割系

新しいファイルを開く

ファイルを上下に画面分割して開く
:new ファイル名

画面分割時の操作

次のウィンドウへ移動
ctrl + w → w
下のウィンドウへ移動
ctrl + w → j
上のウィンドウへ移動
ctrl + w → k

ウィンドウを閉じる
:q

分割した画面のサイズ変更

全ての画面の大きさを揃える
ctrl + w → =
選択中の分割画面の大きさをnだけ大きくする
n → ctrl + w → +
選択中の分割画面の大きさをnだけ小さくする
n → ctrl + w → -

画面切り替え系

ファイルを新しいバッファとして開く
:enew ファイル名
bufferの確認
:buffers

バッファ切り替え

バッファ番号n番をactiveにする
:b n
画面を閉じてinactiveにする
:hid
次のバッファを開く
:bn
上下に画面分割してn番目のbufferを開く
:sbn

上記で上下の画面分割と、簡単な画面切り替えができるようになれればと思っています。
そのあと、横の画面分割とより詳細な画面の調節を習得しようと思っています。

仕事でExcelのショートカット覚えていて思ったのは、わからなくなったらその都度調べるのが一番よいということでした。
Excelは3ヶ月でショートカットをだいたい覚えたので、vimも3ヶ月とかでそこそこ快適に使えるようになるのではないかなぁと思っています。

node.jsの標準搭載のdebugを使ってみる

CentOS7 node.js さくらVPS

node.jsの標準搭載のdebugを使ってみる

node.js標準搭載のdebugを使ってみます。
さくらVPSを使用して、外部から実際に接続することを前提としています。
最低限のセキュリティ対策をした上で、実験後開いたポートはすぐ閉じるようにしてください。
セキュリティの問題が起こる可能性は低いですが、自己責任で行ってください。

  1. 前提
  2. 一番簡単なhttpサーバの実装
  3. debugを試す
  4. おわり

0. 前提

・さくらVPS CentOS7
・node.js v7.4.0
・firewallは有効にしてあり、最低限のセキュリティ対策はしてある

1. 一番簡単なhttpサーバの実装

適当なディレクトリを作成し、そこで作業します。

$mkdir testapp
$cd testapp

新しいファイル作成し簡単なコーディングを行います。

$vim server.js

server.jsに以下を入力してください。

var http = require('http');  
http.createServer(function(req, res) {  
  res.writeHead(200, {'Content-Type' : 'text/plain'});  
  res.write('Hello World!!');  
  res.end();  
}).listen(8000);  

今回は8000番ポートを使用しています。
8000番ポートを一時的に開放します。

$firewall-cmd --add-port=8000/tcp

作成した簡易サーバを起動します。

$node server.js

外部から接続をしてみます。
http://(vpsのipアドレス):8000/

Hello World!!が表示されます。

2. debugを試す

一度サーバを停止します。
Ctrl + Z server.jsを以下のように編集します

var http = require('http');  
http.createServer(function(req, res) {  
  debugger;  ★この行を追加
  res.writeHead(200, {'Content-Type' : 'text/plain'});  
  res.write('Hello World!!');  
  res.end();  
}).listen(8000);  

デバッグモードで起動します。
デバッグモードは、node debug ファイル名 となります。

$node debug server.js

すると、以下のように表示されます。

[ユーザ名@ホスト名 ディレクトリ名]$ node debug server.js
< Debugger listening on 127.0.0.1:5858
connecting to 127.0.0.1:5858 ... ok
break in server.js:1
> 1 var http = require('http');
  2 http.createServer(function(req, res) {
  3   res.writeHead(200, {'Content-Type' : 'text/plain'});
debug>

サーバー起動時にどのような順序で処理が実行されているのかを観察することができます。
主な操作は以下のとおりです。

コマンド : 内容
n : 次の処理へ
s : 次の処理へ(関数等の中身まで詳細に追う)
o : 一つ外側の処理へ
c : 一行一行止める処理をやめる
exec (コマンド) : 簡単なnode.jsのスクリプトを実行する

サーバの立ち上げを最後まで行ってしまいます。

>c

その後、手持ちのスマホなどから、
http://(vpsipアドレス):8000/
へアクセスします。

すると、debugger;の部分で停止することがわかります。
debuggarは、その部分が実行された場合に処理を一旦止めてくれます。
debugモードを使うと、実際にアクセスしたときにどのような流れで処理が行われるのかを観察することができます。

また、debugger;の行で止まっている時に、reqの中身を見てみます。

> exec console.log(req)

中身が多すぎて全て表示されませんが、ざっと見てみると、自分がどんな環境からアクセスしたのか、という情報などを確認することができます。

より詳しい説明は、公式のDocumentationを参考ください。

3. おわり

サーバを停止します。
Ctrl + Z
開いたポートは閉じます

$firewall-cmd --remove-port=8000/tcp
$firewall-cmd --list-ports
(8000が表示されないことを確認)

さくらVPSでTCP Wrapperを実際に触ってみる

さくらVPSを用いて、TCP Wrapperの設定をしてみる手順書と、調べたことのメモ書きです。 TCP WrapperでSSHが繋がらなくなることを確認してみます。

  1. 前提
  2. TCP Wrapperとは?
  3. 設定前の確認
  4. TCP Wrapperの設定
  5. sshがつながらないことの確認
  6. 設定例

0. 前提

  • さくらVPSの初期設定が完了している
  • SSHの接続設定が完了している
  • SSHのポート番号は33331
  • TCP Wrapperは初期状態のまま未設定

1. TCP Wrapperとは?

(初心者が調べた結果) TCP Wrapperは、アプリケーションレベルで、一部のサービスへのアクセス制御を行うことができる仕組みです。 アプリケーションレベルというのは、インターネットの通信プロトコルTCP/IP階層モデル(もしくはOSI参照モデル)のアプリケーション層です。参考ページ ファイアーウォールを抜けた後に行うアクセス制御なので、かなりギリギリのところで食い止めるイメージです。

一部のサービスへのアクセス制御というのは、すべてのアクセスに対して有効なアクセス制御ではないということです。 libwrapというTCP Wrapperのライブラリにリンクしているサービスに対してのみアクセス制御を行います。 例えば、sshd(sshのデーモン)はアクセス対象です。

よって、TCP Wrapperを設定しても、ファイアーウォールを設定していないと危険な状態となります。 sshなどの一部の大切な機能について、より強固な設定を行うためのもの、という位置づけです。

2. sshの接続設定と確認

TCP Wrapperの設定をする前に、SSHの接続が可能であることを確認してください。 ローカルからTeratermなどを使ってVPSに接続できることを確認してください。

次に、ポートが開放されているかを確認します。 ポートをチェックができるサイトで確認します。 ポートが開放されていることが確認できるはずです。

3. TCP Wrapperの設定

/etc/hosts.denyファイルに、「TCP Wrapperでアクセス制御できるすべてのアクセスを禁止する」設定を記載します。 hosts.denyを開きます。

vi /etc/hosts.deny

hosts.denyに以下を追加します。

ALL: ALL

4. sshがつながらないことの確認

TCP Wrapperは再起動等を行わなくても反映されます。

ローカルからsshにより接続を再度確認してください。 sshによるアクセスができないことが確認できます。

次に、ポートが開放されているかを確認します。 ポートをチェックができるサイトで確認します。 ポートが開放されていることが確認できるはずです。

  1. 設定例 このままではsshでの接続ができません。 ローカルからの接続と、sshについては接続する機器のみを指定して接続を許可します。

hosts.denyは変更せずに、hosts.allowを開きます。

vi /etc/hosts.allow

hosts.allowに以下を追加します。

ALL: 127.0.0.l
sshd: (作業を行うPCのグローバルIPアドレス)

作業を行うPCのグローバルIPアドレスIPアドレスをチェックするサイトで確認すると楽だと思います。

上記の設定により、全てのサービスは、自分自身からの通信は許可します。 sshdについては、作業を行うPCからの通信も許可します。

さくらVPS CentOS 7でMongoDBを試してみる手順書2

さくらVPSにMongoDB ver3.4を試しにインストールしてみた際の手順書1
さくらVPSにMongoDB ver3.4を試しにインストールしてみた際の手順書2

さくらVPSにMongoDB ver3.4を試しにインストールしてみた際の手順書1のつづきです。

  1. 前提
  2. MongoDBのインストール
  3. MongoDBの起動と終了
  4. MongoDBを動かしてみる ☆今回
  5. MongoDBをNode.jsから呼び出してみる

4. MongoDBを動かしてみる ☆今回

4.1. MongoDBを動かす前の最低限の知識 - MongoDBのレコードは、JSON記法に似たドキュメント
- ドキュメントは、フィールド名とその値で構成(例:"name" : "hogehoge")
- ドキュメントにはarrayも含めることが可能
- SQLの基本操作で言うところのSelectがFind、DeleteがRemove

4.2. MongoDB Shellにテストデータを取り込む

ダウンロード用のディレクトリを作成し、
その中に公式ドキュメントで用意されている、テストデータをダウンロードします。

$cd ~
$mkdir downloads
$curl https://raw.githubusercontent.com/mongodb/docs-assets/primer-dataset/primer-dataset.json > ~/downloads/primer-dataset.json

/home/hogehoge/downloads/primer-dataset.jsonが作成されました。

中身を確認すると、{}で囲まれたとても長いファイルであることが確認できます。

内容は、たくさんのレストランの情報です。

$ tail -n 5 ~/downloads/primer-dataset.json ★後ろから5行分を表示
(以下表示内容)

{"address": {"building": "971", "coord": [-73.964618, 40.801388], "street": "Ams
terdam Ave", "zipcode": "10025"}, "borough": "Manhattan", "cuisine": "Other", "g
rades": [], "name": "Subway", "restaurant_id": "50018987"}                      
{"address": {"building": "325", "coord": [-73.99514429999999, 40.7521509], "stre
et": "W 33Rd St", "zipcode": "10001"}, "borough": "Manhattan", "cuisine": "Other
", "grades": [], "name": "Fairfield Inn Suites Penn Station", "restaurant_id": "
50018989"}                                                                      
{"address": {"building": "399", "coord": [-73.906438, 40.669367], "street": "Mot
her Gaston Blvd", "zipcode": "11212"}, "borough": "Brooklyn", "cuisine": "Other"
, "grades": [], "name": "", "restaurant_id": "50018993"}                        
{"address": {"building": "461", "coord": [-74.138492, 40.631136], "street": "Por
t Richmond Ave", "zipcode": "10302"}, "borough": "Staten Island", "cuisine": "Ot
her", "grades": [], "name": "Indian Oven", "restaurant_id": "50018994"}         
{"address": {"building": "921", "coord": [-73.9691347, 40.6389857], "street": "C
ortelyou Rd", "zipcode": "11218"}, "borough": "Brooklyn", "cuisine": "Other", "g
rades": [], "name": "Cold Press'D", "restaurant_id": "50018995"}     

このレストランのテストデータをmongodbにインポートします。
まず、MongoDBを起動します。

$sudo service mongod start

データをMongoDBにインポートします。

$ mongoimport --db test --collection restaurants --drop --file ~/download/primer-dataset.json
2017-01-17T14:40:14.567+0900    connected to: localhost
2017-01-17T14:40:14.568+0900    dropping: test.restaurants
2017-01-17T14:40:16.155+0900    imported 25359 documents

mongoimportコマンドは、読み込みが可能な規則で書かれたJSON(MongoDB用に拡張したJSONを含む)、CSV、TSVファイルを取り込むコマンドです。

MongoDBの構造はDBに紐づくCollectionにJSONのようなデータが格納されていますが、
DB
.Collection
.Document
.Document
.Document
.Document
.Collection
.Document
DB
.Collection
.Document

上記のimportコマンドでは、testというDBのrestaurantsというCollectionに取り込みを行いました。
Collectionは、RDBで言うところのTableに似た単位となります。

4.3. MongoDB Shell(mongo)の起動と終了 MongoDBを操作するのには、mongoというシェルを使います。
mongoにコマンドを打ち込むことでMongoDBを操作します。

mongoを起動します。

$mongo

すると、カーソルの一番左が「 > 」になり、mongoシェルが起動したことがわかります。

mongoシェルを終了するには、

Ctrl + C

を押下します。

4.4. Find mongoを再度起動します。

$mongo

「test」という名前がついたDBに移動します。

>use test

先程レストランのテストデータを取り込んだrestaurants Collectionから、borought(区)がManhattanのレストランを探します。

>db.restaurants.find( { "borough" : "Manhattan" } )

(とても見にくいが、よく見るとboroughがManhattanのDocumentが出力)

4.5. Insert

restaurants Collectionに、港区にあるレストランhogehogeを追加します。

> db.restaurants.insert( { "name" : "hogehoge", "borough" : "Minato-Ku" } )
WriteResult({ "nInserted" : 1 })  

Findを使って、Insertが完了したかを調べてみます。
港区で検索してみます。

> db.restaurants.find({"borough" : "Minato-Ku"})
{ "_id" : ObjectId("587db78e08cd7086ba0d6a33"), "name" : "hogehoge", "borough" : "Minato-Ku" }

ANDやOR等を使った検索はここでは扱いません。

4.6. Update

港区にあったレストランhogehogeをレストランfugaに改名させます。

>db.restaurants.update({"name":"hogehoge"},{$set:{"name":"fuga"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

4.7. Remove

レストランfugaは日本のレストランなので、このCollectionにあるのは間違っています。 fugaレストランのデータを削除します。

>db.restaurants.remove({"name":"fuga"})

以上がMongoDBの最低限の一番簡単な触りの操作です。

もう少し詳しいチュートリアルのためには、例えば公式ドキュメントがあります。