Help us understand the problem. What is going on with this article?

NeDB を使ってみた

More than 3 years have passed since last update.

はじめに

Electron を使ってデスクトップアプリを作成しようとしています。ある程度のデータを保存しておいて検索したりしたいので、データベースエンジンを使用したいと思います。
ただし今回は、アプリと別にデータベースエンジンをインストールする必要があるのは避けたいと思いました。
Electron であれば、Web Storage や Indexed DB が使えるけれど、使い勝手がいいとは言えません。
アプリに組込できるタイプのデータベースエンジンを探しました。その中に NeDB がありました。

参考 Node.jsで使えるファイルDB"NeDB"のススメ

NeDB とは

NeDB は、組込型の永続的あるいはインメモリのデータベースで、Node.js 、NW.js 、Electron およびブラウザで動作し、100 % JavaScript でバイナリ依存しません。API は MongoDB のサブセットで、非常に高速です。

公式 louischatriot/nedb: Embedded datastore for node.js

NeDB を使ってみた

  • NeDB 1.8.0

インストールする

Node.js アプリのワークスペースにインストールします。

npm install --save nedb 

データベースを作成する

インメモリのみ(データベース読込不要)

var Datastore = require('nedb');
var db = new Datastore();

永続的なデータストアに手動で読込

var Datastore = require('nedb');
var db = new Datastore({ 
    filename: '◇◇/◆◆.db'
});
db.loadDatabase();
あるいは
db.loadDatabase(function(err){
  // コマンドが実行される
});

ファイル名の拡張子は任意です。
ファイルはなければ自動で作成されます。
あれば読込まれます。

永続的なデータストアに自動で読込

var Datastore = require('nedb');
var db = new Datastore({ 
    filename: '◇◇/◆◆.db',
    autoload: true
});
// 直ぐにコマンドが発行できる

複数コレクションを作成

var db = {};
db.◆◆ = new Datastore('◇◇/◆◆.db');
db.●● = new Datastore('◇◇/●●.db');
db.◆◆.loadDatabase();
db.●●.loadDatabase();

コレクションごとに読込する必要あります。非同期で実行されます。

ドキュメントを挿入する

MongoDB もそうですが、NoSQL データベースでは、データの構成単位は「レコード」でなく「ドキュメント」なんですね。

var doc = {
    ○○: ◇◇,
    ....
};
db.insert(doc);
あるいは
db.insert(doc, function(err, newDoc){
    ....
});

newDoc は新規挿入されたドキュメント。
_id フィールドが追加されます。これは 16 文字の英数文字列。変更は不可です。

ドキュメントの配列も挿入できます。

var docs = [
    { .... },
    { .... },
];
db.insert(docs, function(err, newDocs){
    ....
});

newDocs は配列になります。

内容が同一のドキュメントは挿入できません。

ドキュメントを検索する

基本的な問合せ

指定した値と一致するドキュメントを検索

db.find({ ○○: ●● }, function(err, docs){
    ....    
});

docs はドキュメントの配列です。一致するドキュメントがなければ [] になります。

指定した文字列を「含む」ドキュメントを検索

db.find({ ○○: /●●/ }, function(err, docs){
    ....
});

条件を AND 指定してドキュメントを検索

db.find({ ○○: ●●, ◇◇: ◆◆ }, function(err, docs){
    ....
});

条件を指定せず全てのドキュメントを検索

db.find({}, function(err, docs){
    ....
});

指定した値と一致するドキュメントの一つを検索

db.findOne({ ○○: ●● }, function(err, doc){
    ....
});

doc は一致するドキュメントがなければ null になります。

比較演算

指定した値より小さい

db.find({ ○○: { $lt: ●● } }, function(err, docs){
    ....

指定した値より小さいか等しい

db.find({ ○○: { $lte: ●● } }, function(err, docs){
    ....

指定した値より大きい

db.find({ ○○: { $gt: ●● } }, function(err, docs){
    ....

指定した値より大きいか等しい

db.find({ ○○: { $gte: ●● } }, function(err, docs){
    ....

指定した値のいずれかと一致する

db.find({ ○○: { $in: [●●, ◆◆] } }, function(err, docs){
    ....

指定した値と異なる

db.find({ ○○: { $ne: ●● } }, function(err, docs){
    ....

指定した値のいずれでもない

db.find({ ○○: { $nin: [●●, ◆◆] } }, function(err, docs){
    ....

指定したフィールドがある

db.find({ ○○: { $exists: true } }, function(err, docs){
    ....

正規表現を他の条件指定と合わせて使う。例えば

db.find({ ○○: { $regex: /●●/, $nin: [◆◆, ■■] } }, function(err, docs){
    ....

条件を論理演算

条件を OR 指定

db.find({ $or: [{ ○○: ●● }, { ◇◇: ◆◆ }] }, function(err, docs){
    ....

条件を NOT 指定

db.find({ $not: { ○○: ●● } }, function(err, docs){
    ....

関数を使って条件指定

db.find({ $where: function(){ return this.○○ = ●●; } }, function(err, docs){
    ....

指定を組合せる。例えば

db.find({ $or: [{ ○○: ●● }, { ◇◇: ◆◆ }], □□: ■■ }, function(err, docs){
    ....

並替など

並替

db.find({ .... }).sort({ ○○: 1 }).exec(function(err, docs){
    ....

逆順に並替

db.find({ .... }).sort({ ○○: -1 }).exec(function(err, docs){
    ....

複数フィールドを指定して並替

db.find({ .... }).sort({ ○○: 1, ◇◇: 1 }).exec(function(err, docs){
    ....

先頭から指定した数だけ

db.find({ .... }).sort({ .... }).limit(●).exec(function(err, docs){
    ....

ドキュメントのフィールドを指定

指定したフィールドだけ

db.find({ .... }, { ○○: 1, ◇◇: 1 }, function(err, docs){
    ....

指定したフィールドを除いて

db.find({ .... }, { ○○: 0, ◇◇: 0 }, function(err, docs){
    ....

MongoDB と非互換の書き方

db.find({ .... }).projection({ ○○: 1, ◇◇: 1 }).exec(function(err, docs){
    ....

ドキュメントをカウントする

指定した条件に一致するドキュメントをカウント

db.count({ .... }, function(err, count){
    ....
});

条件を指定せず全てのドキュメントをカウント

db.count({}, function(err, count){
    ....
});

ドキュメントを更新する

ドキュメントの内容を別のに置換

db.update({ .... }, { ○○: ◆◆ }, { multi: .... }, function(err, numReplaced){
    ....
});

指定されたフィールドの値が置換えられます。
指定されなかったフィールドは残らないので注意が必要です。
_id は変更されずに保持されます。

既存のフィールドの値を置換

db.update({ .... }, { $set: { ○○: ●● } }, { multi: .... }, function(err, numReplaced){
    ....
});

指定されたフィールドの値が置換えられます。
指定されなかったフィールドと値は残ります。

フィールドを削除

db.update({ .... }, { $unset: { ○○: true } }, {}, function () {
    ....
});

ドキュメントあるいはフィールドがあれば更新、なければ挿入

db.update({ .... }, { ○○: ●● }, { upsert: true }, function(err, numReplaced, upsert){
    ....
});

ドキュメントを削除する

指定した条件のドキュメント一つを削除する

db.remove({ .... }, {}, function(err, numRemoved){
    ....
});

指定した条件のドキュメント全てを削除する

db.remove({ ... }, { multi: true }, function(err, numRemoved){
    ....
});

全てのドキュメントを削除する

db.remove({}, { multi: true }, function(err, numRemoved){
    ....
});

おわりに

API が MongoDB のサブセットなので、新たなリファレンスは要らないとも思ったけれど、自分が MongoDB のコマンドをよく知らないのと、完全な互換ではなさそうなので、整理を兼ねて記事にしておくことにしました。
これでも公式サイトのドキュメントの全部ではありません。自分が使うにはこれだけあれば十分かなと思って書きました。

tinymouse
SI 企業の SE であり、日曜プログラマであり、二児の父。
https://tinymouse.hatenadiary.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした