Node.js で使えそうな軽量 NoSQL ライブラリを調べる

Node.js でちょっとしたアプリを開発していて、PostgreSQL とか MySQL とかいう DB を用意するのは大仰かな、という時に、SQLite を選定することはまぁありがちだろう。

しかし、それよりもさらにもう少しお手軽に、インストールが不要な・楽なライブラリは何かないかな、つーか RDBMS ほどガッチリした型がないモノもあるんだよなー、とか思って、軽量な NoSQL ライブラリを調べてみた。

目次

UnQLite

UnQLite は、SQLite に近い思想で作られている、シングル DB ファイルで管理される NoSQL ライブラリ。なんて読むんだろう、うんこらいと?w

C 言語ベースで作られているのだが、Node.js から操作するラッパーの npm パッケージがメンテされておらず、Node.js からの利用は難しい。

また、そもそもローカルでの UnQLite 単独の動作も上手くいかなかったので、今回はドッグフーディングできず。

TingoDB

TingoDB は、NoSQL で知られる MongoDB と互換性があるという、Node.js ベースのライブラリ。ちんごでーびー?w

$ npm install -S tingodb

試しに以下のような Node.js スクリプトを書いてみた。

const DB = require('tingodb')().Db;

// 引数で指定した名前のディレクトリを用意しておくこと
const db = new DB('./tingodb-db', {});

// Collection (Table みたいなモノ) を選択する・コレが `db` で指定したディレクトリ配下にファイルとして作られる
const collection = db.collection('hello-world-collection');
// Collection に Document (レコードみたいなモノ) を書き込む
collection.insert(
  [{ hello: 'World 1' }, { hello: 'World 2' }],
  { w: 1 },  // Primary に書き込みが完了したら成功とする、デフォルトオプション
  (insertError, result) => {
    if(insertError) return console.error('Insert Error', insertError);
    console.log('Insert Result', result);
    
    // 条件を指定して Document を検索・取得する
    collection.findOne({ hello: 'World 2' }, (findError, item) => {
      if(findError) return console.error('Find Error', findError);
      console.log('Find Result', item);
    });
  }
);

実行してみる。

$ node ./tingodb.js
Insert Result [
  { hello: 'World 1', _id: ObjectID { id: 2 } },
  { hello: 'World 2', _id: ObjectID { id: 3 } }
]
Find Result { hello: 'World 2', _id: ObjectID { id: 3 } }

すると、次のようなファイルが生成されていた。

{"k":"0000000078","o":"0000000048","v":"001"}
{"_id":2,"_uid":2,"_dt":1630979090746,"_s":"4db1f9e54aeb4202e72f33276616f874"}
{"hello":"World 1","_id":{"$wrap":"$oid","v":2}}
{"k":"0000000078","o":"0000000048","v":"001"}
{"_id":3,"_uid":3,"_dt":1630979090748,"_s":"2f62c9a076c6455b0d4154c968c58c21"}
{"hello":"World 2","_id":{"$wrap":"$oid","v":3}}

拡張子はないが JSON チックな形式で記述されており、テキストエディタで開ける。メタ情報とデータが2行で1セットになっている感じ。雰囲気は JSON Lines っぽい。

自分は MongoDB に詳しくないのだが、データを JSON チックなテキストファイルで管理できるので扱いやすいかも。MongoDB のパッケージと互換性を保つためなのか、コールバック形式で記述するのがちょいと面倒かしら。

NeDB

NeDB は、TingoDB よりももう少しライトに使えそうなライブラリ。データはまさに JSON Lines 形式で管理される。

$ npm install -S nedb

前述の TingoDB とほとんど同じような Insert・Select をやってみる。

const Datastore = require('nedb');

// 引数で指定した名前のディレクトリ・ファイルがなければ自動的に作成される
// 引数で Collection (Table みたいなモノ) 名を指定する・コレがファイルとして作られる
const collection = new Datastore({ filename: './nedb-db/hello-world-collection', autoload: true });

// Collection に Document (レコードみたいなモノ) を書き込む
collection.insert(
  [{ hello: 'World 1' }, { hello: 'World 2' }],
  (insertError, result) => {
    if(insertError) return console.error('Insert Error', insertError);
    console.log('Insert Result', result);
    
    // 条件を指定して Document を検索・取得する
    collection.findOne({ hello: 'World 2' }, (findError, item) => {
      if(findError) return console.error('Find Error', findError);
      console.log('Find Result', item);
    });
  }
);

実行するとこんな感じ。メタデータとなる _id の持ち方が TingoDB とは違う。

$ node ./nedb.js
Insert Result [
  { hello: 'World 1', _id: 'ji9uXbxP92l42coh' },
  { hello: 'World 2', _id: '0VwBfnDfSuHa1JIm' }
]
Find Result { hello: 'World 2', _id: '0VwBfnDfSuHa1JIm' }

生成された DB ファイルはテキストファイルとして開ける。JSON Lines っぽい感じで、1行が1レコード (1 Document) となっている。

{"hello":"World 1","_id":"ji9uXbxP92l42coh"}
{"hello":"World 2","_id":"0VwBfnDfSuHa1JIm"}

dby : DBYaml

dby は、YAML ファイルでデータを管理するライブラリ。Go 言語製で、Node.js (npm) 向けのラッパーはなさそうだが、面白そうだったので紹介のみ。

試してないヤツ

その他試していないのだが、以下はいずれも JSON ファイルとしてデータを管理してくれそうなライブラリ。サンプルコードを見るに、使用感は NeDB や TingoDB に似ている感じ。

どれを使うか

UnQLite は C 言語ベースなので、環境を選ばず使えそうかなーとは思うのだが、自分の環境では上手く使えなかったので惜しいなーと。

それ以外は、DB とするファイルに JSON や YAML といった形式を使うライブラリが多かった。テキストベースで扱いやすいし、配列の入れ子なども出来て NoSQL 的なデータの持ち方が容易なので、さもありなん。

今回ドッグフーディングした TingoDB と NeDB はよく似ている。結局 JSON ファイルで管理するので、ファイル I/O 性能に左右され、ライブラリとしての性能は大差ないかなと思う。いずれもコールバック関数で処理を記述していくタイプで、コーディングスタイルも大きく違わないかしら。大きな違いは「MongoDB の API と互換性があるかどうか」で、TingoDB は互換性があるので、「本番環境では MongoDB を使うつもり」のような要件があるのであれば、TingoDB が良いだろう。そうでなければ、MongoDB の API などを意識せず手軽に書ける NeDB を使うのが良いと思う。