NeDB を使ってみた

# はじめに

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

参考 [Node.jsで使えるファイルDB"NeDB"のススメ](http://www.slideshare.net/isamusuzuki54/dbne-db)

# NeDB とは

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

公式 [louischatriot/nedb: Embedded datastore for node.js](https://github.com/louischatriot/nedb)

# NeDB を使ってみた

* NeDB 1.8.0

## インストールする

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

```console
npm install nedb --save 
```

## データベースを作成する

### インメモリのみ（データベース読込不要）

```js:
var Datastore = require('nedb');
var db = new Datastore();
```

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

```js:
var Datastore = require('nedb');
var db = new Datastore({ 
    filename: '◇◇/◆◆.db'
});
db.loadDatabase();
```
```js:
db.loadDatabase(function(err){
    // コマンドが実行できる
});
```

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

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

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

## ドキュメントを挿入する

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

```js:
var doc = {
    ○○: ●●,
    ....
};
db.insert(doc);
```
```js
db.insert(doc, function(err, newDoc){
    ....
});
```
```newDoc``` は新規挿入されたドキュメント。
```_id``` フィールドが追加されます。これは 16 文字の英数文字列。変更は不可です。

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

```js:
var docs = [
    { .... },
    { .... },
];
db.insert(docs, function(err, newDocs){
    ....
});
```    
```newDocs``` は配列になります。

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

## ドキュメントを検索する

### 基本的な問合せ

#### 指定した値と一致するドキュメントを検索
```js:
db.find({ ○○: ●● }, function(err, docs){
    ....	
});
```
```docs``` はドキュメントの配列です。一致するドキュメントがなければ ```[]``` になります。 

#### 指定した文字列を「含む」ドキュメントを検索
```js:
db.find({ ○○: /●●/ }, function(err, docs){
    ....
});
```

#### 条件を AND 指定してドキュメントを検索
```js:
db.find({ ○○: ●●, □□: ■■ }, function(err, docs){
    ....
});
```

#### 条件を指定せず全てのドキュメントを検索
```js:
db.find({}, function(err, docs){
    ....
});
```

#### 指定した値と一致するドキュメントの一つを検索
```js:
db.findOne({ ○○: ●● }, function(err, doc){
    ....
});
```
```doc``` は一致するドキュメントがなければ ```null``` になります。

### 比較演算

#### 指定した値より小さい
```js:
db.find({ ○○: { $lt: ●● } }, function(err, docs){
    ....
```

#### 指定した値より小さいか等しい
```js:
db.find({ ○○: { $lte: ●● } }, function(err, docs){
    ....
```

#### 指定した値より大きい
```js:
db.find({ ○○: { $gt: ●● } }, function(err, docs){
    ....
```

#### 指定した値より大きいか等しい
```js:
db.find({ ○○: { $gte: ●● } }, function(err, docs){
    ....
```

#### 指定した値のいずれかと一致する
```js:
db.find({ ○○: { $in: [●●, ■■] } }, function(err, docs){
    ....
```

#### 指定した値と異なる
```js:
db.find({ ○○: { $ne: ●● } }, function(err, docs){
    ....
```

#### 指定した値のいずれでもない
```js:
db.find({ ○○: { $nin: [●●, ■■] } }, function(err, docs){
    ....
```

#### 指定したフィールドがある
```js:
db.find({ ○○: { $exists: true } }, function(err, docs){
    ....
```

#### 正規表現を他の条件指定と合わせて使う。例えば
```js:
db.find({ ○○: { $regex: /●●/, $nin: [●●, ■■] } }, function(err, docs){
    ....
```

### 条件を論理演算

#### 条件を OR 指定
```js:
db.find({ $or: [{ ○○: ●● }, { □□: ■■ }] }, function(err, docs){
    ....
```

#### 条件を NOT 指定
```js:
db.find({ $not: { ○○: ●● } }, function(err, docs){
    ....
```

#### 関数を使って条件指定
```js:
db.find({ $where: function(){ return this.○○ = ●●; } }, function(err, docs){
    ....
```

#### 指定を組合せる。例えば
```js:
db.find({ $or: [{ ○○: ●● }, { □□: ■■ }], ○○: ×× }, function(err, docs){
    ....
```

### 並替など

#### 並替
```js:
db.find({ .... }).sort({ ○○: 1 }).exec(function(err, docs){
    ....
```

#### 逆順に並替
```js:
db.find({ .... }).sort({ ○○: -1 }).exec(function(err, docs){
    ....
```

#### 複数フィールドを指定して並替
```js:
db.find({ .... }).sort({ ○○: 1, ◇◇: 1 }).exec(function(err, docs){
    ....
```

#### 先頭から指定した数だけ
```js:
db.find({ .... }).sort({ .... }).limit(●).exec(function(err, docs){
    ....
```

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

#### 指定したフィールドだけ
```js:
db.find({ .... }, { ○○: 1, □□: 1 }, function(err, docs){
    ....
```

#### 指定したフィールドを除いて
```js:
db.find({ .... }, { ○○: 0, □□: 0 }, function(err, docs){
    ....
```

#### MongoDB と非互換の書き方
```js:
db.find({ .... }).projection({ ○○: 1, □□: 1 }).exec(function(err, docs){
    ....
```

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

#### 指定した条件に一致するドキュメントをカウント
```js:
db.count({ .... }, function(err, count){
    ....
});
```

#### 条件を指定せず全てのドキュメントをカウント
```js:
db.count({}, function(err, count){
    ....
});
```

## ドキュメントを更新する

#### ドキュメントの内容を別のに置換
```js:
db.update({ .... }, { ○○: ●● }, { multi: .... }, function(err, numReplaced){
    ....
});
```
指定されたフィールドの値が置換えられます。
指定されなかったフィールドは残らないので注意が必要です。
```_id``` は変更されずに保持されます。

#### 既存のフィールドの値を置換
```js:
db.update({ .... }, { $set: { ○○: ●● } }, { multi: .... }, function(err, numReplaced){
    ....
});
```
指定されたフィールドの値が置換えられます。
指定されなかったフィールドと値は残ります。

#### フィールドを削除
```js:
db.update({ .... }, { $unset: { ○○: true } }, {}, function () {
    ....
});
```

#### ドキュメントあるいはフィールドがあれば更新、なければ挿入
```js:
db.update({ .... }, { ○○: ●● }, { upsert: true }, function(err, numReplaced, upsert){
    ....
});
```

## ドキュメントを削除する

#### 指定した条件のドキュメント一つを削除
```js:
db.remove({ .... }, {}, function(err, numRemoved){
    ....
});
```

#### 指定した条件のドキュメント全てを削除
```js:
db.remove({ ... }, { multi: true }, function(err, numRemoved){
    ....
});
```

#### 全てのドキュメントを削除
```js:
db.remove({}, { multi: true }, function(err, numRemoved){
    ....
});
```

# おわりに

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

## 追記

後継版を見つけました。これを使ってみました。

[NeDB を使ってみたの続き #JavaScript - Qiita](https://qiita.com/tinymouse/items/ebb171661bac4f47e27a)