Old Sunset Days

Node.jsからMongoDBを利用する

日付 タグ node.js mongodb カテゴリ node.js

目次

Node.jsでnode-mongodb-nativeを使ってMongoDB

Node.jsからMongoDBに接続して扱うにはいくつか利用できるパッケージがあるようだが、 今回は標準的なnode-mongodb-nativeを利用してテストしてみる。

https://github.com/mongodb/node-mongodb-native - node-mongodb-native

MacへのMongoDBのインストールはこの前にやってあったので、MongoDBを利用するためにMongoDBのサービスを先に起動しておく。
( ==> MacにNoSQLのMongoDBを入れて使ってみる

テストディレクトリ作成とnode-mongodb-nativeのインストール

node-mongotestというテストディレクトリを作成、npmでnode-mongodb-nativeをインストール。

$ mkdir node-mongotest
$ cd node-mongotest
$ npm init
$ npm install mongodb --save

これでnode-mongodb-nativeを使える準備ができた。

MongoDBへの接続方法の確認

node-mongodb-nativeでのMongoへの接続は、MongoClient.connect(url)で接続できる。
自分のローカルMacで動いているMongoDBに接続するにはURLはmongodb://localhost/とすれば良い。 今回テストで使用するDB名はmongo_test、コレクション名はmemoとした。

var MongoClient = require('mongodb').MongoClient;
const mongo_url = 'mongodb://localhost/';
const mongo_dbname = 'mongo_test';
const collection_name = 'memo';
let client;
const connectDB = async() => {
  if(!client)
    client = await MongoClient.connect(mongo_url,
       { useNewUrlParser: true, useUnifiedTopology: true });
}
const db = () => { return client.db(mongo_dbname); }

MongoDBへデータをINSERTする

まずは何にしてもドキュメント(データのこと)がないと始まらないので、まずはINSERTの方法について確認していく。

// 1つデータを追加する
const create = async (title, message) => {
  await connectDB();
  const collection = db().collection(collection_name);
  await collection.insertOne({ title: title, message: message });
}

insertOneにコレクションに追加したいデータを指定すれば良い。
ここではメモ的な内容を保存することを前提として、titlemessageを保存することにした。

MongoDBのデータをUPDATEする

// データをアップデートする
const update = async (title, message) => {
  await connectDB();
  const collection = db().collection(collection_name);
  await collection.updateOne(
    { title: title },
    { $set: { title: title, message: message } });
}

updateOneでコレクションに入っているtitleでマッチするドキュメントでアップデートをかけている。

MongoDBのコレクションから1つSELECT

// データを1つ読み込む
const read = async (title) => {
  await connectDB();
  const collection = db().collection(collection_name);
  const data = await collection.findOne({ title: title });
  console.log(`data = ${util.inspect(data)}`);
}

findOnetitleでマッチするドキュメントを1つ取り出している。 このメソッドはマッチするドキュメントが1つだけしかないとき、あるいは結果を1つだけ取り出したい時に使う。

MongoDBのコレクションから全部をSELECT

先ほどのfindOneは1つのドキュメントを取り出す関数だったが、全部取り出すにはcollection.find()を使えばいい。

// 全データを読む
const readall = async () => {
  await connectDB();
  const collection = db().collection(collection_name);
  const docs = await new Promise((resolve, reject) => {
    var docs = [];
    collection.find().forEach(
      doc => { docs.push(doc); },
      err  => {
        if (err) reject(err);
        else resolve(docs);
      }
    );
  });
  console.log(`data = ${util.inspect(docs)}`);
}

ただし、collection.find()で得られる結果はCursor(カーソル)であるため、

collection.find(query).forEach(function(doc) {
  // handle
}, function(err) {
  // done or error
});

のような形でforEachでデータを処理している。

MongoDBのコレクションからデータをDELETE

コレクションからドキュメントを削除するには

const destroy = async (title) => {
  await connectDB();
  const collection = db().collection(collection_name);
  const doc = await collection.findOne({ title: title });
  if (!doc) {
    throw new Error(`No data found for ${title}`);
  } else {
    await collection.findOneAndDelete({ title: title });
  }
}

findOneAndDeletetitleでマッチをかけてデータを削除だが、その前に該当するデータが存在するかどうかfindOneで検索をかけている。

MongoDBへの接続を解除

async function close() {
  if (client) client.close();
  client = undefined;
}

MongoDBヘの接続解除はclose()を呼べばいい。

通しでテストをしてみる

以上の処理にもとづいたテストサンプルを流してみる。

mongotest.js

var util = require('util');
var MongoClient = require('mongodb').MongoClient;
const mongo_url = 'mongodb://localhossst/';
const mongo_dbname = 'mongo_test';
const collection_name = 'memo';

let client;

const connectDB = async() => {
  if(!client)
    client = await MongoClient.connect(mongo_url,
       { useNewUrlParser: true, useUnifiedTopology: true,  connectTimeoutMS: 5000});
}

const db = () => { return client.db(mongo_dbname); }

// 1つデータを追加する
const create = async (title, message) => {
  await connectDB();
  const collection = db().collection(collection_name);
  await collection.insertOne({ title: title, message: message });
  //console.log('create done');
}

// データをアップデートする
const update = async (title, message) => {
  await connectDB();
  const collection = db().collection(collection_name);
  await collection.updateOne(
    { title: title },
    { $set: { title: title, message: message } });
  //console.log('update done');
}

// データを1つ読み込む
const read = async (title) => {
  await connectDB();
  const collection = db().collection(collection_name);
  const data = await collection.findOne({ title: title });
  console.log(`data = ${util.inspect(data)}`);
  //console.log('read done');
}

// 全データを読む
const readall = async () => {
  await connectDB();
  const collection = db().collection(collection_name);
  const docs = await new Promise((resolve, reject) => {
    var docs = [];
    collection.find({}).forEach(
      doc => { docs.push(doc); },
      err  => {
        if (err) reject(err);
        else resolve(docs);
      }
    );
  });
  console.log(`data = ${util.inspect(docs)}`);
  //console.log('readall done');
}

// データを削除する
const destroy = async (title) => {
  await connectDB();
  const collection = db().collection(collection_name);
  const doc = await collection.findOne({ title: title });
  if (!doc) {
    throw new Error(`No data found for ${title}`);
  } else {
    await collection.findOneAndDelete({ title: title });
  }
  //console.log('destroy done');
}

async function close() {
  if (client) client.close();
  client = undefined;
  //console.log('close done');
}

(async () => {
  try {
    console.log('********* Add 3 items *********');
    await create('yesterday', 'go to school');
    await create('today', 'watch a movie');
    await create('tomorrow', 'play baseball');
    await readall();

    console.log('******** update 1 item *********');
    await update('today', 'watch TV');
    await readall();

    console.log(' ******** select 1 item *********');
    await read('tomorrow');

    console.log('******** delete 1 item *********');
    await destroy('today');
    await readall();
  } catch(err) {
    console.log('******** error handling *********');
    console.error(err);
  } finally {
    console.log('********* all tests done *********');
    await close();
  }
})();

実行した結果は以下の通り。

$ node mongotest.js
********* Add 3 items *********
data = [
  {
    _id: 5fba15a745613070bb19c8af,
    title: 'yesterday',
    message: 'go to school'
  },
  {
    _id: 5fba15a745613070bb19c8b0,
    title: 'today',
    message: 'watch a movie'
  },
  {
    _id: 5fba15a745613070bb19c8b1,
    title: 'tomorrow',
    message: 'play baseball'
  }
]
******** update 1 item *********
data = [
  {
    _id: 5fba15a745613070bb19c8af,
    title: 'yesterday',
    message: 'go to school'
  },
  {
    _id: 5fba15a745613070bb19c8b0,
    title: 'today',
    message: 'watch TV'
  },
  {
    _id: 5fba15a745613070bb19c8b1,
    title: 'tomorrow',
    message: 'play baseball'
  }
]
 ******** select 1 item *********
data = {
  _id: 5fba15a745613070bb19c8b1,
  title: 'tomorrow',
  message: 'play baseball'
}
******** delete 1 item *********
data = [
  {
    _id: 5fba15a745613070bb19c8af,
    title: 'yesterday',
    message: 'go to school'
  },
  {
    _id: 5fba15a745613070bb19c8b1,
    title: 'tomorrow',
    message: 'play baseball'
  }
]
********* all tests done *********

ドキュメント追加、参照、削除がきちんとできたことが確認できた。
Expressと組み合わせたりすれば、サーバー処理でMongoDBに接続してデータを保存する処理が可能になるだろう。